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_SIMPLE_PROMPT,
13 projectionFor,
14} from "../shared.ts";
15
16/** One OpenCode 2 turn with streamed reasoning and text, ended by `execution.succeeded`. */
17export function assertOpenCode2SimpleOutput(
18 result: OrchestratorV2ScenarioResult,
19 transcript: ProviderReplayTranscript,
20) {
21 assertBaseProjection({ result, transcript, runCount: 1, runStatuses: ["completed"] });
22 const projection = projectionFor(result, transcript.scenario);
23 assertSemanticProjectionIntegrity(projection);
24 assertVisibleTurnItemsMirrorLocalTurnItems(projection);
25 assertTurnItemTypeSequence(projection, [
26 "user_message",
27 "reasoning",
28 "assistant_message",
29 "checkpoint",
30 ]);
31 assertUserMessagesInclude(projection, [OPENCODE2_SIMPLE_PROMPT]);
32 assertAssistantTextIncludes(projection, "391 is not prime: it's the product 17 × 23.");
33
34 const reasoning = projection.turnItems.find((item) => item.type === "reasoning");
35 assert.include(reasoning?.type === "reasoning" ? reasoning.text : "", "17×23 = 391");
36
37 // `step.ended` usage becomes the turn's usage; the model's input limit is the window.
38 const [providerTurn] = projection.providerTurns;
39 assert.equal(providerTurn?.status, "completed");
40 assert.deepInclude(providerTurn?.turnTokenUsage, {
41 usageStatus: "complete",
42 inputTokens: 8701 + 489,
43 cachedInputTokens: 489,
44 outputTokens: 113 + 147,
45 reasoningTokens: 147,
46 });
47 assert.deepInclude(providerTurn?.tokenUsage, {
48 usedTokens: 8701 + 489 + 113,
49 maxTokens: 524288,
50 });
51}