1import { assert } from "@effect/vitest";
2import type { ProviderReplayTranscript } from "@t3tools/contracts";
3
4import type { OrchestratorV2ScenarioResult } from "../../OrchestratorScenario.ts";
5import { assertSemanticProjectionIntegrity, projectionFor } from "../shared.ts";
6
7/**
8 * A native subagent's approval is asked on the parent thread, as Codex and
9 * OpenCode ask theirs: Muse children have no provider session of their own to
10 * answer from. The answer reaches Muse and the subagent finishes its work.
11 */
12export function assertMuseSubagentApprovalOutput(
13 result: OrchestratorV2ScenarioResult,
14 transcript: ProviderReplayTranscript,
15) {
16 const projection = projectionFor(result, transcript.scenario);
17 assertSemanticProjectionIntegrity(projection);
18 assert.deepEqual(
19 projection.runs.map((run) => run.status),
20 ["completed"],
21 );
22
23 const requests = projection.runtimeRequests;
24 assert.lengthOf(requests, 1);
25 assert.equal(requests[0]?.kind, "command");
26 assert.equal(requests[0]?.status, "resolved");
27 assert.equal(requests[0]?.decision, "accept");
28 const outbound = transcript.entries.flatMap((entry) =>
29 entry.type === "expect_outbound" ? [entry.frame] : [],
30 );
31 assert.isTrue(
32 outbound.some(
33 (frame) =>
34 typeof frame === "object" &&
35 frame !== null &&
36 Reflect.get(frame, "method") === "approval/decide",
37 ),
38 "the answer reaches Muse",
39 );
40
41 assert.lengthOf(projection.subagents, 1);
42 const subagent = projection.subagents[0]!;
43 assert.equal(subagent.status, "completed");
44 assert.match(subagent.result ?? "", /^Written\.?$/u);
45 if (subagent.childThreadId === null) throw new Error("The subagent has no child thread");
46 const child = result.projections.get(subagent.childThreadId);
47 if (child === undefined) throw new Error("The child thread has no projection");
48 assert.lengthOf(child.runtimeRequests, 0);
49 assert.deepEqual(
50 child.messages.map((message) => [message.role, message.text]),
51 [
52 ["user", subagent.prompt],
53 ["assistant", subagent.result],
54 ],
55 );
56}