1import { assert } from "@effect/vitest";
2import type { ProviderReplayTranscript } from "@t3tools/contracts";
3
4import type { OrchestratorV2ScenarioResult } from "../../OrchestratorScenario.ts";
5import {
6 assertBaseProjection,
7 assertSemanticProjectionIntegrity,
8 assertTurnItemTypes,
9 assertUserMessagesInclude,
10 assertVisibleTurnItemsMirrorLocalTurnItems,
11 OPENCODE2_INTERRUPT_PROMPT,
12 projectionFor,
13} from "../shared.ts";
14
15/**
16 * Stop during a running shell call: `session.interrupt`, then OpenCode fails the
17 * tool as aborted and ends the execution with `interrupted`.
18 */
19export function assertOpenCode2InterruptOutput(
20 result: OrchestratorV2ScenarioResult,
21 transcript: ProviderReplayTranscript,
22) {
23 assertBaseProjection({ result, transcript, runCount: 1, runStatuses: ["interrupted"] });
24 const projection = projectionFor(result, transcript.scenario);
25 assertSemanticProjectionIntegrity(projection);
26 assertVisibleTurnItemsMirrorLocalTurnItems(projection);
27 assertTurnItemTypes(projection, [
28 "user_message",
29 "assistant_message",
30 "command_execution",
31 "run_interrupt_request",
32 "run_interrupt_result",
33 ]);
34 assertUserMessagesInclude(projection, [OPENCODE2_INTERRUPT_PROMPT]);
35
36 const shell = projection.turnItems.find((item) => item.type === "command_execution");
37 assert.deepInclude(shell, { input: "sleep 60 && echo LATE", status: "interrupted" });
38 assert.equal(
39 projection.turnItems.find((item) => item.type === "run_interrupt_result")?.status,
40 "interrupted",
41 );
42 assert.deepEqual(
43 projection.providerTurns.map((turn) => turn.status),
44 ["interrupted"],
45 );
46 assert.equal(projection.providerThreads[0]?.status, "idle");
47}