1import { assert } from "@effect/vitest";
2import type { ProviderReplayTranscript } from "@t3tools/contracts";
3
4import type { OrchestratorV2ScenarioResult } from "../../OrchestratorScenario.ts";
5import {
6 assertAssistantTextIncludes,
7 assertBaseProjection,
8 assertSemanticProjectionIntegrity,
9 assertTurnItemTypeSequence,
10 assertUserMessagesInclude,
11 assertVisibleTurnItemsMirrorLocalTurnItems,
12 OPENCODE2_QUESTION_PROMPT,
13 projectionFor,
14} from "../shared.ts";
15
16/**
17 * The question tool's form is a user input request with the model's options and
18 * a free-form answer; the answer goes back to OpenCode and the turn uses it.
19 */
20export function assertOpenCode2QuestionOutput(
21 result: OrchestratorV2ScenarioResult,
22 transcript: ProviderReplayTranscript,
23) {
24 assertBaseProjection({ result, transcript, runCount: 1, runStatuses: ["completed"] });
25 const projection = projectionFor(result, transcript.scenario);
26 assertSemanticProjectionIntegrity(projection);
27 assertVisibleTurnItemsMirrorLocalTurnItems(projection);
28 assertUserMessagesInclude(projection, [OPENCODE2_QUESTION_PROMPT]);
29 // The form is the item; OpenCode's own `question` tool call is not repeated.
30 assertTurnItemTypeSequence(projection, [
31 "user_message",
32 "assistant_message",
33 "user_input_request",
34 "assistant_message",
35 "checkpoint",
36 ]);
37 assertAssistantTextIncludes(projection, "blue");
38
39 const [request] = projection.runtimeRequests;
40 assert.lengthOf(projection.runtimeRequests, 1);
41 assert.deepInclude(request, { kind: "user_input", status: "resolved" });
42 assert.deepEqual(request?.answers, { q0: "blue" });
43
44 const item = projection.turnItems.find((candidate) => candidate.type === "user_input_request");
45 assert.equal(item?.status, "completed");
46 assert.deepEqual(item?.type === "user_input_request" ? item.questions : [], [
47 {
48 id: "q0",
49 header: "Color preference",
50 question: "Which color do you prefer?",
51 options: [
52 { label: "Red", description: "Choose the color red", value: "Red" },
53 { label: "Blue", description: "Choose the color blue", value: "Blue" },
54 ],
55 multiSelect: false,
56 allowCustomAnswer: true,
57 },
58 ]);
59}