1import { assert } from "@effect/vitest";
2import type { ProviderReplayTranscript } from "@t3tools/contracts";
3
4import type { OrchestratorV2ScenarioResult } from "../../OrchestratorScenario.ts";
5import {
6 assertBaseProjection,
7 assertSemanticProjectionIntegrity,
8 assertUserMessagesInclude,
9 assertVisibleTurnItemsMirrorLocalTurnItems,
10 backgroundNotifications,
11 projectionFor,
12} from "../shared.ts";
13import { GROK_MONITOR_PROMPT, GROK_MONITOR_TICKS } from "./input.ts";
14
15// Grok streams each monitor tick as an in_progress Bash result that already
16// carries exit_code 0 and ends the monitor only through `_x.ai/task_completed`.
17// Run 1 is held open until that end; Grok's own reply to the finished monitor
18// then arrives as a continuation run, like Claude and Codex background wakes.
19export function assertGrokMonitorOutput(
20 result: OrchestratorV2ScenarioResult,
21 transcript: ProviderReplayTranscript,
22) {
23 assertBaseProjection({
24 result,
25 transcript,
26 runCount: 2,
27 runStatuses: ["completed", "completed"],
28 });
29 const projection = projectionFor(result, transcript.scenario);
30 assertSemanticProjectionIntegrity(projection);
31 assertVisibleTurnItemsMirrorLocalTurnItems(projection);
32 assertUserMessagesInclude(projection, [GROK_MONITOR_PROMPT]);
33
34 const [rootRun, wakeRun] = projection.runs;
35 const monitors = projection.turnItems.filter((item) => item.type === "command_execution");
36 assert.lengthOf(monitors, 1);
37 const monitor = monitors[0];
38 assert.equal(monitor?.runId, rootRun?.id, "the monitor belongs to the run that started it");
39 assert.equal(monitor?.status, "completed");
40 assert.equal(monitor?.output, GROK_MONITOR_TICKS.map((tick) => `${tick}\n`).join(""));
41
42 // Every tick before the end is shown running, with the output so far.
43 const monitorUpdates = result.domainEvents.flatMap((event) =>
44 event.type === "turn-item.updated" &&
45 event.payload.id === monitor?.id &&
46 event.payload.type === "command_execution"
47 ? [event.payload]
48 : [],
49 );
50 const firstCompleted = monitorUpdates.findIndex((update) => update.status === "completed");
51 for (const tick of GROK_MONITOR_TICKS.slice(0, -1)) {
52 const tickUpdate = monitorUpdates.findIndex((update) => update.output?.includes(tick));
53 assert.isAtLeast(tickUpdate, 0, `missing monitor update for ${tick}`);
54 assert.isBelow(tickUpdate, firstCompleted, `the monitor completed before ${tick}`);
55 assert.equal(monitorUpdates[tickUpdate]?.status, "running", `${tick} must show running`);
56 }
57
58 // Run 1 settles only after the monitor's structured end.
59 const monitorCompletedAt = result.domainEvents.findIndex(
60 (event) =>
61 event.type === "turn-item.updated" &&
62 event.payload.id === monitor?.id &&
63 event.payload.status === "completed",
64 );
65 const rootCompletedAt = result.domainEvents.findIndex(
66 (event) =>
67 event.type === "run.updated" &&
68 event.payload.id === rootRun?.id &&
69 event.payload.status === "completed",
70 );
71 assert.isAtLeast(monitorCompletedAt, 0);
72 assert.isAbove(rootCompletedAt, monitorCompletedAt, "run 1 completed before the monitor ended");
73
74 const rootTexts = projection.turnItems.flatMap((item) =>
75 item.runId === rootRun?.id && item.type === "assistant_message" ? [item.text.trim()] : [],
76 );
77 assert.include(rootTexts, "ROOT_DONE");
78
79 // Full access approves the monitor prompt for T3. It must answer with
80 // Grok's allow-once: Grok saves `always-allow` for the whole project.
81 const permissionAnswer = transcript.entries.find(
82 (entry) =>
83 entry.type === "expect_outbound" &&
84 (entry.frame as { kind?: unknown; method?: unknown }).kind === "response" &&
85 (entry.frame as { method?: unknown }).method === "session/request_permission",
86 );
87 assert.deepEqual(
88 permissionAnswer?.type === "expect_outbound"
89 ? (permissionAnswer.frame as { result?: unknown }).result
90 : undefined,
91 { outcome: { outcome: "selected", optionId: "allow-once" } },
92 );
93
94 // The timeline names the monitor from Grok's task_completed snapshot.
95 assert.deepEqual(backgroundNotifications(projection), [
96 {
97 summary: 'Monitor "Watch three tick echoes" finished',
98 outcome: "completed",
99 source: { kind: "monitor" },
100 },
101 ]);
102
103 // Grok's own wake reply is a provider continuation, not more of run 1.
104 const wakeMessage = projection.messages.find((message) => message.id === wakeRun?.userMessageId);
105 assert.equal(`${wakeMessage?.createdBy}:${wakeMessage?.creationSource}`, "agent:provider");
106 assert.isNotEmpty(
107 projection.turnItems.filter(
108 (item) => item.runId === wakeRun?.id && item.type === "assistant_message",
109 ),
110 "Grok's reply to the finished monitor must land in the continuation run",
111 );
112}