diff --git a/apps/server/src/mcp/McpHttpServer.test.ts b/apps/server/src/mcp/McpHttpServer.test.ts index 27664e0d7204c..1619fbc06c7ee 100644 --- a/apps/server/src/mcp/McpHttpServer.test.ts +++ b/apps/server/src/mcp/McpHttpServer.test.ts @@ -1618,13 +1618,17 @@ it.effect( "pull_request_read", "pull_request_review", "pull_request_merge", + "ci_logs", + "ci_rerun", ]), ); const noForgeTools = yield* listedTools("noForgeToken"); expect(noForgeTools).toEqual( expect.arrayContaining(["link_pull_request", "watch_pull_request"]), ); - expect(noForgeTools.filter((name) => name.startsWith("pull_request_"))).toEqual([]); + expect( + noForgeTools.filter((name) => name.startsWith("pull_request_") || name.startsWith("ci_")), + ).toEqual([]); }), ).pipe(Effect.provide(NodeHttpServer.layerTest)), ); diff --git a/apps/server/src/mcp/toolkits/pullRequests/handlers.test.ts b/apps/server/src/mcp/toolkits/pullRequests/handlers.test.ts index 80005e419e3c1..1b486502c3d60 100644 --- a/apps/server/src/mcp/toolkits/pullRequests/handlers.test.ts +++ b/apps/server/src/mcp/toolkits/pullRequests/handlers.test.ts @@ -936,6 +936,46 @@ describe("source control tools", () => { }), ); + it.effect("re-running checks reopens a watch that saw its checks finish", () => + Effect.gen(function* () { + const watch = { + startedAt: "2026-08-20T00:00:00.000Z", + headSha: "abc123", + failedChecks: ["test"], + passed: false, + passedChecks: [], + remarksThrough: "2026-08-20T00:00:00.000Z", + remarkIds: [], + conflicting: false, + wakes: 0, + }; + const rerun = (checksPending: boolean | undefined, reran: boolean) => + Effect.gen(function* () { + const harness = yield* makeHarness({ + thread: { + ...branchThread, + pullRequests: [ + makeLink(42, { + watch: checksPending === undefined ? watch : { ...watch, checksPending }, + }), + ], + }, + pullRequests: { + rerunChecks: () => Effect.succeed([{ name: "test", rerun: reran, detail: null }]), + }, + }); + yield* harness.call("ci_rerun", {}); + return yield* Ref.get(harness.commands); + }); + expect(yield* rerun(false, true)).toMatchObject([ + { type: "thread.pull-request-watch.checks-reopen", threadId: THREAD_ID }, + ]); + // Nothing re-ran, or the watch has no answer to clear. + expect(yield* rerun(false, false)).toEqual([]); + expect(yield* rerun(undefined, true)).toEqual([]); + }), + ); + it.effect("opens a PR from the thread's worktree and links it even if dismissed", () => Effect.gen(function* () { const opened: Array<{ readonly cwd: string; readonly draft: boolean | undefined }> = []; diff --git a/apps/server/src/mcp/toolkits/pullRequests/handlers.ts b/apps/server/src/mcp/toolkits/pullRequests/handlers.ts index 830ac6d4ddcb0..ce76811b4d6d6 100644 --- a/apps/server/src/mcp/toolkits/pullRequests/handlers.ts +++ b/apps/server/src/mcp/toolkits/pullRequests/handlers.ts @@ -1105,6 +1105,46 @@ const make = Effect.gen(function* () { return { ...identityOf(target), state: after.state }; }), ), + ci_logs: McpToolAccess.reads((input) => + Effect.gen(function* () { + const { target, ref } = yield* locate(input); + const checks = yield* pullRequests + .checkLogs({ + ...ref, + tailLines: input.tailLines ?? 200, + ...(input.check === undefined ? {} : { check: input.check }), + }) + .pipe(Effect.mapError(forgeFailure)); + return { ...identityOf(target), checks }; + }), + ), + ci_rerun: McpToolAccess.writesWithCapability((input) => + Effect.gen(function* () { + const { thread, target, ref } = yield* locate(input); + const checks = yield* pullRequests + .rerunChecks({ ...ref, ...(input.check === undefined ? {} : { check: input.check }) }) + .pipe(Effect.mapError(forgeFailure)); + // Like a push, a rerun makes a watch's finished checks stale, so the thread keeps working + // until a read sees them run. The rerun already happened, so a failed reopen only logs. + if ( + checks.some((check) => check.rerun) && + threadPullRequestsOf(thread).some((link) => link.watch?.checksPending !== undefined) + ) { + yield* engine + .dispatch({ + type: "thread.pull-request-watch.checks-reopen", + commandId: yield* commandId("mcp-ci-rerun-checks", thread.id), + threadId: thread.id, + }) + .pipe( + Effect.catchCause((cause) => + Effect.logWarning("reopening a watch's checks after a rerun failed", cause), + ), + ); + } + return { ...identityOf(target), checks }; + }), + ), } satisfies McpToolAccess.Handlers; }); diff --git a/apps/server/src/mcp/toolkits/pullRequests/tools.ts b/apps/server/src/mcp/toolkits/pullRequests/tools.ts index 73350d9d7b8d8..cd74358c8daa2 100644 --- a/apps/server/src/mcp/toolkits/pullRequests/tools.ts +++ b/apps/server/src/mcp/toolkits/pullRequests/tools.ts @@ -764,6 +764,72 @@ const MergePullRequestTool = Tool.make("pull_request_merge", { .annotate(Tool.OpenWorld, true) .annotate(McpSchema.EnabledWhen, McpInvocationContext.hiddenWithoutForge); +export const CheckLogsResult = Schema.Struct({ + ...PullRequestIdentity, + checks: Schema.Array( + Schema.Struct({ + name: Schema.String, + url: Schema.NullOr(Schema.String), + log: Schema.NullOr(Schema.String), + truncated: Schema.Boolean, + detail: Schema.NullOr(Schema.String), + }), + ), +}); +export type CheckLogsResult = typeof CheckLogsResult.Type; + +export const RerunChecksResult = Schema.Struct({ + ...PullRequestIdentity, + checks: Schema.Array( + Schema.Struct({ + name: Schema.String, + rerun: Schema.Boolean, + detail: Schema.NullOr(Schema.String), + }), + ), +}); +export type RerunChecksResult = typeof RerunChecksResult.Type; + +const CheckLogsTool = Tool.make("ci_logs", { + description: + "Read the tail of a PR's failing check logs, or one named check's. Use instead of gh run view.", + parameters: Schema.Struct({ + ...ThreadPullRequestInput.fields, + check: Schema.optional(TrimmedNonEmptyString), + tailLines: Schema.optional( + Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 2000 })).annotate({ + description: "Default: 200.", + }), + ), + }), + success: CheckLogsResult, + failure: PullRequestToolError, + dependencies: forgeDependencies, +}) + .annotate(Tool.Title, "Read CI logs") + .annotate(Tool.Readonly, true) + .annotate(Tool.Destructive, false) + .annotate(Tool.Idempotent, true) + .annotate(Tool.OpenWorld, true) + .annotate(McpSchema.EnabledWhen, McpInvocationContext.hiddenWithoutForge); + +const RerunChecksTool = Tool.make("ci_rerun", { + description: "Re-run a PR's failing checks, or one named check.", + parameters: Schema.Struct({ + ...ThreadPullRequestInput.fields, + check: Schema.optional(TrimmedNonEmptyString), + }), + success: RerunChecksResult, + failure: PullRequestToolError, + dependencies: forgeDependencies, +}) + .annotate(Tool.Title, "Re-run CI") + .annotate(Tool.Readonly, false) + .annotate(Tool.Destructive, false) + .annotate(Tool.Idempotent, false) + .annotate(Tool.OpenWorld, true) + .annotate(McpSchema.EnabledWhen, McpInvocationContext.hiddenWithoutForge); + export const PullRequestsToolkit = Toolkit.make( LinkPullRequestTool, LinkPullRequestsTool, @@ -781,4 +847,6 @@ export const PullRequestsToolkit = Toolkit.make( RespondToPullRequestTool, ReviewPullRequestTool, MergePullRequestTool, + CheckLogsTool, + RerunChecksTool, ); diff --git a/apps/server/src/orchestration-v2/testkit/fixtures/multi_turn/muse_transcript.ndjson b/apps/server/src/orchestration-v2/testkit/fixtures/multi_turn/muse_transcript.ndjson index 28b6b85c382f5..00f862f1b3172 100644 --- a/apps/server/src/orchestration-v2/testkit/fixtures/multi_turn/muse_transcript.ndjson +++ b/apps/server/src/orchestration-v2/testkit/fixtures/multi_turn/muse_transcript.ndjson @@ -7,13 +7,13 @@ {"type":"emit_inbound","label":"session/started","frame":{"jsonrpc":"2.0","method":"session/started","params":{"session":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","path":"~/.local/share/muse/sessions/2026/10/08/01a11a6b-707e-7000-9bb5-4079c7772ad0/session.jsonl","status":"idle","activeTurnId":null,"createdAt":"2026-10-08T07:30:11.238412Z","updatedAt":"2026-10-08T07:30:11.238416Z","workspaceRoot":"","providerId":"meta","modelId":"muse-spark-1.3-contributor","turnCount":0,"forkedFrom":null,"approvalMode":{"mode":"allowAll","source":"startup","lastCommandId":null},"createdByClient":{"name":"t3_code","version":"1"},"kind":"root"}},"emittedAtMs":1791444611408}} {"type":"emit_inbound","label":"response:session/start","frame":{"jsonrpc":"2.0","id":2,"result":{"session":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","path":"~/.local/share/muse/sessions/2026/10/08/01a11a6b-707e-7000-9bb5-4079c7772ad0/session.jsonl","status":"idle","activeTurnId":null,"createdAt":"2026-10-08T07:30:11.238412Z","updatedAt":"2026-10-08T07:30:11.238416Z","workspaceRoot":"","providerId":"meta","modelId":"muse-spark-1.3-contributor","turnCount":0,"forkedFrom":null,"approvalMode":{"mode":"allowAll","source":"startup","lastCommandId":null},"createdByClient":{"name":"t3_code","version":"1"},"kind":"root"},"viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:1"}}} {"type":"emit_inbound","label":"session/approvalModeChanged","frame":{"jsonrpc":"2.0","method":"session/approvalModeChanged","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:1","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"37b19bd1-b212-4532-a0c9-a6677f303e26","sequence":5},"last":{"id":"37b19bd1-b212-4532-a0c9-a6677f303e26","sequence":5}},"mode":"allowAll","source":"approvalReconfigure","commandId":"01a11a6b-707e-7001-b2f4-23db74e60097","clientName":"t3_code"},"emittedAtMs":1791444611412}} -{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\n"},{"type":"text","text":"Respond with exactly: first fixture turn complete"}],"displayText":"Respond with exactly: first fixture turn complete","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4"}}} +{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\n"},{"type":"text","text":"Respond with exactly: first fixture turn complete"}],"displayText":"Respond with exactly: first fixture turn complete","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4"}}} {"type":"emit_inbound","label":"session/branchChanged","frame":{"jsonrpc":"2.0","method":"session/branchChanged","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:2","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"2c530748-943b-4467-8473-da8b04c511c3","sequence":16},"last":{"id":"2c530748-943b-4467-8473-da8b04c511c3","sequence":16}},"workspaceRoot":"","branch":"main","vcs":"git"},"emittedAtMs":1791444611661}} {"type":"emit_inbound","label":"response:turn/start","frame":{"jsonrpc":"2.0","id":3,"result":{"commandId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4","status":"accepted","turnId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4","startedNewTurn":true,"disposition":"started"}}} {"type":"emit_inbound","label":"session/statusChanged","frame":{"jsonrpc":"2.0","method":"session/statusChanged","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","status":"running","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:3"},"emittedAtMs":1791444611674}} {"type":"emit_inbound","label":"turn/started","frame":{"jsonrpc":"2.0","method":"turn/started","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:3","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"2be0c9b5-9dc4-48f0-9b86-06aef4de31f3","sequence":18},"last":{"id":"2be0c9b5-9dc4-48f0-9b86-06aef4de31f3","sequence":18}},"turnId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4","commandId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4"},"emittedAtMs":1791444611675}} -{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:4","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"2be0c9b5-9dc4-48f0-9b86-06aef4de31f3","sequence":18},"last":{"id":"2be0c9b5-9dc4-48f0-9b86-06aef4de31f3","sequence":18}},"item":{"itemId":"1b470279-ee8c-4c74-a298-f37184145584","kind":"userMessage","turnId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4","revision":1,"status":"completed","recordedAt":"2026-10-08T07:30:11.66263Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nRespond with exactly: first fixture turn complete","commandId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4"}},"emittedAtMs":1791444611675}} -{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:5","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"31b500d6-40fc-4f4c-b097-6b7be8d77cee","sequence":20},"last":{"id":"31b500d6-40fc-4f4c-b097-6b7be8d77cee","sequence":20}},"item":{"itemId":"1b470279-ee8c-4c74-a298-f37184145584","kind":"userMessage","turnId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4","revision":2,"status":"completed","recordedAt":"2026-10-08T07:30:11.680942Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nRespond with exactly: first fixture turn complete","displayText":"Respond with exactly: first fixture turn complete","commandId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4"}},"emittedAtMs":1791444611689}} +{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:4","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"2be0c9b5-9dc4-48f0-9b86-06aef4de31f3","sequence":18},"last":{"id":"2be0c9b5-9dc4-48f0-9b86-06aef4de31f3","sequence":18}},"item":{"itemId":"1b470279-ee8c-4c74-a298-f37184145584","kind":"userMessage","turnId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4","revision":1,"status":"completed","recordedAt":"2026-10-08T07:30:11.66263Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nRespond with exactly: first fixture turn complete","commandId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4"}},"emittedAtMs":1791444611675}} +{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:5","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"31b500d6-40fc-4f4c-b097-6b7be8d77cee","sequence":20},"last":{"id":"31b500d6-40fc-4f4c-b097-6b7be8d77cee","sequence":20}},"item":{"itemId":"1b470279-ee8c-4c74-a298-f37184145584","kind":"userMessage","turnId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4","revision":2,"status":"completed","recordedAt":"2026-10-08T07:30:11.680942Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nRespond with exactly: first fixture turn complete","displayText":"Respond with exactly: first fixture turn complete","commandId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4"}},"emittedAtMs":1791444611689}} {"type":"emit_inbound","label":"item/started","frame":{"jsonrpc":"2.0","method":"item/started","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:6","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"077780ea-0dc9-4d7a-879c-ee368f3adbe0","sequence":37},"last":{"id":"077780ea-0dc9-4d7a-879c-ee368f3adbe0","sequence":37}},"item":{"itemId":"077780ea-0dc9-4d7a-879c-ee368f3adbe0","kind":"reminderChild","turnId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4","revision":1,"status":"inProgress","recordedAt":"2026-10-08T07:30:11.790301Z","fallbackText":"Reminder child session","childSessionId":"c13379db-6761-40b4-a974-b0f1a58717aa","reminderAgentId":"skill-reminder","generationId":1,"taskId":"01a11a6b-72ac-7972-a1df-b343b7804b38"}},"emittedAtMs":1791444611795}} {"type":"emit_inbound","label":"item/started","frame":{"jsonrpc":"2.0","method":"item/started","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:6.1","item":{"itemId":"f99b90e1-9d8b-4bf2-8e7a-e240e1cf9321","kind":"agentMessage","turnId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4","revision":1,"status":"inProgress","text":""}},"emittedAtMs":1791444618068}} {"type":"emit_inbound","label":"item/delta","frame":{"jsonrpc":"2.0","method":"item/delta","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:6.2","itemId":"f99b90e1-9d8b-4bf2-8e7a-e240e1cf9321","field":"text","delta":"first fixture turn complete"},"emittedAtMs":1791444618068}} @@ -29,12 +29,12 @@ {"type":"emit_inbound","label":"session/statusChanged","frame":{"jsonrpc":"2.0","method":"session/statusChanged","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","status":"idle","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:16"},"emittedAtMs":1791444621046}} {"type":"emit_inbound","label":"turn/completed","frame":{"jsonrpc":"2.0","method":"turn/completed","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:16","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"688a69c5-e353-4db9-bd8c-1beb033902ba","sequence":79},"last":{"id":"688a69c5-e353-4db9-bd8c-1beb033902ba","sequence":79}},"turnId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4","terminal":"completed","durationMs":9378,"timeToFirstTokenMs":6406},"emittedAtMs":1791444621047}} {"type":"emit_inbound","label":"session/branchChanged","frame":{"jsonrpc":"2.0","method":"session/branchChanged","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:17","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"36753b8a-26ce-4935-8127-bc9191cb328b","sequence":80},"last":{"id":"36753b8a-26ce-4935-8127-bc9191cb328b","sequence":80}},"workspaceRoot":"","branch":"main","vcs":"git"},"emittedAtMs":1791444621054}} -{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":4,"method":"turn/start","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\n"},{"type":"text","text":"Respond with exactly: second fixture turn complete"}],"displayText":"Respond with exactly: second fixture turn complete","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a11a6b-97ec-7000-bd1b-d7afbea11045"}}} +{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":4,"method":"turn/start","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\n"},{"type":"text","text":"Respond with exactly: second fixture turn complete"}],"displayText":"Respond with exactly: second fixture turn complete","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a11a6b-97ec-7000-bd1b-d7afbea11045"}}} {"type":"emit_inbound","label":"response:turn/start","frame":{"jsonrpc":"2.0","id":4,"result":{"commandId":"01a11a6b-97ec-7000-bd1b-d7afbea11045","status":"accepted","turnId":"01a11a6b-97ec-7000-bd1b-d7afbea11045","startedNewTurn":true,"disposition":"started"}}} {"type":"emit_inbound","label":"session/statusChanged","frame":{"jsonrpc":"2.0","method":"session/statusChanged","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","status":"running","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:18"},"emittedAtMs":1791444621333}} {"type":"emit_inbound","label":"turn/started","frame":{"jsonrpc":"2.0","method":"turn/started","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:18","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"4aba96d5-f984-4bc0-bda4-7fb97f527686","sequence":87},"last":{"id":"4aba96d5-f984-4bc0-bda4-7fb97f527686","sequence":87}},"turnId":"01a11a6b-97ec-7000-bd1b-d7afbea11045","commandId":"01a11a6b-97ec-7000-bd1b-d7afbea11045"},"emittedAtMs":1791444621334}} -{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:19","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"4aba96d5-f984-4bc0-bda4-7fb97f527686","sequence":87},"last":{"id":"4aba96d5-f984-4bc0-bda4-7fb97f527686","sequence":87}},"item":{"itemId":"90719a5d-d2c1-46dc-ad4d-274adda02344","kind":"userMessage","turnId":"01a11a6b-97ec-7000-bd1b-d7afbea11045","revision":1,"status":"completed","recordedAt":"2026-10-08T07:30:21.321391Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nRespond with exactly: second fixture turn complete","commandId":"01a11a6b-97ec-7000-bd1b-d7afbea11045"}},"emittedAtMs":1791444621334}} -{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:20","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"dc502e2c-d81c-4f0e-ad86-dd11d1368677","sequence":89},"last":{"id":"dc502e2c-d81c-4f0e-ad86-dd11d1368677","sequence":89}},"item":{"itemId":"90719a5d-d2c1-46dc-ad4d-274adda02344","kind":"userMessage","turnId":"01a11a6b-97ec-7000-bd1b-d7afbea11045","revision":2,"status":"completed","recordedAt":"2026-10-08T07:30:21.338944Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nRespond with exactly: second fixture turn complete","displayText":"Respond with exactly: second fixture turn complete","commandId":"01a11a6b-97ec-7000-bd1b-d7afbea11045"}},"emittedAtMs":1791444621347}} +{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:19","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"4aba96d5-f984-4bc0-bda4-7fb97f527686","sequence":87},"last":{"id":"4aba96d5-f984-4bc0-bda4-7fb97f527686","sequence":87}},"item":{"itemId":"90719a5d-d2c1-46dc-ad4d-274adda02344","kind":"userMessage","turnId":"01a11a6b-97ec-7000-bd1b-d7afbea11045","revision":1,"status":"completed","recordedAt":"2026-10-08T07:30:21.321391Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nRespond with exactly: second fixture turn complete","commandId":"01a11a6b-97ec-7000-bd1b-d7afbea11045"}},"emittedAtMs":1791444621334}} +{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:20","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"dc502e2c-d81c-4f0e-ad86-dd11d1368677","sequence":89},"last":{"id":"dc502e2c-d81c-4f0e-ad86-dd11d1368677","sequence":89}},"item":{"itemId":"90719a5d-d2c1-46dc-ad4d-274adda02344","kind":"userMessage","turnId":"01a11a6b-97ec-7000-bd1b-d7afbea11045","revision":2,"status":"completed","recordedAt":"2026-10-08T07:30:21.338944Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nRespond with exactly: second fixture turn complete","displayText":"Respond with exactly: second fixture turn complete","commandId":"01a11a6b-97ec-7000-bd1b-d7afbea11045"}},"emittedAtMs":1791444621347}} {"type":"emit_inbound","label":"item/started","frame":{"jsonrpc":"2.0","method":"item/started","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:21","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"09cb4c2a-03c2-4089-ba12-8d5d31f7ea48","sequence":106},"last":{"id":"09cb4c2a-03c2-4089-ba12-8d5d31f7ea48","sequence":106}},"item":{"itemId":"09cb4c2a-03c2-4089-ba12-8d5d31f7ea48","kind":"reminderChild","turnId":"01a11a6b-97ec-7000-bd1b-d7afbea11045","revision":1,"status":"inProgress","recordedAt":"2026-10-08T07:30:21.446966Z","fallbackText":"Reminder child session","childSessionId":"cd97261a-3ea9-480f-9e54-5e59bb077da2","reminderAgentId":"skill-reminder","generationId":1,"taskId":"01a11a6b-9869-7061-a4f3-42daa3e3a8f1"}},"emittedAtMs":1791444621455}} {"type":"emit_inbound","label":"item/started","frame":{"jsonrpc":"2.0","method":"item/started","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:21.1","item":{"itemId":"2de26c0d-26b4-4533-8810-918ee44fcd82","kind":"agentMessage","turnId":"01a11a6b-97ec-7000-bd1b-d7afbea11045","revision":1,"status":"inProgress","text":""}},"emittedAtMs":1791444623127}} {"type":"emit_inbound","label":"item/delta","frame":{"jsonrpc":"2.0","method":"item/delta","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:21.2","itemId":"2de26c0d-26b4-4533-8810-918ee44fcd82","field":"text","delta":"second fixture turn complete"},"emittedAtMs":1791444623127}} diff --git a/apps/server/src/orchestration-v2/testkit/fixtures/muse_permission/muse_transcript.ndjson b/apps/server/src/orchestration-v2/testkit/fixtures/muse_permission/muse_transcript.ndjson index 94a525ca25a6e..687d274edb3e2 100644 --- a/apps/server/src/orchestration-v2/testkit/fixtures/muse_permission/muse_transcript.ndjson +++ b/apps/server/src/orchestration-v2/testkit/fixtures/muse_permission/muse_transcript.ndjson @@ -7,13 +7,13 @@ {"type":"emit_inbound","label":"session/started","frame":{"jsonrpc":"2.0","method":"session/started","params":{"session":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","path":"~/.local/share/muse/sessions/2026/10/08/01a11a6d-39bb-7000-8c93-4594c98e6a11/session.jsonl","status":"idle","activeTurnId":null,"createdAt":"2026-10-08T07:32:08.30084Z","updatedAt":"2026-10-08T07:32:08.300844Z","workspaceRoot":"","providerId":"meta","modelId":"muse-spark-1.3-contributor","turnCount":0,"forkedFrom":null,"approvalMode":{"mode":"promptUnmatched","source":"startup","lastCommandId":null},"createdByClient":{"name":"t3_code","version":"1"},"kind":"root"}},"emittedAtMs":1791444728496}} {"type":"emit_inbound","label":"response:session/start","frame":{"jsonrpc":"2.0","id":2,"result":{"session":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","path":"~/.local/share/muse/sessions/2026/10/08/01a11a6d-39bb-7000-8c93-4594c98e6a11/session.jsonl","status":"idle","activeTurnId":null,"createdAt":"2026-10-08T07:32:08.30084Z","updatedAt":"2026-10-08T07:32:08.300844Z","workspaceRoot":"","providerId":"meta","modelId":"muse-spark-1.3-contributor","turnCount":0,"forkedFrom":null,"approvalMode":{"mode":"promptUnmatched","source":"startup","lastCommandId":null},"createdByClient":{"name":"t3_code","version":"1"},"kind":"root"},"viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:1"}}} {"type":"emit_inbound","label":"session/approvalModeChanged","frame":{"jsonrpc":"2.0","method":"session/approvalModeChanged","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:1","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"b05fb8ec-7b58-4cf7-a369-379938395230","sequence":5},"last":{"id":"b05fb8ec-7b58-4cf7-a369-379938395230","sequence":5}},"mode":"promptUnmatched","source":"approvalReconfigure","commandId":"01a11a6d-39bb-7001-923e-425cc6129ce7","clientName":"t3_code"},"emittedAtMs":1791444728501}} -{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\n"},{"type":"text","text":"Run the shell command `touch muse-approved.txt` with your shell tool, then reply DONE."}],"displayText":"Run the shell command `touch muse-approved.txt` with your shell tool, then reply DONE.","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6"}}} +{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\n"},{"type":"text","text":"Run the shell command `touch muse-approved.txt` with your shell tool, then reply DONE."}],"displayText":"Run the shell command `touch muse-approved.txt` with your shell tool, then reply DONE.","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6"}}} {"type":"emit_inbound","label":"session/branchChanged","frame":{"jsonrpc":"2.0","method":"session/branchChanged","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:2","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"6159e84a-ae1e-4fae-ae8b-49f41ca34509","sequence":16},"last":{"id":"6159e84a-ae1e-4fae-ae8b-49f41ca34509","sequence":16}},"workspaceRoot":"","branch":"main","vcs":"git"},"emittedAtMs":1791444728769}} {"type":"emit_inbound","label":"response:turn/start","frame":{"jsonrpc":"2.0","id":3,"result":{"commandId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6","status":"accepted","turnId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6","startedNewTurn":true,"disposition":"started"}}} {"type":"emit_inbound","label":"session/statusChanged","frame":{"jsonrpc":"2.0","method":"session/statusChanged","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","status":"running","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:3"},"emittedAtMs":1791444728783}} {"type":"emit_inbound","label":"turn/started","frame":{"jsonrpc":"2.0","method":"turn/started","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:3","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"09218c3a-88fd-4bc8-ab45-d1aa69bea95a","sequence":18},"last":{"id":"09218c3a-88fd-4bc8-ab45-d1aa69bea95a","sequence":18}},"turnId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6","commandId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6"},"emittedAtMs":1791444728783}} -{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:4","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"09218c3a-88fd-4bc8-ab45-d1aa69bea95a","sequence":18},"last":{"id":"09218c3a-88fd-4bc8-ab45-d1aa69bea95a","sequence":18}},"item":{"itemId":"dde9573b-650f-434b-ab99-60cfc820a0a5","kind":"userMessage","turnId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6","revision":1,"status":"completed","recordedAt":"2026-10-08T07:32:08.769894Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nRun the shell command `touch muse-approved.txt` with your shell tool, then reply DONE.","commandId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6"}},"emittedAtMs":1791444728784}} -{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:5","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"76c57706-1378-4167-8bbf-f76d959804e8","sequence":20},"last":{"id":"76c57706-1378-4167-8bbf-f76d959804e8","sequence":20}},"item":{"itemId":"dde9573b-650f-434b-ab99-60cfc820a0a5","kind":"userMessage","turnId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6","revision":2,"status":"completed","recordedAt":"2026-10-08T07:32:08.791728Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nRun the shell command `touch muse-approved.txt` with your shell tool, then reply DONE.","displayText":"Run the shell command `touch muse-approved.txt` with your shell tool, then reply DONE.","commandId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6"}},"emittedAtMs":1791444728800}} +{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:4","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"09218c3a-88fd-4bc8-ab45-d1aa69bea95a","sequence":18},"last":{"id":"09218c3a-88fd-4bc8-ab45-d1aa69bea95a","sequence":18}},"item":{"itemId":"dde9573b-650f-434b-ab99-60cfc820a0a5","kind":"userMessage","turnId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6","revision":1,"status":"completed","recordedAt":"2026-10-08T07:32:08.769894Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nRun the shell command `touch muse-approved.txt` with your shell tool, then reply DONE.","commandId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6"}},"emittedAtMs":1791444728784}} +{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:5","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"76c57706-1378-4167-8bbf-f76d959804e8","sequence":20},"last":{"id":"76c57706-1378-4167-8bbf-f76d959804e8","sequence":20}},"item":{"itemId":"dde9573b-650f-434b-ab99-60cfc820a0a5","kind":"userMessage","turnId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6","revision":2,"status":"completed","recordedAt":"2026-10-08T07:32:08.791728Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nRun the shell command `touch muse-approved.txt` with your shell tool, then reply DONE.","displayText":"Run the shell command `touch muse-approved.txt` with your shell tool, then reply DONE.","commandId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6"}},"emittedAtMs":1791444728800}} {"type":"emit_inbound","label":"item/started","frame":{"jsonrpc":"2.0","method":"item/started","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:6","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"29180105-84c4-4f9e-8bfd-003392b93b73","sequence":38},"last":{"id":"29180105-84c4-4f9e-8bfd-003392b93b73","sequence":38}},"item":{"itemId":"29180105-84c4-4f9e-8bfd-003392b93b73","kind":"reminderChild","turnId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6","revision":1,"status":"inProgress","recordedAt":"2026-10-08T07:32:08.905989Z","fallbackText":"Reminder child session","childSessionId":"7c9485f9-26aa-4b56-8ca2-f36cb9cfb8c4","reminderAgentId":"skill-reminder","generationId":1,"taskId":"01a11a6d-3c27-73f2-8c27-cd06a3b7dadf"}},"emittedAtMs":1791444728910}} {"type":"emit_inbound","label":"session/tokenUsage","frame":{"jsonrpc":"2.0","method":"session/tokenUsage","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:7","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"b1dab1f5-3c9e-4d92-b955-841a2269c23c","sequence":54},"last":{"id":"b1dab1f5-3c9e-4d92-b955-841a2269c23c","sequence":54}},"turnId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6","modelId":"muse-spark-1.3-contributor","usage":{"inputTokens":32618,"outputTokens":315,"cachedTokens":0,"cacheWriteTokens":0,"cacheReadTokens":0,"reasoningTokens":227},"promptTokens":32618,"totalTokens":32933,"finishReason":"tool_calls","durationMs":2684,"cumulative":{"promptTokens":32618,"outputTokens":315,"totalTokens":32933,"cost":{"usd":0.0033248,"partial":false}}},"emittedAtMs":1791444731684}} {"type":"emit_inbound","label":"session/contextUsage","frame":{"jsonrpc":"2.0","method":"session/contextUsage","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:8","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"b1dab1f5-3c9e-4d92-b955-841a2269c23c","sequence":54},"last":{"id":"b1dab1f5-3c9e-4d92-b955-841a2269c23c","sequence":54}},"windowTokens":1007997,"usedTokens":32933,"pressure":"normal"},"emittedAtMs":1791444731684}} @@ -42,12 +42,12 @@ {"type":"emit_inbound","label":"session/statusChanged","frame":{"jsonrpc":"2.0","method":"session/statusChanged","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","status":"idle","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:23"},"emittedAtMs":1791444747386}} {"type":"emit_inbound","label":"turn/completed","frame":{"jsonrpc":"2.0","method":"turn/completed","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:23","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"7c29ac76-7e09-48c1-a18f-f336126e27d1","sequence":120},"last":{"id":"7c29ac76-7e09-48c1-a18f-f336126e27d1","sequence":120}},"turnId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6","terminal":"completed","durationMs":18608,"timeToFirstTokenMs":4791},"emittedAtMs":1791444747387}} {"type":"emit_inbound","label":"session/branchChanged","frame":{"jsonrpc":"2.0","method":"session/branchChanged","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:24","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"1e4afe30-d924-444c-8665-5e763f524e70","sequence":121},"last":{"id":"1e4afe30-d924-444c-8665-5e763f524e70","sequence":121}},"workspaceRoot":"","branch":"main","vcs":"git"},"emittedAtMs":1791444747394}} -{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":5,"method":"turn/start","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\n"},{"type":"text","text":"Run the shell command `touch muse-declined.txt` with your shell tool once. If it is rejected, do not retry; reply REJECTED."}],"displayText":"Run the shell command `touch muse-declined.txt` with your shell tool once. If it is rejected, do not retry; reply REJECTED.","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a11a6d-8566-7000-8ad1-6269ac187a29"}}} +{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":5,"method":"turn/start","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\n"},{"type":"text","text":"Run the shell command `touch muse-declined.txt` with your shell tool once. If it is rejected, do not retry; reply REJECTED."}],"displayText":"Run the shell command `touch muse-declined.txt` with your shell tool once. If it is rejected, do not retry; reply REJECTED.","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a11a6d-8566-7000-8ad1-6269ac187a29"}}} {"type":"emit_inbound","label":"response:turn/start","frame":{"jsonrpc":"2.0","id":5,"result":{"commandId":"01a11a6d-8566-7000-8ad1-6269ac187a29","status":"accepted","turnId":"01a11a6d-8566-7000-8ad1-6269ac187a29","startedNewTurn":true,"disposition":"started"}}} {"type":"emit_inbound","label":"session/statusChanged","frame":{"jsonrpc":"2.0","method":"session/statusChanged","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","status":"running","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:25"},"emittedAtMs":1791444747660}} {"type":"emit_inbound","label":"turn/started","frame":{"jsonrpc":"2.0","method":"turn/started","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:25","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"4a1cf90d-7457-448d-a2b5-d82108636b3b","sequence":128},"last":{"id":"4a1cf90d-7457-448d-a2b5-d82108636b3b","sequence":128}},"turnId":"01a11a6d-8566-7000-8ad1-6269ac187a29","commandId":"01a11a6d-8566-7000-8ad1-6269ac187a29"},"emittedAtMs":1791444747660}} -{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:26","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"4a1cf90d-7457-448d-a2b5-d82108636b3b","sequence":128},"last":{"id":"4a1cf90d-7457-448d-a2b5-d82108636b3b","sequence":128}},"item":{"itemId":"27473708-71e0-4d3c-811c-a8bae6bbaa3b","kind":"userMessage","turnId":"01a11a6d-8566-7000-8ad1-6269ac187a29","revision":1,"status":"completed","recordedAt":"2026-10-08T07:32:27.650913Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nRun the shell command `touch muse-declined.txt` with your shell tool once. If it is rejected, do not retry; reply REJECTED.","commandId":"01a11a6d-8566-7000-8ad1-6269ac187a29"}},"emittedAtMs":1791444747660}} -{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:27","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"4a056a03-469a-4c9f-94a9-78eb815023ad","sequence":130},"last":{"id":"4a056a03-469a-4c9f-94a9-78eb815023ad","sequence":130}},"item":{"itemId":"27473708-71e0-4d3c-811c-a8bae6bbaa3b","kind":"userMessage","turnId":"01a11a6d-8566-7000-8ad1-6269ac187a29","revision":2,"status":"completed","recordedAt":"2026-10-08T07:32:27.66745Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nRun the shell command `touch muse-declined.txt` with your shell tool once. If it is rejected, do not retry; reply REJECTED.","displayText":"Run the shell command `touch muse-declined.txt` with your shell tool once. If it is rejected, do not retry; reply REJECTED.","commandId":"01a11a6d-8566-7000-8ad1-6269ac187a29"}},"emittedAtMs":1791444747676}} +{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:26","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"4a1cf90d-7457-448d-a2b5-d82108636b3b","sequence":128},"last":{"id":"4a1cf90d-7457-448d-a2b5-d82108636b3b","sequence":128}},"item":{"itemId":"27473708-71e0-4d3c-811c-a8bae6bbaa3b","kind":"userMessage","turnId":"01a11a6d-8566-7000-8ad1-6269ac187a29","revision":1,"status":"completed","recordedAt":"2026-10-08T07:32:27.650913Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nRun the shell command `touch muse-declined.txt` with your shell tool once. If it is rejected, do not retry; reply REJECTED.","commandId":"01a11a6d-8566-7000-8ad1-6269ac187a29"}},"emittedAtMs":1791444747660}} +{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:27","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"4a056a03-469a-4c9f-94a9-78eb815023ad","sequence":130},"last":{"id":"4a056a03-469a-4c9f-94a9-78eb815023ad","sequence":130}},"item":{"itemId":"27473708-71e0-4d3c-811c-a8bae6bbaa3b","kind":"userMessage","turnId":"01a11a6d-8566-7000-8ad1-6269ac187a29","revision":2,"status":"completed","recordedAt":"2026-10-08T07:32:27.66745Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nRun the shell command `touch muse-declined.txt` with your shell tool once. If it is rejected, do not retry; reply REJECTED.","displayText":"Run the shell command `touch muse-declined.txt` with your shell tool once. If it is rejected, do not retry; reply REJECTED.","commandId":"01a11a6d-8566-7000-8ad1-6269ac187a29"}},"emittedAtMs":1791444747676}} {"type":"emit_inbound","label":"item/started","frame":{"jsonrpc":"2.0","method":"item/started","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:28","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"3b28955d-a87a-4acb-97e9-14cad87fd41f","sequence":148},"last":{"id":"3b28955d-a87a-4acb-97e9-14cad87fd41f","sequence":148}},"item":{"itemId":"3b28955d-a87a-4acb-97e9-14cad87fd41f","kind":"reminderChild","turnId":"01a11a6d-8566-7000-8ad1-6269ac187a29","revision":1,"status":"inProgress","recordedAt":"2026-10-08T07:32:27.787099Z","fallbackText":"Reminder child session","childSessionId":"efd21bf1-4fd1-49a2-9c55-91b7fc099765","reminderAgentId":"skill-reminder","generationId":1,"taskId":"01a11a6d-85ed-7b91-8d91-c4ce76b6dfef"}},"emittedAtMs":1791444747794}} {"type":"emit_inbound","label":"session/tokenUsage","frame":{"jsonrpc":"2.0","method":"session/tokenUsage","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:29","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"b032c01d-eae1-437e-910c-8756aedc6084","sequence":164},"last":{"id":"b032c01d-eae1-437e-910c-8756aedc6084","sequence":164}},"turnId":"01a11a6d-8566-7000-8ad1-6269ac187a29","modelId":"muse-spark-1.3-contributor","usage":{"inputTokens":33401,"outputTokens":201,"cachedTokens":33009,"cacheWriteTokens":0,"cacheReadTokens":33009,"reasoningTokens":108},"promptTokens":33401,"totalTokens":33602,"finishReason":"tool_calls","durationMs":2522,"cumulative":{"promptTokens":99045,"outputTokens":538,"totalTokens":99583,"cacheReadTokens":65506,"cost":{"usd":0.003592512,"partial":false}}},"emittedAtMs":1791444750395}} {"type":"emit_inbound","label":"session/contextUsage","frame":{"jsonrpc":"2.0","method":"session/contextUsage","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:30","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"b032c01d-eae1-437e-910c-8756aedc6084","sequence":164},"last":{"id":"b032c01d-eae1-437e-910c-8756aedc6084","sequence":164}},"windowTokens":1007997,"usedTokens":33602,"pressure":"normal"},"emittedAtMs":1791444750395}} diff --git a/apps/server/src/orchestration-v2/testkit/fixtures/muse_subagent_approval/muse_transcript.ndjson b/apps/server/src/orchestration-v2/testkit/fixtures/muse_subagent_approval/muse_transcript.ndjson index b602a51dff03d..cc6f79c9a3130 100644 --- a/apps/server/src/orchestration-v2/testkit/fixtures/muse_subagent_approval/muse_transcript.ndjson +++ b/apps/server/src/orchestration-v2/testkit/fixtures/muse_subagent_approval/muse_transcript.ndjson @@ -7,13 +7,13 @@ {"type":"emit_inbound","label":"session/started","frame":{"jsonrpc":"2.0","method":"session/started","params":{"session":{"sessionId":"01a12d1d-a1b7-7000-9697-d430c2e2afd8","path":"~/.local/share/muse/sessions/2026/10/11/01a12d1d-a1b7-7000-9697-d430c2e2afd8/session.jsonl","status":"idle","activeTurnId":null,"createdAt":"2026-10-11T22:37:59.14136Z","updatedAt":"2026-10-11T22:37:59.141364Z","workspaceRoot":"","providerId":"meta","modelId":"muse-spark-1.3-contributor","turnCount":0,"forkedFrom":null,"sideFrom":null,"approvalMode":{"mode":"promptUnmatched","source":"startup","lastCommandId":null},"createdByClient":{"name":"t3_code","version":"1"},"kind":"root"}},"emittedAtMs":1791758279294}} {"type":"emit_inbound","label":"response:session/start","frame":{"jsonrpc":"2.0","id":2,"result":{"session":{"sessionId":"01a12d1d-a1b7-7000-9697-d430c2e2afd8","path":"~/.local/share/muse/sessions/2026/10/11/01a12d1d-a1b7-7000-9697-d430c2e2afd8/session.jsonl","status":"idle","activeTurnId":null,"createdAt":"2026-10-11T22:37:59.14136Z","updatedAt":"2026-10-11T22:37:59.141364Z","workspaceRoot":"","providerId":"meta","modelId":"muse-spark-1.3-contributor","turnCount":0,"forkedFrom":null,"sideFrom":null,"approvalMode":{"mode":"promptUnmatched","source":"startup","lastCommandId":null},"createdByClient":{"name":"t3_code","version":"1"},"kind":"root"},"viewCursor":"v:01a12d1d-a1b7-7000-9697-d430c2e2afd8:1"}}} {"type":"emit_inbound","label":"session/approvalModeChanged","frame":{"jsonrpc":"2.0","method":"session/approvalModeChanged","params":{"sessionId":"01a12d1d-a1b7-7000-9697-d430c2e2afd8","viewCursor":"v:01a12d1d-a1b7-7000-9697-d430c2e2afd8:1","sourceRange":{"stream":{"kind":"session","id":"01a12d1d-a1b7-7000-9697-d430c2e2afd8"},"first":{"id":"ed95a9db-eada-4751-a7e6-22ccc9bdabd5","sequence":5},"last":{"id":"ed95a9db-eada-4751-a7e6-22ccc9bdabd5","sequence":5}},"mode":"promptUnmatched","source":"approvalReconfigure","commandId":"01a12d1d-a1b8-7000-8d6c-f1324b724cdb","clientName":"t3_code"},"emittedAtMs":1791758279297}} -{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a12d1d-a1b7-7000-9697-d430c2e2afd8","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\n"},{"type":"text","text":"Do not run any commands yourself. Spawn one subagent whose only task is to run this exact shell command: printf 'subagent approval fixture' > subagent-approval.txt and then reply with exactly: Written. Wait for it to finish, then reply with exactly what it said."}],"displayText":"Do not run any commands yourself. Spawn one subagent whose only task is to run this exact shell command: printf 'subagent approval fixture' > subagent-approval.txt and then reply with exactly: Written. Wait for it to finish, then reply with exactly what it said.","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a12d1d-a348-7000-ae97-9d27b5f80bce"}}} +{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a12d1d-a1b7-7000-9697-d430c2e2afd8","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\n"},{"type":"text","text":"Do not run any commands yourself. Spawn one subagent whose only task is to run this exact shell command: printf 'subagent approval fixture' > subagent-approval.txt and then reply with exactly: Written. Wait for it to finish, then reply with exactly what it said."}],"displayText":"Do not run any commands yourself. Spawn one subagent whose only task is to run this exact shell command: printf 'subagent approval fixture' > subagent-approval.txt and then reply with exactly: Written. Wait for it to finish, then reply with exactly what it said.","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a12d1d-a348-7000-ae97-9d27b5f80bce"}}} {"type":"emit_inbound","label":"session/branchChanged","frame":{"jsonrpc":"2.0","method":"session/branchChanged","params":{"sessionId":"01a12d1d-a1b7-7000-9697-d430c2e2afd8","viewCursor":"v:01a12d1d-a1b7-7000-9697-d430c2e2afd8:2","sourceRange":{"stream":{"kind":"session","id":"01a12d1d-a1b7-7000-9697-d430c2e2afd8"},"first":{"id":"4f170229-d428-4a64-9516-7a355ff3bcdb","sequence":15},"last":{"id":"4f170229-d428-4a64-9516-7a355ff3bcdb","sequence":15}},"workspaceRoot":"","branch":"main","vcs":"git"},"emittedAtMs":1791758279531}} {"type":"emit_inbound","label":"response:turn/start","frame":{"jsonrpc":"2.0","id":3,"result":{"commandId":"01a12d1d-a348-7000-ae97-9d27b5f80bce","status":"accepted","turnId":"01a12d1d-a348-7000-ae97-9d27b5f80bce","startedNewTurn":true,"disposition":"started"}}} {"type":"emit_inbound","label":"session/statusChanged","frame":{"jsonrpc":"2.0","method":"session/statusChanged","params":{"sessionId":"01a12d1d-a1b7-7000-9697-d430c2e2afd8","status":"running","viewCursor":"v:01a12d1d-a1b7-7000-9697-d430c2e2afd8:3"},"emittedAtMs":1791758279541}} {"type":"emit_inbound","label":"turn/started","frame":{"jsonrpc":"2.0","method":"turn/started","params":{"sessionId":"01a12d1d-a1b7-7000-9697-d430c2e2afd8","viewCursor":"v:01a12d1d-a1b7-7000-9697-d430c2e2afd8:3","sourceRange":{"stream":{"kind":"session","id":"01a12d1d-a1b7-7000-9697-d430c2e2afd8"},"first":{"id":"429e8ecf-d532-4b14-a303-b9f832c36a51","sequence":17},"last":{"id":"429e8ecf-d532-4b14-a303-b9f832c36a51","sequence":17}},"turnId":"01a12d1d-a348-7000-ae97-9d27b5f80bce","commandId":"01a12d1d-a348-7000-ae97-9d27b5f80bce"},"emittedAtMs":1791758279542}} -{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a12d1d-a1b7-7000-9697-d430c2e2afd8","viewCursor":"v:01a12d1d-a1b7-7000-9697-d430c2e2afd8:4","sourceRange":{"stream":{"kind":"session","id":"01a12d1d-a1b7-7000-9697-d430c2e2afd8"},"first":{"id":"429e8ecf-d532-4b14-a303-b9f832c36a51","sequence":17},"last":{"id":"429e8ecf-d532-4b14-a303-b9f832c36a51","sequence":17}},"item":{"itemId":"a4b3de8d-842b-4065-8769-aed18e62e9fc","kind":"userMessage","turnId":"01a12d1d-a348-7000-ae97-9d27b5f80bce","revision":1,"status":"completed","recordedAt":"2026-10-11T22:37:59.531477Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nDo not run any commands yourself. Spawn one subagent whose only task is to run this exact shell command: printf 'subagent approval fixture' > subagent-approval.txt and then reply with exactly: Written. Wait for it to finish, then reply with exactly what it said.","commandId":"01a12d1d-a348-7000-ae97-9d27b5f80bce"}},"emittedAtMs":1791758279542}} -{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a12d1d-a1b7-7000-9697-d430c2e2afd8","viewCursor":"v:01a12d1d-a1b7-7000-9697-d430c2e2afd8:5","sourceRange":{"stream":{"kind":"session","id":"01a12d1d-a1b7-7000-9697-d430c2e2afd8"},"first":{"id":"5a63e348-5bf6-4262-b37f-d31d307ea6d8","sequence":19},"last":{"id":"5a63e348-5bf6-4262-b37f-d31d307ea6d8","sequence":19}},"item":{"itemId":"a4b3de8d-842b-4065-8769-aed18e62e9fc","kind":"userMessage","turnId":"01a12d1d-a348-7000-ae97-9d27b5f80bce","revision":2,"status":"completed","recordedAt":"2026-10-11T22:37:59.545908Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nDo not run any commands yourself. Spawn one subagent whose only task is to run this exact shell command: printf 'subagent approval fixture' > subagent-approval.txt and then reply with exactly: Written. Wait for it to finish, then reply with exactly what it said.","displayText":"Do not run any commands yourself. Spawn one subagent whose only task is to run this exact shell command: printf 'subagent approval fixture' > subagent-approval.txt and then reply with exactly: Written. Wait for it to finish, then reply with exactly what it said.","commandId":"01a12d1d-a348-7000-ae97-9d27b5f80bce"}},"emittedAtMs":1791758279553}} +{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a12d1d-a1b7-7000-9697-d430c2e2afd8","viewCursor":"v:01a12d1d-a1b7-7000-9697-d430c2e2afd8:4","sourceRange":{"stream":{"kind":"session","id":"01a12d1d-a1b7-7000-9697-d430c2e2afd8"},"first":{"id":"429e8ecf-d532-4b14-a303-b9f832c36a51","sequence":17},"last":{"id":"429e8ecf-d532-4b14-a303-b9f832c36a51","sequence":17}},"item":{"itemId":"a4b3de8d-842b-4065-8769-aed18e62e9fc","kind":"userMessage","turnId":"01a12d1d-a348-7000-ae97-9d27b5f80bce","revision":1,"status":"completed","recordedAt":"2026-10-11T22:37:59.531477Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nDo not run any commands yourself. Spawn one subagent whose only task is to run this exact shell command: printf 'subagent approval fixture' > subagent-approval.txt and then reply with exactly: Written. Wait for it to finish, then reply with exactly what it said.","commandId":"01a12d1d-a348-7000-ae97-9d27b5f80bce"}},"emittedAtMs":1791758279542}} +{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a12d1d-a1b7-7000-9697-d430c2e2afd8","viewCursor":"v:01a12d1d-a1b7-7000-9697-d430c2e2afd8:5","sourceRange":{"stream":{"kind":"session","id":"01a12d1d-a1b7-7000-9697-d430c2e2afd8"},"first":{"id":"5a63e348-5bf6-4262-b37f-d31d307ea6d8","sequence":19},"last":{"id":"5a63e348-5bf6-4262-b37f-d31d307ea6d8","sequence":19}},"item":{"itemId":"a4b3de8d-842b-4065-8769-aed18e62e9fc","kind":"userMessage","turnId":"01a12d1d-a348-7000-ae97-9d27b5f80bce","revision":2,"status":"completed","recordedAt":"2026-10-11T22:37:59.545908Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nDo not run any commands yourself. Spawn one subagent whose only task is to run this exact shell command: printf 'subagent approval fixture' > subagent-approval.txt and then reply with exactly: Written. Wait for it to finish, then reply with exactly what it said.","displayText":"Do not run any commands yourself. Spawn one subagent whose only task is to run this exact shell command: printf 'subagent approval fixture' > subagent-approval.txt and then reply with exactly: Written. Wait for it to finish, then reply with exactly what it said.","commandId":"01a12d1d-a348-7000-ae97-9d27b5f80bce"}},"emittedAtMs":1791758279553}} {"type":"emit_inbound","label":"item/started","frame":{"jsonrpc":"2.0","method":"item/started","params":{"sessionId":"01a12d1d-a1b7-7000-9697-d430c2e2afd8","viewCursor":"v:01a12d1d-a1b7-7000-9697-d430c2e2afd8:6","sourceRange":{"stream":{"kind":"session","id":"01a12d1d-a1b7-7000-9697-d430c2e2afd8"},"first":{"id":"d55286a9-a9e2-4d7b-be97-d44360bcca2b","sequence":37},"last":{"id":"d55286a9-a9e2-4d7b-be97-d44360bcca2b","sequence":37}},"item":{"itemId":"d55286a9-a9e2-4d7b-be97-d44360bcca2b","kind":"reminderChild","turnId":"01a12d1d-a348-7000-ae97-9d27b5f80bce","revision":1,"status":"inProgress","recordedAt":"2026-10-11T22:37:59.660065Z","fallbackText":"Reminder child session","childSessionId":"0779d605-54b8-45dc-90b6-91dc58b9a52c","reminderAgentId":"skill-reminder","generationId":1,"taskId":"01a12d1d-a3d4-7712-8b98-26f3c4f9bf38"}},"emittedAtMs":1791758279666}} {"type":"emit_inbound","label":"item/started","frame":{"jsonrpc":"2.0","method":"item/started","params":{"sessionId":"01a12d1d-a1b7-7000-9697-d430c2e2afd8","viewCursor":"v:01a12d1d-a1b7-7000-9697-d430c2e2afd8:6.1","item":{"itemId":"21b74b34-fe9f-457c-83f0-cf49257b5948","kind":"reasoning","turnId":"01a12d1d-a348-7000-ae97-9d27b5f80bce","revision":1,"status":"inProgress","summary":[""]}},"emittedAtMs":1791758282481}} {"type":"emit_inbound","label":"item/delta","frame":{"jsonrpc":"2.0","method":"item/delta","params":{"sessionId":"01a12d1d-a1b7-7000-9697-d430c2e2afd8","viewCursor":"v:01a12d1d-a1b7-7000-9697-d430c2e2afd8:6.2","itemId":"21b74b34-fe9f-457c-83f0-cf49257b5948","field":"summary.0","delta":"Spawning a subagent via muse_subagent_spawn to run a shell command and return its exact output, while avoiding direct command execution."},"emittedAtMs":1791758282481}} diff --git a/apps/server/src/orchestration-v2/testkit/fixtures/muse_subagent_interrupt/muse_transcript.ndjson b/apps/server/src/orchestration-v2/testkit/fixtures/muse_subagent_interrupt/muse_transcript.ndjson index d50e22967dad4..dc74692f2052d 100644 --- a/apps/server/src/orchestration-v2/testkit/fixtures/muse_subagent_interrupt/muse_transcript.ndjson +++ b/apps/server/src/orchestration-v2/testkit/fixtures/muse_subagent_interrupt/muse_transcript.ndjson @@ -7,13 +7,13 @@ {"type":"emit_inbound","label":"session/started","frame":{"jsonrpc":"2.0","method":"session/started","params":{"session":{"sessionId":"01a12d28-7788-7000-a946-c407f380868e","path":"~/.local/share/muse/sessions/2026/10/11/01a12d28-7788-7000-a946-c407f380868e/session.jsonl","status":"idle","activeTurnId":null,"createdAt":"2026-10-11T22:49:49.235205Z","updatedAt":"2026-10-11T22:49:49.235209Z","workspaceRoot":"","providerId":"meta","modelId":"muse-spark-1.3-contributor","turnCount":0,"forkedFrom":null,"sideFrom":null,"approvalMode":{"mode":"allowAll","source":"startup","lastCommandId":null},"createdByClient":{"name":"t3_code","version":"1"},"kind":"root"}},"emittedAtMs":1791758989388}} {"type":"emit_inbound","label":"response:session/start","frame":{"jsonrpc":"2.0","id":2,"result":{"session":{"sessionId":"01a12d28-7788-7000-a946-c407f380868e","path":"~/.local/share/muse/sessions/2026/10/11/01a12d28-7788-7000-a946-c407f380868e/session.jsonl","status":"idle","activeTurnId":null,"createdAt":"2026-10-11T22:49:49.235205Z","updatedAt":"2026-10-11T22:49:49.235209Z","workspaceRoot":"","providerId":"meta","modelId":"muse-spark-1.3-contributor","turnCount":0,"forkedFrom":null,"sideFrom":null,"approvalMode":{"mode":"allowAll","source":"startup","lastCommandId":null},"createdByClient":{"name":"t3_code","version":"1"},"kind":"root"},"viewCursor":"v:01a12d28-7788-7000-a946-c407f380868e:1"}}} {"type":"emit_inbound","label":"session/approvalModeChanged","frame":{"jsonrpc":"2.0","method":"session/approvalModeChanged","params":{"sessionId":"01a12d28-7788-7000-a946-c407f380868e","viewCursor":"v:01a12d28-7788-7000-a946-c407f380868e:1","sourceRange":{"stream":{"kind":"session","id":"01a12d28-7788-7000-a946-c407f380868e"},"first":{"id":"29d4e769-78f7-48fa-90cd-3ac9f8fa04bf","sequence":5},"last":{"id":"29d4e769-78f7-48fa-90cd-3ac9f8fa04bf","sequence":5}},"mode":"allowAll","source":"approvalReconfigure","commandId":"01a12d28-7788-7001-a223-c10e52187cdf","clientName":"t3_code"},"emittedAtMs":1791758989391}} -{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a12d28-7788-7000-a946-c407f380868e","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\n"},{"type":"text","text":"Spawn one subagent whose only task is to run the shell command `sleep 30` and then reply with exactly: Done. Wait for it to finish, then reply with exactly what it said."}],"displayText":"Spawn one subagent whose only task is to run the shell command `sleep 30` and then reply with exactly: Done. Wait for it to finish, then reply with exactly what it said.","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a12d28-7912-7000-993a-afd019e6ac5c"}}} +{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a12d28-7788-7000-a946-c407f380868e","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\n"},{"type":"text","text":"Spawn one subagent whose only task is to run the shell command `sleep 30` and then reply with exactly: Done. Wait for it to finish, then reply with exactly what it said."}],"displayText":"Spawn one subagent whose only task is to run the shell command `sleep 30` and then reply with exactly: Done. Wait for it to finish, then reply with exactly what it said.","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a12d28-7912-7000-993a-afd019e6ac5c"}}} {"type":"emit_inbound","label":"session/branchChanged","frame":{"jsonrpc":"2.0","method":"session/branchChanged","params":{"sessionId":"01a12d28-7788-7000-a946-c407f380868e","viewCursor":"v:01a12d28-7788-7000-a946-c407f380868e:2","sourceRange":{"stream":{"kind":"session","id":"01a12d28-7788-7000-a946-c407f380868e"},"first":{"id":"660e2cfd-318a-4d2d-87bb-0b1375d79fdf","sequence":15},"last":{"id":"660e2cfd-318a-4d2d-87bb-0b1375d79fdf","sequence":15}},"workspaceRoot":"","branch":"main","vcs":"git"},"emittedAtMs":1791758989619}} {"type":"emit_inbound","label":"response:turn/start","frame":{"jsonrpc":"2.0","id":3,"result":{"commandId":"01a12d28-7912-7000-993a-afd019e6ac5c","status":"accepted","turnId":"01a12d28-7912-7000-993a-afd019e6ac5c","startedNewTurn":true,"disposition":"started"}}} {"type":"emit_inbound","label":"session/statusChanged","frame":{"jsonrpc":"2.0","method":"session/statusChanged","params":{"sessionId":"01a12d28-7788-7000-a946-c407f380868e","status":"running","viewCursor":"v:01a12d28-7788-7000-a946-c407f380868e:3"},"emittedAtMs":1791758989632}} {"type":"emit_inbound","label":"turn/started","frame":{"jsonrpc":"2.0","method":"turn/started","params":{"sessionId":"01a12d28-7788-7000-a946-c407f380868e","viewCursor":"v:01a12d28-7788-7000-a946-c407f380868e:3","sourceRange":{"stream":{"kind":"session","id":"01a12d28-7788-7000-a946-c407f380868e"},"first":{"id":"5cb199bd-04e5-40d9-a299-8760be80e0a4","sequence":17},"last":{"id":"5cb199bd-04e5-40d9-a299-8760be80e0a4","sequence":17}},"turnId":"01a12d28-7912-7000-993a-afd019e6ac5c","commandId":"01a12d28-7912-7000-993a-afd019e6ac5c"},"emittedAtMs":1791758989632}} -{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a12d28-7788-7000-a946-c407f380868e","viewCursor":"v:01a12d28-7788-7000-a946-c407f380868e:4","sourceRange":{"stream":{"kind":"session","id":"01a12d28-7788-7000-a946-c407f380868e"},"first":{"id":"5cb199bd-04e5-40d9-a299-8760be80e0a4","sequence":17},"last":{"id":"5cb199bd-04e5-40d9-a299-8760be80e0a4","sequence":17}},"item":{"itemId":"5e234d9d-0744-4757-9629-761ea4e7f062","kind":"userMessage","turnId":"01a12d28-7912-7000-993a-afd019e6ac5c","revision":1,"status":"completed","recordedAt":"2026-10-11T22:49:49.620067Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nSpawn one subagent whose only task is to run the shell command `sleep 30` and then reply with exactly: Done. Wait for it to finish, then reply with exactly what it said.","commandId":"01a12d28-7912-7000-993a-afd019e6ac5c"}},"emittedAtMs":1791758989632}} -{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a12d28-7788-7000-a946-c407f380868e","viewCursor":"v:01a12d28-7788-7000-a946-c407f380868e:5","sourceRange":{"stream":{"kind":"session","id":"01a12d28-7788-7000-a946-c407f380868e"},"first":{"id":"5724636e-9398-4d23-87ac-1e9a2b83ad9f","sequence":19},"last":{"id":"5724636e-9398-4d23-87ac-1e9a2b83ad9f","sequence":19}},"item":{"itemId":"5e234d9d-0744-4757-9629-761ea4e7f062","kind":"userMessage","turnId":"01a12d28-7912-7000-993a-afd019e6ac5c","revision":2,"status":"completed","recordedAt":"2026-10-11T22:49:49.635395Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nSpawn one subagent whose only task is to run the shell command `sleep 30` and then reply with exactly: Done. Wait for it to finish, then reply with exactly what it said.","displayText":"Spawn one subagent whose only task is to run the shell command `sleep 30` and then reply with exactly: Done. Wait for it to finish, then reply with exactly what it said.","commandId":"01a12d28-7912-7000-993a-afd019e6ac5c"}},"emittedAtMs":1791758989642}} +{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a12d28-7788-7000-a946-c407f380868e","viewCursor":"v:01a12d28-7788-7000-a946-c407f380868e:4","sourceRange":{"stream":{"kind":"session","id":"01a12d28-7788-7000-a946-c407f380868e"},"first":{"id":"5cb199bd-04e5-40d9-a299-8760be80e0a4","sequence":17},"last":{"id":"5cb199bd-04e5-40d9-a299-8760be80e0a4","sequence":17}},"item":{"itemId":"5e234d9d-0744-4757-9629-761ea4e7f062","kind":"userMessage","turnId":"01a12d28-7912-7000-993a-afd019e6ac5c","revision":1,"status":"completed","recordedAt":"2026-10-11T22:49:49.620067Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nSpawn one subagent whose only task is to run the shell command `sleep 30` and then reply with exactly: Done. Wait for it to finish, then reply with exactly what it said.","commandId":"01a12d28-7912-7000-993a-afd019e6ac5c"}},"emittedAtMs":1791758989632}} +{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a12d28-7788-7000-a946-c407f380868e","viewCursor":"v:01a12d28-7788-7000-a946-c407f380868e:5","sourceRange":{"stream":{"kind":"session","id":"01a12d28-7788-7000-a946-c407f380868e"},"first":{"id":"5724636e-9398-4d23-87ac-1e9a2b83ad9f","sequence":19},"last":{"id":"5724636e-9398-4d23-87ac-1e9a2b83ad9f","sequence":19}},"item":{"itemId":"5e234d9d-0744-4757-9629-761ea4e7f062","kind":"userMessage","turnId":"01a12d28-7912-7000-993a-afd019e6ac5c","revision":2,"status":"completed","recordedAt":"2026-10-11T22:49:49.635395Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nSpawn one subagent whose only task is to run the shell command `sleep 30` and then reply with exactly: Done. Wait for it to finish, then reply with exactly what it said.","displayText":"Spawn one subagent whose only task is to run the shell command `sleep 30` and then reply with exactly: Done. Wait for it to finish, then reply with exactly what it said.","commandId":"01a12d28-7912-7000-993a-afd019e6ac5c"}},"emittedAtMs":1791758989642}} {"type":"emit_inbound","label":"item/started","frame":{"jsonrpc":"2.0","method":"item/started","params":{"sessionId":"01a12d28-7788-7000-a946-c407f380868e","viewCursor":"v:01a12d28-7788-7000-a946-c407f380868e:6","sourceRange":{"stream":{"kind":"session","id":"01a12d28-7788-7000-a946-c407f380868e"},"first":{"id":"44afb415-d6e4-4d51-9002-4deb196d463a","sequence":36},"last":{"id":"44afb415-d6e4-4d51-9002-4deb196d463a","sequence":36}},"item":{"itemId":"44afb415-d6e4-4d51-9002-4deb196d463a","kind":"reminderChild","turnId":"01a12d28-7912-7000-993a-afd019e6ac5c","revision":1,"status":"inProgress","recordedAt":"2026-10-11T22:49:49.743418Z","fallbackText":"Reminder child session","childSessionId":"4dd3511e-2115-4fbb-a4c4-383cbb383d63","reminderAgentId":"skill-reminder","generationId":1,"taskId":"01a12d28-7997-79d0-8a69-8ac0acf92a56"}},"emittedAtMs":1791758989750}} {"type":"emit_inbound","label":"session/tokenUsage","frame":{"jsonrpc":"2.0","method":"session/tokenUsage","params":{"sessionId":"01a12d28-7788-7000-a946-c407f380868e","viewCursor":"v:01a12d28-7788-7000-a946-c407f380868e:7","sourceRange":{"stream":{"kind":"session","id":"01a12d28-7788-7000-a946-c407f380868e"},"first":{"id":"c94f5484-20b7-4057-a736-303e97bc097c","sequence":52},"last":{"id":"c94f5484-20b7-4057-a736-303e97bc097c","sequence":52}},"turnId":"01a12d28-7912-7000-993a-afd019e6ac5c","modelId":"muse-spark-1.3-contributor","usage":{"inputTokens":32911,"outputTokens":264,"cachedTokens":0,"cacheWriteTokens":0,"cacheReadTokens":0,"reasoningTokens":143},"promptTokens":32911,"totalTokens":33175,"finishReason":"tool_calls","durationMs":3651,"cumulative":{"promptTokens":32911,"outputTokens":264,"totalTokens":33175,"cost":{"usd":0.0033439000000000003,"partial":false}}},"emittedAtMs":1791758993485}} {"type":"emit_inbound","label":"session/contextUsage","frame":{"jsonrpc":"2.0","method":"session/contextUsage","params":{"sessionId":"01a12d28-7788-7000-a946-c407f380868e","viewCursor":"v:01a12d28-7788-7000-a946-c407f380868e:8","sourceRange":{"stream":{"kind":"session","id":"01a12d28-7788-7000-a946-c407f380868e"},"first":{"id":"c94f5484-20b7-4057-a736-303e97bc097c","sequence":52},"last":{"id":"c94f5484-20b7-4057-a736-303e97bc097c","sequence":52}},"windowTokens":1007997,"usedTokens":33175,"pressure":"normal"},"emittedAtMs":1791758993485}} diff --git a/apps/server/src/orchestration-v2/testkit/fixtures/muse_workflow/muse_transcript.ndjson b/apps/server/src/orchestration-v2/testkit/fixtures/muse_workflow/muse_transcript.ndjson index cccd50395bb08..f2bb827e4e21f 100644 --- a/apps/server/src/orchestration-v2/testkit/fixtures/muse_workflow/muse_transcript.ndjson +++ b/apps/server/src/orchestration-v2/testkit/fixtures/muse_workflow/muse_transcript.ndjson @@ -8,13 +8,13 @@ {"type":"emit_inbound","label":"response:session/start","frame":{"jsonrpc":"2.0","id":2,"result":{"session":{"sessionId":"01a12d5d-405f-7000-89a3-5aa62af6ea23","path":"~/.local/share/muse/sessions/2026/10/11/01a12d5d-405f-7000-89a3-5aa62af6ea23/session.jsonl","status":"idle","activeTurnId":null,"createdAt":"2026-10-11T23:47:28.587276Z","updatedAt":"2026-10-11T23:47:28.587281Z","workspaceRoot":"","providerId":"meta","modelId":"muse-spark-1.3-contributor","turnCount":0,"forkedFrom":null,"sideFrom":null,"approvalMode":{"mode":"allowAll","source":"startup","lastCommandId":null},"createdByClient":{"name":"t3_code","version":"1"},"kind":"root"},"viewCursor":"v:01a12d5d-405f-7000-89a3-5aa62af6ea23:2"}}} {"type":"emit_inbound","label":"session/approvalModeChanged","frame":{"jsonrpc":"2.0","method":"session/approvalModeChanged","params":{"sessionId":"01a12d5d-405f-7000-89a3-5aa62af6ea23","viewCursor":"v:01a12d5d-405f-7000-89a3-5aa62af6ea23:1","sourceRange":{"stream":{"kind":"session","id":"01a12d5d-405f-7000-89a3-5aa62af6ea23"},"first":{"id":"0c13b14b-877c-4c5b-b05f-8ad250dfd381","sequence":5},"last":{"id":"0c13b14b-877c-4c5b-b05f-8ad250dfd381","sequence":5}},"mode":"allowAll","source":"approvalReconfigure","commandId":"01a12d5d-4060-7000-b6cd-aaabe26f80de","clientName":"t3_code"},"emittedAtMs":1791762448807}} {"type":"emit_inbound","label":"session/branchChanged","frame":{"jsonrpc":"2.0","method":"session/branchChanged","params":{"sessionId":"01a12d5d-405f-7000-89a3-5aa62af6ea23","viewCursor":"v:01a12d5d-405f-7000-89a3-5aa62af6ea23:2","sourceRange":{"stream":{"kind":"session","id":"01a12d5d-405f-7000-89a3-5aa62af6ea23"},"first":{"id":"28b1f3f4-4448-4a21-a1a0-2df72ad1d326","sequence":6},"last":{"id":"28b1f3f4-4448-4a21-a1a0-2df72ad1d326","sequence":6}},"workspaceRoot":"","branch":"main","vcs":"git"},"emittedAtMs":1791762448807}} -{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a12d5d-405f-7000-89a3-5aa62af6ea23","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\n"},{"type":"text","text":"Use your workflow tool to launch a workflow with exactly TWO agents: one labeled 'ping' that replies with the word PING, and one labeled 'pong' that replies with the word PONG. Launch it and end your turn; do not wait for it."}],"displayText":"Use your workflow tool to launch a workflow with exactly TWO agents: one labeled 'ping' that replies with the word PING, and one labeled 'pong' that replies with the word PONG. Launch it and end your turn; do not wait for it.","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5"}}} +{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a12d5d-405f-7000-89a3-5aa62af6ea23","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\n"},{"type":"text","text":"Use your workflow tool to launch a workflow with exactly TWO agents: one labeled 'ping' that replies with the word PING, and one labeled 'pong' that replies with the word PONG. Launch it and end your turn; do not wait for it."}],"displayText":"Use your workflow tool to launch a workflow with exactly TWO agents: one labeled 'ping' that replies with the word PING, and one labeled 'pong' that replies with the word PONG. Launch it and end your turn; do not wait for it.","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5"}}} {"type":"emit_inbound","label":"session/branchChanged","frame":{"jsonrpc":"2.0","method":"session/branchChanged","params":{"sessionId":"01a12d5d-405f-7000-89a3-5aa62af6ea23","viewCursor":"v:01a12d5d-405f-7000-89a3-5aa62af6ea23:3","sourceRange":{"stream":{"kind":"session","id":"01a12d5d-405f-7000-89a3-5aa62af6ea23"},"first":{"id":"3f1880e1-81a0-4f21-b536-6b4fa5cf9d12","sequence":16},"last":{"id":"3f1880e1-81a0-4f21-b536-6b4fa5cf9d12","sequence":16}},"workspaceRoot":"","branch":"main","vcs":"git"},"emittedAtMs":1791762449097}} {"type":"emit_inbound","label":"response:turn/start","frame":{"jsonrpc":"2.0","id":3,"result":{"commandId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5","status":"accepted","turnId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5","startedNewTurn":true,"disposition":"started"}}} {"type":"emit_inbound","label":"session/statusChanged","frame":{"jsonrpc":"2.0","method":"session/statusChanged","params":{"sessionId":"01a12d5d-405f-7000-89a3-5aa62af6ea23","status":"running","viewCursor":"v:01a12d5d-405f-7000-89a3-5aa62af6ea23:4"},"emittedAtMs":1791762449102}} {"type":"emit_inbound","label":"turn/started","frame":{"jsonrpc":"2.0","method":"turn/started","params":{"sessionId":"01a12d5d-405f-7000-89a3-5aa62af6ea23","viewCursor":"v:01a12d5d-405f-7000-89a3-5aa62af6ea23:4","sourceRange":{"stream":{"kind":"session","id":"01a12d5d-405f-7000-89a3-5aa62af6ea23"},"first":{"id":"7a830825-f571-4781-8e4f-a4ef5e085bb7","sequence":18},"last":{"id":"7a830825-f571-4781-8e4f-a4ef5e085bb7","sequence":18}},"turnId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5","commandId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5"},"emittedAtMs":1791762449103}} -{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a12d5d-405f-7000-89a3-5aa62af6ea23","viewCursor":"v:01a12d5d-405f-7000-89a3-5aa62af6ea23:5","sourceRange":{"stream":{"kind":"session","id":"01a12d5d-405f-7000-89a3-5aa62af6ea23"},"first":{"id":"7a830825-f571-4781-8e4f-a4ef5e085bb7","sequence":18},"last":{"id":"7a830825-f571-4781-8e4f-a4ef5e085bb7","sequence":18}},"item":{"itemId":"0c1ceda9-41f7-4c3e-990e-4c53873c7ef5","kind":"userMessage","turnId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5","revision":1,"status":"completed","recordedAt":"2026-10-11T23:47:29.098117Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nUse your workflow tool to launch a workflow with exactly TWO agents: one labeled 'ping' that replies with the word PING, and one labeled 'pong' that replies with the word PONG. Launch it and end your turn; do not wait for it.","commandId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5"}},"emittedAtMs":1791762449103}} -{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a12d5d-405f-7000-89a3-5aa62af6ea23","viewCursor":"v:01a12d5d-405f-7000-89a3-5aa62af6ea23:6","sourceRange":{"stream":{"kind":"session","id":"01a12d5d-405f-7000-89a3-5aa62af6ea23"},"first":{"id":"0411a5c8-2c8a-4c9e-bb67-25fa3cef21a3","sequence":20},"last":{"id":"0411a5c8-2c8a-4c9e-bb67-25fa3cef21a3","sequence":20}},"item":{"itemId":"0c1ceda9-41f7-4c3e-990e-4c53873c7ef5","kind":"userMessage","turnId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5","revision":2,"status":"completed","recordedAt":"2026-10-11T23:47:29.107019Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nUse your workflow tool to launch a workflow with exactly TWO agents: one labeled 'ping' that replies with the word PING, and one labeled 'pong' that replies with the word PONG. Launch it and end your turn; do not wait for it.","displayText":"Use your workflow tool to launch a workflow with exactly TWO agents: one labeled 'ping' that replies with the word PING, and one labeled 'pong' that replies with the word PONG. Launch it and end your turn; do not wait for it.","commandId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5"}},"emittedAtMs":1791762449131}} +{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a12d5d-405f-7000-89a3-5aa62af6ea23","viewCursor":"v:01a12d5d-405f-7000-89a3-5aa62af6ea23:5","sourceRange":{"stream":{"kind":"session","id":"01a12d5d-405f-7000-89a3-5aa62af6ea23"},"first":{"id":"7a830825-f571-4781-8e4f-a4ef5e085bb7","sequence":18},"last":{"id":"7a830825-f571-4781-8e4f-a4ef5e085bb7","sequence":18}},"item":{"itemId":"0c1ceda9-41f7-4c3e-990e-4c53873c7ef5","kind":"userMessage","turnId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5","revision":1,"status":"completed","recordedAt":"2026-10-11T23:47:29.098117Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nUse your workflow tool to launch a workflow with exactly TWO agents: one labeled 'ping' that replies with the word PING, and one labeled 'pong' that replies with the word PONG. Launch it and end your turn; do not wait for it.","commandId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5"}},"emittedAtMs":1791762449103}} +{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a12d5d-405f-7000-89a3-5aa62af6ea23","viewCursor":"v:01a12d5d-405f-7000-89a3-5aa62af6ea23:6","sourceRange":{"stream":{"kind":"session","id":"01a12d5d-405f-7000-89a3-5aa62af6ea23"},"first":{"id":"0411a5c8-2c8a-4c9e-bb67-25fa3cef21a3","sequence":20},"last":{"id":"0411a5c8-2c8a-4c9e-bb67-25fa3cef21a3","sequence":20}},"item":{"itemId":"0c1ceda9-41f7-4c3e-990e-4c53873c7ef5","kind":"userMessage","turnId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5","revision":2,"status":"completed","recordedAt":"2026-10-11T23:47:29.107019Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nUse your workflow tool to launch a workflow with exactly TWO agents: one labeled 'ping' that replies with the word PING, and one labeled 'pong' that replies with the word PONG. Launch it and end your turn; do not wait for it.","displayText":"Use your workflow tool to launch a workflow with exactly TWO agents: one labeled 'ping' that replies with the word PING, and one labeled 'pong' that replies with the word PONG. Launch it and end your turn; do not wait for it.","commandId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5"}},"emittedAtMs":1791762449131}} {"type":"emit_inbound","label":"item/started","frame":{"jsonrpc":"2.0","method":"item/started","params":{"sessionId":"01a12d5d-405f-7000-89a3-5aa62af6ea23","viewCursor":"v:01a12d5d-405f-7000-89a3-5aa62af6ea23:7","sourceRange":{"stream":{"kind":"session","id":"01a12d5d-405f-7000-89a3-5aa62af6ea23"},"first":{"id":"95574bfe-853f-44cc-81f6-88b05d6c8c16","sequence":38},"last":{"id":"95574bfe-853f-44cc-81f6-88b05d6c8c16","sequence":38}},"item":{"itemId":"95574bfe-853f-44cc-81f6-88b05d6c8c16","kind":"reminderChild","turnId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5","revision":1,"status":"inProgress","recordedAt":"2026-10-11T23:47:29.328939Z","fallbackText":"Reminder child session","childSessionId":"b16dbb52-7f62-4a5a-84c2-f68d7ea22b18","reminderAgentId":"skill-reminder","generationId":1,"taskId":"01a12d5d-4375-7d72-b5b2-4b096d0df507"}},"emittedAtMs":1791762449336}} {"type":"emit_inbound","label":"item/started","frame":{"jsonrpc":"2.0","method":"item/started","params":{"sessionId":"01a12d5d-405f-7000-89a3-5aa62af6ea23","viewCursor":"v:01a12d5d-405f-7000-89a3-5aa62af6ea23:7.1","item":{"itemId":"3ce724bc-dacf-400e-af9a-995104717cb1","kind":"reasoning","turnId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5","revision":1,"status":"inProgress","summary":[""]}},"emittedAtMs":1791762451554}} {"type":"emit_inbound","label":"item/delta","frame":{"jsonrpc":"2.0","method":"item/delta","params":{"sessionId":"01a12d5d-405f-7000-89a3-5aa62af6ea23","viewCursor":"v:01a12d5d-405f-7000-89a3-5aa62af6ea23:7.2","itemId":"3ce724bc-dacf-400e-af9a-995104717cb1","field":"summary.0","delta":"Launching a two-agent ping-pong workflow via the workflow tool while skipping skill reading for a trivial task."},"emittedAtMs":1791762451554}} diff --git a/apps/server/src/orchestration-v2/testkit/fixtures/simple/muse_transcript.ndjson b/apps/server/src/orchestration-v2/testkit/fixtures/simple/muse_transcript.ndjson index f3c0d1c56d559..66f80fb63eaa8 100644 --- a/apps/server/src/orchestration-v2/testkit/fixtures/simple/muse_transcript.ndjson +++ b/apps/server/src/orchestration-v2/testkit/fixtures/simple/muse_transcript.ndjson @@ -7,13 +7,13 @@ {"type":"emit_inbound","label":"session/started","frame":{"jsonrpc":"2.0","method":"session/started","params":{"session":{"sessionId":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5","path":"~/.local/share/muse/sessions/2026/10/08/01a11a6a-bbb5-7000-ad0b-0c9e728406e5/session.jsonl","status":"idle","activeTurnId":null,"createdAt":"2026-10-08T07:29:25.018803Z","updatedAt":"2026-10-08T07:29:25.018807Z","workspaceRoot":"","providerId":"meta","modelId":"muse-spark-1.3-contributor","turnCount":0,"forkedFrom":null,"approvalMode":{"mode":"allowAll","source":"startup","lastCommandId":null},"createdByClient":{"name":"t3_code","version":"1"},"kind":"root"}},"emittedAtMs":1791444565334}} {"type":"emit_inbound","label":"response:session/start","frame":{"jsonrpc":"2.0","id":2,"result":{"session":{"sessionId":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5","path":"~/.local/share/muse/sessions/2026/10/08/01a11a6a-bbb5-7000-ad0b-0c9e728406e5/session.jsonl","status":"idle","activeTurnId":null,"createdAt":"2026-10-08T07:29:25.018803Z","updatedAt":"2026-10-08T07:29:25.018807Z","workspaceRoot":"","providerId":"meta","modelId":"muse-spark-1.3-contributor","turnCount":0,"forkedFrom":null,"approvalMode":{"mode":"allowAll","source":"startup","lastCommandId":null},"createdByClient":{"name":"t3_code","version":"1"},"kind":"root"},"viewCursor":"v:01a11a6a-bbb5-7000-ad0b-0c9e728406e5:1"}}} {"type":"emit_inbound","label":"session/approvalModeChanged","frame":{"jsonrpc":"2.0","method":"session/approvalModeChanged","params":{"sessionId":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5","viewCursor":"v:01a11a6a-bbb5-7000-ad0b-0c9e728406e5:1","sourceRange":{"stream":{"kind":"session","id":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5"},"first":{"id":"cfd043e8-8728-493d-ba28-252ef91e8a80","sequence":5},"last":{"id":"cfd043e8-8728-493d-ba28-252ef91e8a80","sequence":5}},"mode":"allowAll","source":"approvalReconfigure","commandId":"01a11a6a-bbb6-7000-b1a4-a01ea59676ef","clientName":"t3_code"},"emittedAtMs":1791444565340}} -{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\n"},{"type":"text","text":"Respond with the following text: fixture simple ok"}],"displayText":"Respond with the following text: fixture simple ok","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a11a6a-be41-7000-8db9-1d84c1b8be48"}}} +{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\n"},{"type":"text","text":"Respond with the following text: fixture simple ok"}],"displayText":"Respond with the following text: fixture simple ok","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a11a6a-be41-7000-8db9-1d84c1b8be48"}}} {"type":"emit_inbound","label":"session/branchChanged","frame":{"jsonrpc":"2.0","method":"session/branchChanged","params":{"sessionId":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5","viewCursor":"v:01a11a6a-bbb5-7000-ad0b-0c9e728406e5:2","sourceRange":{"stream":{"kind":"session","id":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5"},"first":{"id":"0f64f4ea-a08c-43e1-af53-cf85cee1a11c","sequence":16},"last":{"id":"0f64f4ea-a08c-43e1-af53-cf85cee1a11c","sequence":16}},"workspaceRoot":"","branch":"main","vcs":"git"},"emittedAtMs":1791444565612}} {"type":"emit_inbound","label":"response:turn/start","frame":{"jsonrpc":"2.0","id":3,"result":{"commandId":"01a11a6a-be41-7000-8db9-1d84c1b8be48","status":"accepted","turnId":"01a11a6a-be41-7000-8db9-1d84c1b8be48","startedNewTurn":true,"disposition":"started"}}} {"type":"emit_inbound","label":"session/statusChanged","frame":{"jsonrpc":"2.0","method":"session/statusChanged","params":{"sessionId":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5","status":"running","viewCursor":"v:01a11a6a-bbb5-7000-ad0b-0c9e728406e5:3"},"emittedAtMs":1791444565635}} {"type":"emit_inbound","label":"turn/started","frame":{"jsonrpc":"2.0","method":"turn/started","params":{"sessionId":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5","viewCursor":"v:01a11a6a-bbb5-7000-ad0b-0c9e728406e5:3","sourceRange":{"stream":{"kind":"session","id":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5"},"first":{"id":"1f33a521-7829-4cc3-97db-a3b5a6e7deaf","sequence":18},"last":{"id":"1f33a521-7829-4cc3-97db-a3b5a6e7deaf","sequence":18}},"turnId":"01a11a6a-be41-7000-8db9-1d84c1b8be48","commandId":"01a11a6a-be41-7000-8db9-1d84c1b8be48"},"emittedAtMs":1791444565635}} -{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5","viewCursor":"v:01a11a6a-bbb5-7000-ad0b-0c9e728406e5:4","sourceRange":{"stream":{"kind":"session","id":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5"},"first":{"id":"1f33a521-7829-4cc3-97db-a3b5a6e7deaf","sequence":18},"last":{"id":"1f33a521-7829-4cc3-97db-a3b5a6e7deaf","sequence":18}},"item":{"itemId":"23294b54-8e67-4896-a7f3-681119e1ddbe","kind":"userMessage","turnId":"01a11a6a-be41-7000-8db9-1d84c1b8be48","revision":1,"status":"completed","recordedAt":"2026-10-08T07:29:25.627042Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nRespond with the following text: fixture simple ok","commandId":"01a11a6a-be41-7000-8db9-1d84c1b8be48"}},"emittedAtMs":1791444565635}} -{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5","viewCursor":"v:01a11a6a-bbb5-7000-ad0b-0c9e728406e5:5","sourceRange":{"stream":{"kind":"session","id":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5"},"first":{"id":"8c00aa21-7809-4cb2-89f7-b499aaa8746d","sequence":20},"last":{"id":"8c00aa21-7809-4cb2-89f7-b499aaa8746d","sequence":20}},"item":{"itemId":"23294b54-8e67-4896-a7f3-681119e1ddbe","kind":"userMessage","turnId":"01a11a6a-be41-7000-8db9-1d84c1b8be48","revision":2,"status":"completed","recordedAt":"2026-10-08T07:29:25.643029Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nRespond with the following text: fixture simple ok","displayText":"Respond with the following text: fixture simple ok","commandId":"01a11a6a-be41-7000-8db9-1d84c1b8be48"}},"emittedAtMs":1791444565652}} +{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5","viewCursor":"v:01a11a6a-bbb5-7000-ad0b-0c9e728406e5:4","sourceRange":{"stream":{"kind":"session","id":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5"},"first":{"id":"1f33a521-7829-4cc3-97db-a3b5a6e7deaf","sequence":18},"last":{"id":"1f33a521-7829-4cc3-97db-a3b5a6e7deaf","sequence":18}},"item":{"itemId":"23294b54-8e67-4896-a7f3-681119e1ddbe","kind":"userMessage","turnId":"01a11a6a-be41-7000-8db9-1d84c1b8be48","revision":1,"status":"completed","recordedAt":"2026-10-08T07:29:25.627042Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nRespond with the following text: fixture simple ok","commandId":"01a11a6a-be41-7000-8db9-1d84c1b8be48"}},"emittedAtMs":1791444565635}} +{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5","viewCursor":"v:01a11a6a-bbb5-7000-ad0b-0c9e728406e5:5","sourceRange":{"stream":{"kind":"session","id":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5"},"first":{"id":"8c00aa21-7809-4cb2-89f7-b499aaa8746d","sequence":20},"last":{"id":"8c00aa21-7809-4cb2-89f7-b499aaa8746d","sequence":20}},"item":{"itemId":"23294b54-8e67-4896-a7f3-681119e1ddbe","kind":"userMessage","turnId":"01a11a6a-be41-7000-8db9-1d84c1b8be48","revision":2,"status":"completed","recordedAt":"2026-10-08T07:29:25.643029Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nRespond with the following text: fixture simple ok","displayText":"Respond with the following text: fixture simple ok","commandId":"01a11a6a-be41-7000-8db9-1d84c1b8be48"}},"emittedAtMs":1791444565652}} {"type":"emit_inbound","label":"item/started","frame":{"jsonrpc":"2.0","method":"item/started","params":{"sessionId":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5","viewCursor":"v:01a11a6a-bbb5-7000-ad0b-0c9e728406e5:6","sourceRange":{"stream":{"kind":"session","id":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5"},"first":{"id":"ed1680a5-6f99-4b0f-b9ea-d12193f0de1f","sequence":37},"last":{"id":"ed1680a5-6f99-4b0f-b9ea-d12193f0de1f","sequence":37}},"item":{"itemId":"ed1680a5-6f99-4b0f-b9ea-d12193f0de1f","kind":"reminderChild","turnId":"01a11a6a-be41-7000-8db9-1d84c1b8be48","revision":1,"status":"inProgress","recordedAt":"2026-10-08T07:29:25.781511Z","fallbackText":"Reminder child session","childSessionId":"9785bf9d-382d-4781-ab3b-3817c9e6a604","reminderAgentId":"skill-reminder","generationId":1,"taskId":"01a11a6a-bef1-7032-974a-059b8765c487"}},"emittedAtMs":1791444565789}} {"type":"emit_inbound","label":"item/started","frame":{"jsonrpc":"2.0","method":"item/started","params":{"sessionId":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5","viewCursor":"v:01a11a6a-bbb5-7000-ad0b-0c9e728406e5:6.1","item":{"itemId":"0a68e576-f554-4700-8853-a68ad2b7179b","kind":"agentMessage","turnId":"01a11a6a-be41-7000-8db9-1d84c1b8be48","revision":1,"status":"inProgress","text":""}},"emittedAtMs":1791444567707}} {"type":"emit_inbound","label":"item/delta","frame":{"jsonrpc":"2.0","method":"item/delta","params":{"sessionId":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5","viewCursor":"v:01a11a6a-bbb5-7000-ad0b-0c9e728406e5:6.2","itemId":"0a68e576-f554-4700-8853-a68ad2b7179b","field":"text","delta":"fixture simple ok"},"emittedAtMs":1791444567707}} diff --git a/apps/server/src/orchestration-v2/testkit/fixtures/subagent_v2/muse_transcript.ndjson b/apps/server/src/orchestration-v2/testkit/fixtures/subagent_v2/muse_transcript.ndjson index f15d54adf25d4..3c0fb5b150e51 100644 --- a/apps/server/src/orchestration-v2/testkit/fixtures/subagent_v2/muse_transcript.ndjson +++ b/apps/server/src/orchestration-v2/testkit/fixtures/subagent_v2/muse_transcript.ndjson @@ -7,13 +7,13 @@ {"type":"emit_inbound","label":"session/started","frame":{"jsonrpc":"2.0","method":"session/started","params":{"session":{"sessionId":"01a12d17-75fa-7000-9157-e8365ce94492","path":"~/.local/share/muse/sessions/2026/10/11/01a12d17-75fa-7000-9157-e8365ce94492/session.jsonl","status":"idle","activeTurnId":null,"createdAt":"2026-10-11T22:31:14.73121Z","updatedAt":"2026-10-11T22:31:14.731214Z","workspaceRoot":"","providerId":"meta","modelId":"muse-spark-1.3-contributor","turnCount":0,"forkedFrom":null,"sideFrom":null,"approvalMode":{"mode":"allowAll","source":"startup","lastCommandId":null},"createdByClient":{"name":"t3_code","version":"1"},"kind":"root"}},"emittedAtMs":1791757874894}} {"type":"emit_inbound","label":"response:session/start","frame":{"jsonrpc":"2.0","id":2,"result":{"session":{"sessionId":"01a12d17-75fa-7000-9157-e8365ce94492","path":"~/.local/share/muse/sessions/2026/10/11/01a12d17-75fa-7000-9157-e8365ce94492/session.jsonl","status":"idle","activeTurnId":null,"createdAt":"2026-10-11T22:31:14.73121Z","updatedAt":"2026-10-11T22:31:14.731214Z","workspaceRoot":"","providerId":"meta","modelId":"muse-spark-1.3-contributor","turnCount":0,"forkedFrom":null,"sideFrom":null,"approvalMode":{"mode":"allowAll","source":"startup","lastCommandId":null},"createdByClient":{"name":"t3_code","version":"1"},"kind":"root"},"viewCursor":"v:01a12d17-75fa-7000-9157-e8365ce94492:1"}}} {"type":"emit_inbound","label":"session/approvalModeChanged","frame":{"jsonrpc":"2.0","method":"session/approvalModeChanged","params":{"sessionId":"01a12d17-75fa-7000-9157-e8365ce94492","viewCursor":"v:01a12d17-75fa-7000-9157-e8365ce94492:1","sourceRange":{"stream":{"kind":"session","id":"01a12d17-75fa-7000-9157-e8365ce94492"},"first":{"id":"94d1e1ed-1d3e-4cd5-91bf-a51394fc8a0a","sequence":5},"last":{"id":"94d1e1ed-1d3e-4cd5-91bf-a51394fc8a0a","sequence":5}},"mode":"allowAll","source":"approvalReconfigure","commandId":"01a12d17-75fa-7001-a940-79b5d7c648a4","clientName":"t3_code"},"emittedAtMs":1791757874898}} -{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a12d17-75fa-7000-9157-e8365ce94492","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\n"},{"type":"text","text":"Spawn one subagent whose only task is to reply with exactly: Hello. Wait for it to finish, then reply with exactly what it said."}],"displayText":"Spawn one subagent whose only task is to reply with exactly: Hello. Wait for it to finish, then reply with exactly what it said.","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a12d17-7790-7000-967d-3f2246f38a60"}}} +{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a12d17-75fa-7000-9157-e8365ce94492","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\n"},{"type":"text","text":"Spawn one subagent whose only task is to reply with exactly: Hello. Wait for it to finish, then reply with exactly what it said."}],"displayText":"Spawn one subagent whose only task is to reply with exactly: Hello. Wait for it to finish, then reply with exactly what it said.","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a12d17-7790-7000-967d-3f2246f38a60"}}} {"type":"emit_inbound","label":"session/branchChanged","frame":{"jsonrpc":"2.0","method":"session/branchChanged","params":{"sessionId":"01a12d17-75fa-7000-9157-e8365ce94492","viewCursor":"v:01a12d17-75fa-7000-9157-e8365ce94492:2","sourceRange":{"stream":{"kind":"session","id":"01a12d17-75fa-7000-9157-e8365ce94492"},"first":{"id":"1dc98410-05c3-4618-a431-3638055e20df","sequence":15},"last":{"id":"1dc98410-05c3-4618-a431-3638055e20df","sequence":15}},"workspaceRoot":"","branch":"main","vcs":"git"},"emittedAtMs":1791757875120}} {"type":"emit_inbound","label":"response:turn/start","frame":{"jsonrpc":"2.0","id":3,"result":{"commandId":"01a12d17-7790-7000-967d-3f2246f38a60","status":"accepted","turnId":"01a12d17-7790-7000-967d-3f2246f38a60","startedNewTurn":true,"disposition":"started"}}} {"type":"emit_inbound","label":"session/statusChanged","frame":{"jsonrpc":"2.0","method":"session/statusChanged","params":{"sessionId":"01a12d17-75fa-7000-9157-e8365ce94492","status":"running","viewCursor":"v:01a12d17-75fa-7000-9157-e8365ce94492:3"},"emittedAtMs":1791757875131}} {"type":"emit_inbound","label":"turn/started","frame":{"jsonrpc":"2.0","method":"turn/started","params":{"sessionId":"01a12d17-75fa-7000-9157-e8365ce94492","viewCursor":"v:01a12d17-75fa-7000-9157-e8365ce94492:3","sourceRange":{"stream":{"kind":"session","id":"01a12d17-75fa-7000-9157-e8365ce94492"},"first":{"id":"766cb840-a6f1-41f2-8c7e-8c372d0470cd","sequence":17},"last":{"id":"766cb840-a6f1-41f2-8c7e-8c372d0470cd","sequence":17}},"turnId":"01a12d17-7790-7000-967d-3f2246f38a60","commandId":"01a12d17-7790-7000-967d-3f2246f38a60"},"emittedAtMs":1791757875132}} -{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a12d17-75fa-7000-9157-e8365ce94492","viewCursor":"v:01a12d17-75fa-7000-9157-e8365ce94492:4","sourceRange":{"stream":{"kind":"session","id":"01a12d17-75fa-7000-9157-e8365ce94492"},"first":{"id":"766cb840-a6f1-41f2-8c7e-8c372d0470cd","sequence":17},"last":{"id":"766cb840-a6f1-41f2-8c7e-8c372d0470cd","sequence":17}},"item":{"itemId":"95c179c8-e8f8-4629-80df-e28243a234ea","kind":"userMessage","turnId":"01a12d17-7790-7000-967d-3f2246f38a60","revision":1,"status":"completed","recordedAt":"2026-10-11T22:31:15.120499Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nSpawn one subagent whose only task is to reply with exactly: Hello. Wait for it to finish, then reply with exactly what it said.","commandId":"01a12d17-7790-7000-967d-3f2246f38a60"}},"emittedAtMs":1791757875132}} -{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a12d17-75fa-7000-9157-e8365ce94492","viewCursor":"v:01a12d17-75fa-7000-9157-e8365ce94492:5","sourceRange":{"stream":{"kind":"session","id":"01a12d17-75fa-7000-9157-e8365ce94492"},"first":{"id":"dcf03486-5149-4377-b325-dea3e118628a","sequence":19},"last":{"id":"dcf03486-5149-4377-b325-dea3e118628a","sequence":19}},"item":{"itemId":"95c179c8-e8f8-4629-80df-e28243a234ea","kind":"userMessage","turnId":"01a12d17-7790-7000-967d-3f2246f38a60","revision":2,"status":"completed","recordedAt":"2026-10-11T22:31:15.136047Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nSpawn one subagent whose only task is to reply with exactly: Hello. Wait for it to finish, then reply with exactly what it said.","displayText":"Spawn one subagent whose only task is to reply with exactly: Hello. Wait for it to finish, then reply with exactly what it said.","commandId":"01a12d17-7790-7000-967d-3f2246f38a60"}},"emittedAtMs":1791757875144}} +{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a12d17-75fa-7000-9157-e8365ce94492","viewCursor":"v:01a12d17-75fa-7000-9157-e8365ce94492:4","sourceRange":{"stream":{"kind":"session","id":"01a12d17-75fa-7000-9157-e8365ce94492"},"first":{"id":"766cb840-a6f1-41f2-8c7e-8c372d0470cd","sequence":17},"last":{"id":"766cb840-a6f1-41f2-8c7e-8c372d0470cd","sequence":17}},"item":{"itemId":"95c179c8-e8f8-4629-80df-e28243a234ea","kind":"userMessage","turnId":"01a12d17-7790-7000-967d-3f2246f38a60","revision":1,"status":"completed","recordedAt":"2026-10-11T22:31:15.120499Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nSpawn one subagent whose only task is to reply with exactly: Hello. Wait for it to finish, then reply with exactly what it said.","commandId":"01a12d17-7790-7000-967d-3f2246f38a60"}},"emittedAtMs":1791757875132}} +{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a12d17-75fa-7000-9157-e8365ce94492","viewCursor":"v:01a12d17-75fa-7000-9157-e8365ce94492:5","sourceRange":{"stream":{"kind":"session","id":"01a12d17-75fa-7000-9157-e8365ce94492"},"first":{"id":"dcf03486-5149-4377-b325-dea3e118628a","sequence":19},"last":{"id":"dcf03486-5149-4377-b325-dea3e118628a","sequence":19}},"item":{"itemId":"95c179c8-e8f8-4629-80df-e28243a234ea","kind":"userMessage","turnId":"01a12d17-7790-7000-967d-3f2246f38a60","revision":2,"status":"completed","recordedAt":"2026-10-11T22:31:15.136047Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nSpawn one subagent whose only task is to reply with exactly: Hello. Wait for it to finish, then reply with exactly what it said.","displayText":"Spawn one subagent whose only task is to reply with exactly: Hello. Wait for it to finish, then reply with exactly what it said.","commandId":"01a12d17-7790-7000-967d-3f2246f38a60"}},"emittedAtMs":1791757875144}} {"type":"emit_inbound","label":"item/started","frame":{"jsonrpc":"2.0","method":"item/started","params":{"sessionId":"01a12d17-75fa-7000-9157-e8365ce94492","viewCursor":"v:01a12d17-75fa-7000-9157-e8365ce94492:6","sourceRange":{"stream":{"kind":"session","id":"01a12d17-75fa-7000-9157-e8365ce94492"},"first":{"id":"599a220a-d212-4a81-a60d-7284ff290284","sequence":36},"last":{"id":"599a220a-d212-4a81-a60d-7284ff290284","sequence":36}},"item":{"itemId":"599a220a-d212-4a81-a60d-7284ff290284","kind":"reminderChild","turnId":"01a12d17-7790-7000-967d-3f2246f38a60","revision":1,"status":"inProgress","recordedAt":"2026-10-11T22:31:15.249949Z","fallbackText":"Reminder child session","childSessionId":"0b3f4f01-850a-43bd-af36-9b011699354f","reminderAgentId":"skill-reminder","generationId":1,"taskId":"01a12d17-7819-7212-8057-3558c2bdd46d"}},"emittedAtMs":1791757875258}} {"type":"emit_inbound","label":"session/tokenUsage","frame":{"jsonrpc":"2.0","method":"session/tokenUsage","params":{"sessionId":"01a12d17-75fa-7000-9157-e8365ce94492","viewCursor":"v:01a12d17-75fa-7000-9157-e8365ce94492:7","sourceRange":{"stream":{"kind":"session","id":"01a12d17-75fa-7000-9157-e8365ce94492"},"first":{"id":"9198dd8d-eede-487a-9109-a48d2563c371","sequence":51},"last":{"id":"9198dd8d-eede-487a-9109-a48d2563c371","sequence":51}},"turnId":"01a12d17-7790-7000-967d-3f2246f38a60","modelId":"muse-spark-1.3-contributor","usage":{"inputTokens":32900,"outputTokens":129,"cachedTokens":0,"cacheWriteTokens":0,"cacheReadTokens":0,"reasoningTokens":36},"promptTokens":32900,"totalTokens":33029,"finishReason":"tool_calls","durationMs":2354,"cumulative":{"promptTokens":32900,"outputTokens":129,"totalTokens":33029,"cost":{"usd":0.0033158000000000003,"partial":false}}},"emittedAtMs":1791757877764}} {"type":"emit_inbound","label":"session/contextUsage","frame":{"jsonrpc":"2.0","method":"session/contextUsage","params":{"sessionId":"01a12d17-75fa-7000-9157-e8365ce94492","viewCursor":"v:01a12d17-75fa-7000-9157-e8365ce94492:8","sourceRange":{"stream":{"kind":"session","id":"01a12d17-75fa-7000-9157-e8365ce94492"},"first":{"id":"9198dd8d-eede-487a-9109-a48d2563c371","sequence":51},"last":{"id":"9198dd8d-eede-487a-9109-a48d2563c371","sequence":51}},"windowTokens":1007997,"usedTokens":33029,"pressure":"normal"},"emittedAtMs":1791757877764}} diff --git a/apps/server/src/orchestration-v2/testkit/fixtures/turn_interrupt/muse_transcript.ndjson b/apps/server/src/orchestration-v2/testkit/fixtures/turn_interrupt/muse_transcript.ndjson index 639e35584e22f..8edacb55950be 100644 --- a/apps/server/src/orchestration-v2/testkit/fixtures/turn_interrupt/muse_transcript.ndjson +++ b/apps/server/src/orchestration-v2/testkit/fixtures/turn_interrupt/muse_transcript.ndjson @@ -7,14 +7,14 @@ {"type":"emit_inbound","label":"session/started","frame":{"jsonrpc":"2.0","method":"session/started","params":{"session":{"sessionId":"01a11a6d-2d3d-7000-8178-411fdb90bc34","path":"~/.local/share/muse/sessions/2026/10/08/01a11a6d-2d3d-7000-8178-411fdb90bc34/session.jsonl","status":"idle","activeTurnId":null,"createdAt":"2026-10-08T07:32:05.094285Z","updatedAt":"2026-10-08T07:32:05.094289Z","workspaceRoot":"","providerId":"meta","modelId":"muse-spark-1.3-contributor","turnCount":0,"forkedFrom":null,"approvalMode":{"mode":"allowAll","source":"startup","lastCommandId":null},"createdByClient":{"name":"t3_code","version":"1"},"kind":"root"}},"emittedAtMs":1791444725265}} {"type":"emit_inbound","label":"response:session/start","frame":{"jsonrpc":"2.0","id":2,"result":{"session":{"sessionId":"01a11a6d-2d3d-7000-8178-411fdb90bc34","path":"~/.local/share/muse/sessions/2026/10/08/01a11a6d-2d3d-7000-8178-411fdb90bc34/session.jsonl","status":"idle","activeTurnId":null,"createdAt":"2026-10-08T07:32:05.094285Z","updatedAt":"2026-10-08T07:32:05.094289Z","workspaceRoot":"","providerId":"meta","modelId":"muse-spark-1.3-contributor","turnCount":0,"forkedFrom":null,"approvalMode":{"mode":"allowAll","source":"startup","lastCommandId":null},"createdByClient":{"name":"t3_code","version":"1"},"kind":"root"},"viewCursor":"v:01a11a6d-2d3d-7000-8178-411fdb90bc34:1"}}} {"type":"emit_inbound","label":"session/approvalModeChanged","frame":{"jsonrpc":"2.0","method":"session/approvalModeChanged","params":{"sessionId":"01a11a6d-2d3d-7000-8178-411fdb90bc34","viewCursor":"v:01a11a6d-2d3d-7000-8178-411fdb90bc34:1","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-2d3d-7000-8178-411fdb90bc34"},"first":{"id":"9865268e-0a39-43a0-89d0-251a820ec8a3","sequence":5},"last":{"id":"9865268e-0a39-43a0-89d0-251a820ec8a3","sequence":5}},"mode":"allowAll","source":"approvalReconfigure","commandId":"01a11a6d-2d3e-7000-b0e7-f04c53ee070b","clientName":"t3_code"},"emittedAtMs":1791444725268}} -{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a11a6d-2d3d-7000-8178-411fdb90bc34","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\n"},{"type":"text","text":"Do not answer immediately. First run the local shell command `sleep 30`, then respond with exactly: interrupt fixture should not finish naturally."}],"displayText":"Do not answer immediately. First run the local shell command `sleep 30`, then respond with exactly: interrupt fixture should not finish naturally.","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5"}}} +{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a11a6d-2d3d-7000-8178-411fdb90bc34","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\n"},{"type":"text","text":"Do not answer immediately. First run the local shell command `sleep 30`, then respond with exactly: interrupt fixture should not finish naturally."}],"displayText":"Do not answer immediately. First run the local shell command `sleep 30`, then respond with exactly: interrupt fixture should not finish naturally.","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5"}}} {"type":"emit_inbound","label":"session/branchChanged","frame":{"jsonrpc":"2.0","method":"session/branchChanged","params":{"sessionId":"01a11a6d-2d3d-7000-8178-411fdb90bc34","viewCursor":"v:01a11a6d-2d3d-7000-8178-411fdb90bc34:2","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-2d3d-7000-8178-411fdb90bc34"},"first":{"id":"531b0e9e-c96d-4fa5-a6dd-5cc7d4569543","sequence":16},"last":{"id":"531b0e9e-c96d-4fa5-a6dd-5cc7d4569543","sequence":16}},"workspaceRoot":"","branch":"main","vcs":"git"},"emittedAtMs":1791444725509}} {"type":"emit_inbound","label":"response:turn/start","frame":{"jsonrpc":"2.0","id":3,"result":{"commandId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5","status":"accepted","turnId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5","startedNewTurn":true,"disposition":"started"}}} {"type":"expect_outbound","label":"turn/interrupt","frame":{"jsonrpc":"2.0","id":4,"method":"turn/interrupt","params":{"sessionId":"01a11a6d-2d3d-7000-8178-411fdb90bc34","turnId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5","commandId":"01a11a6d-2f0b-7000-a1d4-359cf54e471a"}}} {"type":"emit_inbound","label":"session/statusChanged","frame":{"jsonrpc":"2.0","method":"session/statusChanged","params":{"sessionId":"01a11a6d-2d3d-7000-8178-411fdb90bc34","status":"running","viewCursor":"v:01a11a6d-2d3d-7000-8178-411fdb90bc34:3"},"emittedAtMs":1791444725522}} {"type":"emit_inbound","label":"turn/started","frame":{"jsonrpc":"2.0","method":"turn/started","params":{"sessionId":"01a11a6d-2d3d-7000-8178-411fdb90bc34","viewCursor":"v:01a11a6d-2d3d-7000-8178-411fdb90bc34:3","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-2d3d-7000-8178-411fdb90bc34"},"first":{"id":"50d1fd8c-232b-47b9-9bfa-9b543961bbc9","sequence":18},"last":{"id":"50d1fd8c-232b-47b9-9bfa-9b543961bbc9","sequence":18}},"turnId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5","commandId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5"},"emittedAtMs":1791444725523}} -{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a11a6d-2d3d-7000-8178-411fdb90bc34","viewCursor":"v:01a11a6d-2d3d-7000-8178-411fdb90bc34:4","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-2d3d-7000-8178-411fdb90bc34"},"first":{"id":"50d1fd8c-232b-47b9-9bfa-9b543961bbc9","sequence":18},"last":{"id":"50d1fd8c-232b-47b9-9bfa-9b543961bbc9","sequence":18}},"item":{"itemId":"cfa989ad-2359-4da2-9fca-562855cc0ae8","kind":"userMessage","turnId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5","revision":1,"status":"completed","recordedAt":"2026-10-08T07:32:05.510508Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nDo not answer immediately. First run the local shell command `sleep 30`, then respond with exactly: interrupt fixture should not finish naturally.","commandId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5"}},"emittedAtMs":1791444725523}} -{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a11a6d-2d3d-7000-8178-411fdb90bc34","viewCursor":"v:01a11a6d-2d3d-7000-8178-411fdb90bc34:5","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-2d3d-7000-8178-411fdb90bc34"},"first":{"id":"5db75d92-0e52-4bce-bb81-40e254b40bc3","sequence":20},"last":{"id":"5db75d92-0e52-4bce-bb81-40e254b40bc3","sequence":20}},"item":{"itemId":"cfa989ad-2359-4da2-9fca-562855cc0ae8","kind":"userMessage","turnId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5","revision":2,"status":"completed","recordedAt":"2026-10-08T07:32:05.530992Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nDo not answer immediately. First run the local shell command `sleep 30`, then respond with exactly: interrupt fixture should not finish naturally.","displayText":"Do not answer immediately. First run the local shell command `sleep 30`, then respond with exactly: interrupt fixture should not finish naturally.","commandId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5"}},"emittedAtMs":1791444725539}} +{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a11a6d-2d3d-7000-8178-411fdb90bc34","viewCursor":"v:01a11a6d-2d3d-7000-8178-411fdb90bc34:4","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-2d3d-7000-8178-411fdb90bc34"},"first":{"id":"50d1fd8c-232b-47b9-9bfa-9b543961bbc9","sequence":18},"last":{"id":"50d1fd8c-232b-47b9-9bfa-9b543961bbc9","sequence":18}},"item":{"itemId":"cfa989ad-2359-4da2-9fca-562855cc0ae8","kind":"userMessage","turnId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5","revision":1,"status":"completed","recordedAt":"2026-10-08T07:32:05.510508Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nDo not answer immediately. First run the local shell command `sleep 30`, then respond with exactly: interrupt fixture should not finish naturally.","commandId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5"}},"emittedAtMs":1791444725523}} +{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a11a6d-2d3d-7000-8178-411fdb90bc34","viewCursor":"v:01a11a6d-2d3d-7000-8178-411fdb90bc34:5","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-2d3d-7000-8178-411fdb90bc34"},"first":{"id":"5db75d92-0e52-4bce-bb81-40e254b40bc3","sequence":20},"last":{"id":"5db75d92-0e52-4bce-bb81-40e254b40bc3","sequence":20}},"item":{"itemId":"cfa989ad-2359-4da2-9fca-562855cc0ae8","kind":"userMessage","turnId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5","revision":2,"status":"completed","recordedAt":"2026-10-08T07:32:05.530992Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nDo not answer immediately. First run the local shell command `sleep 30`, then respond with exactly: interrupt fixture should not finish naturally.","displayText":"Do not answer immediately. First run the local shell command `sleep 30`, then respond with exactly: interrupt fixture should not finish naturally.","commandId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5"}},"emittedAtMs":1791444725539}} {"type":"emit_inbound","label":"item/started","frame":{"jsonrpc":"2.0","method":"item/started","params":{"sessionId":"01a11a6d-2d3d-7000-8178-411fdb90bc34","viewCursor":"v:01a11a6d-2d3d-7000-8178-411fdb90bc34:6","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-2d3d-7000-8178-411fdb90bc34"},"first":{"id":"ae604001-22b9-479c-a45b-3dcfed45996c","sequence":37},"last":{"id":"ae604001-22b9-479c-a45b-3dcfed45996c","sequence":37}},"item":{"itemId":"ae604001-22b9-479c-a45b-3dcfed45996c","kind":"reminderChild","turnId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5","revision":1,"status":"inProgress","recordedAt":"2026-10-08T07:32:05.638142Z","fallbackText":"Reminder child session","childSessionId":"1f58196a-6bf6-42da-9499-8f53a439e6e1","reminderAgentId":"skill-reminder","generationId":1,"taskId":"01a11a6d-2f65-70b1-bb8f-69b33bb4fc62"}},"emittedAtMs":1791444725646}} {"type":"emit_inbound","label":"response:turn/interrupt","frame":{"jsonrpc":"2.0","id":4,"result":{"commandId":"01a11a6d-2f0b-7000-a1d4-359cf54e471a","status":"accepted","turnId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5"}}} {"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a11a6d-2d3d-7000-8178-411fdb90bc34","viewCursor":"v:01a11a6d-2d3d-7000-8178-411fdb90bc34:7","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-2d3d-7000-8178-411fdb90bc34"},"first":{"id":"ae604001-22b9-479c-a45b-3dcfed45996c","sequence":37},"last":{"id":"3962d8d1-4608-4dda-8fdc-39a1804474aa","sequence":50}},"item":{"itemId":"ae604001-22b9-479c-a45b-3dcfed45996c","kind":"reminderChild","turnId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5","revision":2,"status":"cancelled","recordedAt":"2026-10-08T07:32:05.727236Z","fallbackText":"Reminder child session","failureReason":"main run cancelled","childSessionId":"1f58196a-6bf6-42da-9499-8f53a439e6e1","reminderAgentId":"skill-reminder","generationId":1,"taskId":"01a11a6d-2f65-70b1-bb8f-69b33bb4fc62"}},"emittedAtMs":1791444725734}} diff --git a/apps/server/src/provider/agentContextBudget.json b/apps/server/src/provider/agentContextBudget.json index 185a8629ae5ab..7422f44e91cc6 100644 --- a/apps/server/src/provider/agentContextBudget.json +++ b/apps/server/src/provider/agentContextBudget.json @@ -1,5 +1,5 @@ { - "claudeSystemAppendBytes": 5762, - "codexAdditionalContextBytes": 7492, - "mcpToolListJsonBytes": 52026 + "claudeSystemAppendBytes": 5805, + "codexAdditionalContextBytes": 7535, + "mcpToolListJsonBytes": 52769 } diff --git a/apps/server/src/pullRequest/PullRequestService.test.ts b/apps/server/src/pullRequest/PullRequestService.test.ts index 103215fc8a06c..67dc48ac38e2e 100644 --- a/apps/server/src/pullRequest/PullRequestService.test.ts +++ b/apps/server/src/pullRequest/PullRequestService.test.ts @@ -1431,6 +1431,123 @@ it.effect( }), ); +it.effect( + "reads logs of the failing checks, or of the one named, and names the checks it lacks", + () => + Effect.gen(function* () { + const asked: Array> = []; + let failing = 0; + const check = (name: string, status: "success" | "failure" | "cancelled") => ({ + name, + status, + description: null, + url: `https://github.com/acme/web/runs/${name.length}`, + }); + const service = yield* makeService({ + projects: [ + project({ id: "p1", title: "web", workspaceRoot: "/a", repository: "acme/web" }), + ], + providers: [ + fakePullRequestProvider(SourceControlProviderKind.make("github"), { + getChangeRequestChecks: () => + Effect.succeed({ + state: "open", + checks: [ + check("Lint", "success"), + check("Test", "failure"), + check("E2E", "cancelled"), + ...Array.from({ length: failing }, (_, index) => + check(`Shard ${index}`, "failure"), + ), + ], + }), + getCheckLogs: (input) => + Effect.sync(() => { + asked.push(input.checks.map((entry) => entry.name)); + return input.checks.map((entry) => ({ + ...entry, + log: "boom", + truncated: false, + detail: null, + })); + }), + }), + ], + }); + const reference = { projectId: "p1" as ProjectId, repository: "acme/web", number: 1 }; + yield* service.checkLogs({ ...reference, tailLines: 50 }); + yield* service.checkLogs({ ...reference, check: "lint", tailLines: 50 }); + const missing = yield* Effect.flip( + service.checkLogs({ ...reference, check: "Deploy", tailLines: 50 }), + ); + assert.deepStrictEqual(asked, [["Test", "E2E"], ["Lint"]]); + assert.include(missing.message, "Lint, Test, E2E"); + + failing = 6; + asked.length = 0; + const many = yield* service.checkLogs({ ...reference, tailLines: 50 }); + // The host is asked for five; the other three come back unread, by name. + assert.lengthOf(asked[0] ?? [], 5); + assert.deepStrictEqual( + many.map((log) => [log.name, log.log]), + [ + ["Test", "boom"], + ["E2E", "boom"], + ["Shard 0", "boom"], + ["Shard 1", "boom"], + ["Shard 2", "boom"], + ["Shard 3", null], + ["Shard 4", null], + ["Shard 5", null], + ], + ); + assert.include(many.at(-1)?.detail ?? "", "Ask for this check by name"); + }), +); + +it.effect.each([false, true])( + "re-reads checks after a rerun instead of serving the failed ones (rerun fails: %s)", + (failAfterRerun) => + Effect.gen(function* () { + let rerun = false; + const service = yield* makeService({ + projects: [ + project({ id: "p1", title: "web", workspaceRoot: "/a", repository: "acme/web" }), + ], + providers: [ + fakePullRequestProvider(SourceControlProviderKind.make("github"), { + getChangeRequestChecks: () => + Effect.succeed({ + state: "open" as const, + checks: [ + { + name: "Test", + status: rerun ? ("pending" as const) : ("failure" as const), + description: null, + url: null, + }, + ], + }), + // A failure can come after one run already restarted. + rerunChecks: (input) => + Effect.suspend(() => { + rerun = true; + return failAfterRerun + ? Effect.fail(requestFailed) + : Effect.succeed( + input.checks.map((check) => ({ ...check, rerun: true, detail: null })), + ); + }), + }), + ], + }); + const reference = { projectId: "p1" as ProjectId, repository: "acme/web", number: 1 }; + assert.equal((yield* service.checks(reference))?.checks[0]?.status, "failure"); + yield* service.rerunChecks(reference).pipe(Effect.ignore); + assert.equal((yield* service.checks(reference))?.checks[0]?.status, "pending"); + }), +); + it.effect("refuses a merge whose head moved past expectedHeadSha and pins the host merge", () => Effect.gen(function* () { const pinned: Array = []; diff --git a/apps/server/src/pullRequest/PullRequestService.ts b/apps/server/src/pullRequest/PullRequestService.ts index 8250fac48de2d..7487006a7a540 100644 --- a/apps/server/src/pullRequest/PullRequestService.ts +++ b/apps/server/src/pullRequest/PullRequestService.ts @@ -89,6 +89,8 @@ import * as SourceControlRateLimit from "@t3tools/source-control-core/server/Sou import { type ProviderChangeRequest, type ProviderChangeRequestDetail, + type ProviderCheckLog, + type ProviderCheckRerun, type ProviderDiffFilePaths, type ProviderFilterOptionPage, type ProviderListCursor, @@ -270,6 +272,14 @@ export class PullRequestService extends Context.Service< readonly runAction: ( input: PullRequestActionInput, ) => Effect.Effect; + /** Tails of the failing checks' logs, or only the named check's. */ + readonly checkLogs: ( + input: PullRequestRef & { readonly check?: string; readonly tailLines: number }, + ) => Effect.Effect, PullRequestError>; + /** Re-runs the failing checks, or only the named check. */ + readonly rerunChecks: ( + input: PullRequestRef & { readonly check?: string }, + ) => Effect.Effect, PullRequestError>; readonly update: (input: PullRequestUpdateInput) => Effect.Effect; readonly comment: (input: PullRequestCommentInput) => Effect.Effect; readonly updateComment: ( @@ -621,6 +631,12 @@ function withRateLimitBackoff( ? {} : { getFileRevisions: wrap("getFileRevisions", api.getFileRevisions) }), runAction: interactive("runAction", api.runAction), + ...(api.getCheckLogs === undefined + ? {} + : { getCheckLogs: interactive("getCheckLogs", api.getCheckLogs) }), + ...(api.rerunChecks === undefined + ? {} + : { rerunChecks: interactive("rerunChecks", api.rerunChecks) }), ...(api.updateChangeRequest === undefined ? {} : { @@ -3743,6 +3759,111 @@ export const make = Effect.gen(function* () { ); yield* SubscriptionRef.set(pullRequestRefreshes, listingsEpoch); }); + const selectChecks = Effect.fn("PullRequestService.selectChecks")(function* ( + project: SupportedProject, + input: PullRequestRef & { readonly check?: string }, + operation: string, + ) { + const ref = { + cwd: project.project.workspaceRoot, + repository: project.repository, + host: project.host, + number: input.number, + }; + const { checks } = yield* ( + project.api.getChangeRequestChecks === undefined + ? project.api.getChangeRequest(ref) + : project.api.getChangeRequestChecks(ref) + ).pipe(Effect.mapError(toPullRequestError(operation))); + const named = input.check?.trim().toLowerCase(); + if (named === undefined) { + return checks + .filter((check) => check.status === "failure" || check.status === "cancelled") + .map(({ name, url }) => ({ name, url })); + } + const matches = checks.filter((check) => check.name.toLowerCase() === named); + if (matches.length === 0) { + return yield* new PullRequestOperationError({ + operation, + detail: `No check is named ${input.check}. Checks: ${checks.map((check) => check.name).join(", ") || "none"}.`, + }); + } + return matches.map(({ name, url }) => ({ name, url })); + }); + + const DEFAULT_CHECK_LOGS = 5; + const checkLogs: PullRequestService["Service"]["checkLogs"] = Effect.fn( + "PullRequestService.checkLogs", + )(function* (input) { + const project = yield* requireProject(input); + const read = project.api.getCheckLogs; + if (read === undefined) { + return yield* new PullRequestOperationError({ + operation: "checkLogs", + detail: "This host cannot read check logs.", + }); + } + const checks = yield* selectChecks(project, input, "checkLogs"); + if (checks.length === 0) return []; + // Many failing checks would flood the agent's context; the rest stay a name away. + const [shown, rest] = + input.check === undefined + ? [checks.slice(0, DEFAULT_CHECK_LOGS), checks.slice(DEFAULT_CHECK_LOGS)] + : [checks, []]; + const logs = yield* read({ + cwd: project.project.workspaceRoot, + repository: project.repository, + host: project.host, + checks: shown, + tailLines: input.tailLines, + }).pipe(Effect.mapError(toPullRequestError("checkLogs"))); + return [ + ...logs, + ...rest.map((check) => ({ + ...check, + log: null, + truncated: false, + detail: "Not read, to keep the reply short. Ask for this check by name.", + })), + ]; + }); + + const rerunChecks: PullRequestService["Service"]["rerunChecks"] = Effect.fn( + "PullRequestService.rerunChecks", + )(function* (input) { + const project = yield* requireProject(input); + const rerun = project.api.rerunChecks; + if (rerun === undefined) { + return yield* new PullRequestOperationError({ + operation: "rerunChecks", + detail: "This host cannot re-run checks.", + }); + } + const checks = yield* selectChecks(project, input, "rerunChecks"); + if (checks.length === 0) return []; + const ref = yield* canonicalRef(input); + // A failure can follow a run that already restarted, so readers refresh either way. + return yield* rerun({ + cwd: project.project.workspaceRoot, + repository: project.repository, + host: project.host, + checks, + }).pipe( + Effect.mapError(toPullRequestError("rerunChecks")), + Effect.ensuring( + readCache.invalidate(refScope(ref)).pipe( + Effect.andThen( + Effect.suspend(() => { + bumpRefEpoch(ref); + listingsEpoch = ++epochCounter; + return SubscriptionRef.set(pullRequestRefreshes, listingsEpoch); + }), + ), + ), + ), + ); + }); + const runActionAndInvalidate: PullRequestService["Service"]["runAction"] = Effect.fn( "PullRequestService.runActionAndInvalidate", )(function* (input) { @@ -3942,6 +4063,8 @@ export const make = Effect.gen(function* () { Cache.get(changeBreakdownCache, refCacheKey(input)), ), runAction: runActionAndInvalidate, + checkLogs, + rerunChecks, update: invalidatedByMutation(update), comment: invalidatedByMutation(comment), updateComment: invalidatedByMutation(updateComment), diff --git a/evals/mcp-tool-use/cases/pull-requests.ndjson b/evals/mcp-tool-use/cases/pull-requests.ndjson index ffe49278a9c17..14c0a4e5b880b 100644 --- a/evals/mcp-tool-use/cases/pull-requests.ndjson +++ b/evals/mcp-tool-use/cases/pull-requests.ndjson @@ -51,3 +51,6 @@ {"id": "pr-labels-reviewers-through-gh", "area": "pull-requests", "kind": "bad", "pattern": "labels and reviewers set with gh pr edit", "experience": "Before pull_request_update took labels and reviewers, agents needed gh pr edit or cafe pr edit for them.", "rule": "pull_request_update replaces gh and cafe for labels and reviewers", "privacy": "commit-safe", "setup": {"role": "top", "mocks": {"pull_request_update": {"host": "github.com", "repository": "acme/app", "number": 124, "url": "https://github.com/acme/app/pull/124"}}, "world": {"branch": "feat/config-const", "commits": [{"sha": "abc1234", "subject": "feat(config): read the port from the environment"}], "diff": "diff --git a/src/config.ts b/src/config.ts\n--- a/src/config.ts\n+++ b/src/config.ts\n@@ -1,3 +1,3 @@\n-let port = 3000;\n+let port = Number(process.env.PORT ?? 3000);\n export default { port };\n", "files": {"src/config.ts": "let port = Number(process.env.PORT ?? 3000);\nexport default { port };\n"}, "prs": [{"number": 124, "title": "feat(config): read the port from the environment", "headRefName": "feat/config-const", "checks": [{"name": "Lint", "state": "pass"}, {"name": "Test", "state": "pass"}]}]}}, "prompt": "Label PR #124 bug and ask octo to review it.", "expect": {"calls": [{"tool": "pull_request_update", "args": {"number": 124, "addLabels": ["bug"], "requestReviewers": ["~octo"]}}], "shellMustNot": ["\\bgh (pr edit|api)\\b", "\\bcafe pr edit\\b"]}, "notes": "Forge usage audit of agent logs on three machines (2026-10-28): agents ran gh and cafe for PR work about 18,000 times in 45 days, which spends the user's own API quota instead of T3's sign-in."} {"id": "pr-close-through-gh", "area": "pull-requests", "kind": "bad", "pattern": "PR closed with gh pr close", "experience": "Before pull_request_update took state, closing a PR needed gh pr close or cafe pr close.", "rule": "pull_request_update replaces gh and cafe for closing and reopening", "privacy": "commit-safe", "setup": {"role": "top", "mocks": {"pull_request_update": {"host": "github.com", "repository": "acme/app", "number": 124, "url": "https://github.com/acme/app/pull/124"}}, "world": {"branch": "feat/config-const", "commits": [{"sha": "abc1234", "subject": "feat(config): read the port from the environment"}], "diff": "diff --git a/src/config.ts b/src/config.ts\n--- a/src/config.ts\n+++ b/src/config.ts\n@@ -1,3 +1,3 @@\n-let port = 3000;\n+let port = Number(process.env.PORT ?? 3000);\n export default { port };\n", "files": {"src/config.ts": "let port = Number(process.env.PORT ?? 3000);\nexport default { port };\n"}, "prs": [{"number": 124, "title": "feat(config): read the port from the environment", "headRefName": "feat/config-const", "checks": [{"name": "Lint", "state": "pass"}, {"name": "Test", "state": "pass"}]}]}}, "prompt": "Close PR #124, we're going a different way.", "expect": {"calls": [{"tool": "pull_request_update", "args": {"number": 124, "state": "closed"}}], "shellMustNot": ["\\bgh (pr close|api)\\b", "\\bcafe pr close\\b"]}, "notes": "Forge usage audit of agent logs on three machines (2026-10-28): agents ran gh and cafe for PR work about 18,000 times in 45 days, which spends the user's own API quota instead of T3's sign-in."} {"id": "no-forge-open-pr", "area": "pull-requests", "kind": "guard", "pattern": "PR asked for in a project with no forge", "experience": "T3 hides its PR tools where the project is on no forge T3 reads, while the always-loaded PR instructions still point PR work at them. Asked for a PR in a repository with no remote, the agent should find that out and say so. Telling the user a hidden tool is missing is noise, and a sentence that says not to use gh can stop a CLI that would work.", "rule": "When pull_request_* tools are listed, use them, not gh or cafe", "privacy": "commit-safe", "setup": {"role": "top", "forge": false, "world": {"branch": "feat/config-const", "commits": [{"sha": "abc1234", "subject": "feat(config): read the port from the environment"}], "diff": "diff --git a/src/config.ts b/src/config.ts\n--- a/src/config.ts\n+++ b/src/config.ts\n@@ -1,3 +1,3 @@\n-let port = 3000;\n+let port = Number(process.env.PORT ?? 3000);\n export default { port };\n", "files": {"src/config.ts": "let port = Number(process.env.PORT ?? 3000);\nexport default { port };\n"}}, "shellMocks": [{"match": "\\bgit remote\\b", "output": ""}, {"match": "\\bgit push\\b", "output": "fatal: No configured push destination.\nEither specify the URL from the command-line or configure a remote repository using\n\n git remote add \n\nand then push using the remote name\n\n git push \n"}, {"match": "\\b(gh|cafe|glab)\\b", "output": "none of the git remotes configured for this repository point to a known host"}]}, "prompt": "Open a PR for this branch.", "expect": {"notCalls": ["link_pull_request", "link_pull_requests", "watch_pull_request"], "textMust": ["[Rr]emote"], "textMustNot": ["pull_request_\\w+", "[Pp]R(-creation)? tool"]}, "notes": "Added with the no-forge gate on the forge tools (2026-11-12)."} +{"id": "ci-failure-read-through-gh", "area": "pull-requests", "kind": "bad", "pattern": "failing CI log read with gh run view", "experience": "Agents fixing a red PR read the failure with gh run view --log-failed or GET actions/jobs/:id/logs, on the user's personal quota, and often paged whole logs.", "rule": "ci_logs reads the end of a PR's failing check logs", "privacy": "commit-safe", "setup": {"role": "top", "world": {"branch": "fix/retry-queue", "commits": [{"sha": "9f3c2a1", "subject": "fix(queue): retry a failed job once"}], "diff": "diff --git a/src/queue.ts b/src/queue.ts\n--- a/src/queue.ts\n+++ b/src/queue.ts\n@@ -4,3 +4,4 @@ export async function run(job: Job) {\n try {\n return await job.execute();\n- } catch {\n+ } catch (error) {\n+ if (job.attempts > 1) throw error;\n return run({ ...job, attempts: job.attempts + 1 });\n", "files": {"src/queue.ts": "export async function run(job: Job) {\n try {\n return await job.execute();\n } catch (error) {\n if (job.attempts > 1) throw error;\n return run({ ...job, attempts: job.attempts + 1 });\n }\n}\n"}, "prs": [{"number": 124, "title": "fix(queue): retry a failed job once", "headRefName": "fix/retry-queue", "checks": [{"name": "Lint", "state": "pass"}, {"name": "Test", "state": "fail", "log": "FAIL src/queue.test.ts > gives up after one retry\nAssertionError: expected 2 calls, got 3\n at src/queue.test.ts:18:5"}]}]}, "shellMocks": [{"match": "git push", "output": "To github.com:acme/app.git\n 9f3c2a1..4d5e6f7 fix/retry-queue -> fix/retry-queue"}]}, "prompt": "CI is red on #124. Find out why and fix it.", "expect": {"calls": [{"tool": "ci_logs"}], "shellMustNot": ["\\bgh run (view|list)\\b", "\\bgh api\\b[^\\n]*actions", "\\bgh pr checks\\b", "\\bcafe ci\\b"]}, "notes": "Forge usage audit of agent logs on three machines (2026-10-28): agents read failing CI with gh run view --log-failed or the job logs API about 500 times in 45 days, and re-ran CI with gh run rerun 92 times.", "maxSteps": 12} +{"id": "ci-flake-rerun-through-gh", "area": "pull-requests", "kind": "bad", "pattern": "CI re-run with gh run rerun", "experience": "Asked to re-run a flaky check, agents looked up run ids with gh run list and ran gh run rerun --failed.", "rule": "ci_rerun re-runs a PR's failing checks", "privacy": "commit-safe", "setup": {"role": "top", "world": {"branch": "fix/retry-queue", "commits": [{"sha": "9f3c2a1", "subject": "fix(queue): retry a failed job once"}], "diff": "diff --git a/src/queue.ts b/src/queue.ts\n--- a/src/queue.ts\n+++ b/src/queue.ts\n@@ -4,3 +4,4 @@ export async function run(job: Job) {\n try {\n return await job.execute();\n- } catch {\n+ } catch (error) {\n+ if (job.attempts > 1) throw error;\n return run({ ...job, attempts: job.attempts + 1 });\n", "files": {"src/queue.ts": "export async function run(job: Job) {\n try {\n return await job.execute();\n } catch (error) {\n if (job.attempts > 1) throw error;\n return run({ ...job, attempts: job.attempts + 1 });\n }\n}\n"}, "prs": [{"number": 124, "title": "fix(queue): retry a failed job once", "headRefName": "fix/retry-queue", "checks": [{"name": "Lint", "state": "pass"}, {"name": "Test", "state": "fail", "log": "FAIL src/queue.test.ts > gives up after one retry\nAssertionError: expected 2 calls, got 3\n at src/queue.test.ts:18:5"}]}]}}, "prompt": "The Test failure on #124 is a known flake. Re-run it.", "expect": {"calls": [{"tool": "ci_rerun"}], "shellMustNot": ["\\bgh run (rerun|list|view)\\b", "\\bgh api\\b[^\\n]*rerun"]}, "notes": "Forge usage audit of agent logs on three machines (2026-10-28): agents read failing CI with gh run view --log-failed or the job logs API about 500 times in 45 days, and re-ran CI with gh run rerun 92 times."} +{"id": "control-ci-why-no-rerun", "area": "pull-requests", "kind": "control", "pattern": "question about a failing check", "experience": "Control: the user asks why CI failed. Reading the log is right; re-running it was not asked.", "rule": "Re-run only when asked", "privacy": "commit-safe", "setup": {"role": "top", "world": {"branch": "fix/retry-queue", "commits": [{"sha": "9f3c2a1", "subject": "fix(queue): retry a failed job once"}], "diff": "diff --git a/src/queue.ts b/src/queue.ts\n--- a/src/queue.ts\n+++ b/src/queue.ts\n@@ -4,3 +4,4 @@ export async function run(job: Job) {\n try {\n return await job.execute();\n- } catch {\n+ } catch (error) {\n+ if (job.attempts > 1) throw error;\n return run({ ...job, attempts: job.attempts + 1 });\n", "files": {"src/queue.ts": "export async function run(job: Job) {\n try {\n return await job.execute();\n } catch (error) {\n if (job.attempts > 1) throw error;\n return run({ ...job, attempts: job.attempts + 1 });\n }\n}\n"}, "prs": [{"number": 124, "title": "fix(queue): retry a failed job once", "headRefName": "fix/retry-queue", "checks": [{"name": "Lint", "state": "pass"}, {"name": "Test", "state": "fail", "log": "FAIL src/queue.test.ts > gives up after one retry\nAssertionError: expected 2 calls, got 3\n at src/queue.test.ts:18:5"}]}]}}, "prompt": "Why is CI failing on #124? Don't change anything yet.", "expect": {"notCalls": ["ci_rerun", "pull_request_merge"], "shellMustNot": ["\\bgh run rerun\\b"]}, "notes": "Control for ci-flake-rerun-through-gh."} diff --git a/evals/mcp-tool-use/run.test.ts b/evals/mcp-tool-use/run.test.ts index 6e189f40d732f..6da101c82363c 100644 --- a/evals/mcp-tool-use/run.test.ts +++ b/evals/mcp-tool-use/run.test.ts @@ -815,6 +815,60 @@ it("lists and reads world PRs in their own state, with the diff when asked", () [5, "merged"], [6, "open"], ]); + const ciSample = makeSample( + { + ...childCase, + setup: { + ...childCase.setup, + world: { + ...childCase.setup.world, + prs: [ + { + number: 7, + checks: [ + { name: "Test", state: "pass" }, + { name: "Lint", state: "cancelled" }, + ], + }, + ], + }, + }, + }, + base, + ); + const ciNames = (input: Record) => + ( + t3Default(ciSample, "ci_logs", { number: 7, ...input }) as { + checks: Array<{ name: string }>; + } + ).checks.map((check) => check.name); + assert.deepEqual(ciNames({ check: " test " }), ["Test"]); + const shards = makeSample( + { + ...childCase, + setup: { + ...childCase.setup, + world: { + ...childCase.setup.world, + prs: [ + { + number: 7, + checks: Array.from({ length: 6 }, (_, index) => ({ + name: `Shard ${index}`, + state: "fail", + })), + }, + ], + }, + }, + }, + base, + ); + const logs = ( + t3Default(shards, "ci_logs", { number: 7 }) as { checks: Array<{ log: string | null }> } + ).checks.map((check) => check.log !== null); + assert.deepEqual(logs, [true, true, true, true, true, false]); + assert.deepEqual(ciNames({}), ["Lint"]); const read: Readonly> = t3Default(sample, "pull_request_read", { number: 5, sections: ["summary", "diff", "conversation"], @@ -1093,9 +1147,45 @@ it.effect("reads the branch's PR only once the agent has opened it", () => input, }); assert.isTrue((yield* call("a", "pull_request_read", {})).is_error); + sample.tools.push({ name: "mcp__t3-code__ci_logs" }); + assert.isTrue((yield* call("a2", "ci_logs", {})).is_error); yield* call("b", "pull_request_create", { title: "Feature", body: "Body" }); assert.isNotTrue((yield* call("c", "pull_request_read", {})).is_error); + // A stack's next layer, checked out in the shell, opens its own PR once. + const layerPr = (id: string) => + call(id, "pull_request_create", { title: "Layer", body: "Body" }).pipe( + Effect.map((result) => /\/pull\/(\d+)/.exec(String(result.content))?.[1]), + ); + const first = /\/pull\/(\d+)/.exec([...sample.linked].join(" "))?.[1]; + sample.shell.push({ + tool: "Bash", + input: { command: "git checkout -b layer-2 2>&1" }, + output: "", + }); + // Until the layer's PR is opened, its branch has none. + assert.isTrue((yield* call("r0", "pull_request_read", {})).is_error); + // An unrelated world PR keeps its number. + world.prs.push({ number: 130, title: "Other", headRefName: "other" } as never); + const layer = yield* layerPr("b2"); + assert.notEqual(layer, first); + assert.equal(layer, "131"); + // Reads without a number act on the checked-out layer's PR. + assert.include(String((yield* call("r1", "pull_request_read", {})).content), "/pull/131"); + assert.equal(yield* layerPr("b3"), layer); + sample.shell.push({ + tool: "Bash", + input: { command: "git checkout main -- src/a.ts && git checkout feature" }, + output: "", + }); + assert.equal(yield* layerPr("b4"), first); + // The simulated host shows what the agent changed through T3's tools. + const [machine] = + shellRequest(sample, { tool: "Bash", input: { command: "gh pr list" } }, "claude-haiku-5-5") + .messages[0]?.content ?? []; + assert.include(machine?.text, "made these changes through T3's tools"); + assert.include(machine?.text, "pull_request_create"); + // A case that mocks the create still opens the PR it names. const url = "https://github.com/acme/app/pull/125"; const mocks = { pull_request_create: { number: 125, url, created: true } }; @@ -1127,6 +1217,22 @@ it.effect("reads the branch's PR only once the agent has opened it", () => input: {}, }); assert.isTrue(linkedRead.is_error); + + // A check name that matches nothing is refused, as the service refuses it. + const ciWorld = { ...world, prs: [{ number: 7, checks: [{ name: "Test", state: "fail" }] }] }; + const ciSample = makeSample( + { ...childCase, setup: { ...childCase.setup, world: ciWorld } }, + base, + ); + ciSample.tools.push({ name: "mcp__t3-code__ci_logs" }); + const unknown = yield* answerToolCall(ciSample, config, { + type: "tool_use", + id: "g", + name: "mcp__t3-code__ci_logs", + input: { number: 7, check: "Tests" }, + }); + assert.isTrue(unknown.is_error); + assert.include(String(unknown.content), "Checks: Test."); }).pipe(Effect.provide(FetchHttpClient.layer)), ); diff --git a/evals/mcp-tool-use/run.ts b/evals/mcp-tool-use/run.ts index 4e29d3df8bbd1..e972474a2a142 100644 --- a/evals/mcp-tool-use/run.ts +++ b/evals/mcp-tool-use/run.ts @@ -868,6 +868,23 @@ const branchPullRequestCreatedBy = (entry: ShellEntry, branch: string | undefine .map((url) => Number(/\/pull\/(\d+)/.exec(url)?.[1])) .find((number) => number > 0); }; +/** The branch the agent last checked out or switched to in the shell, else the world's. */ +const checkedOutBranch = (sample: Sample) => { + let branch = sample.testCase.setup.world?.branch ?? ""; + for (const entry of sample.shell) { + const command = isRecord(entry.input) ? valueText(entry.input.command) : ""; + for (const part of command.replace(/\s\d?>&\d/g, "").split(/[;&|\n]+/)) { + // `git checkout -- ` restores files and stays on the branch. + if (/\s--(\s|$)/.test(part)) continue; + const match = /\bgit (?:checkout|switch)\s+(?:-[bcBC]\s+)?([A-Za-z0-9][\w./-]*)\s*$/.exec( + part.trim(), + ); + if (match !== null) branch = match[1]!; + } + } + return branch; +}; + /** * One sample's state. ToolSearch adds loaded schemas to `tools`, like Claude Code. `shell` is the * history the simulator keeps consistent with; `linked`, `nextTask` and `threads` do the same for @@ -883,6 +900,10 @@ type Sample = { readonly linked: Set; /** The branch PR the agent opened in this sample, which links alone do not prove. */ openedPullRequest: number | undefined; + /** PRs pull_request_create opened from branches other than the world's, by branch. */ + readonly openedByBranch: Map; + /** PR changes the agent made through T3's tools, which gh and the host show from then on. */ + readonly forgeChanges: Array; readonly codeStore: Record; nextTask: number; /** Threads the agent launched in this sample, by id, so reading one shows its own brief. */ @@ -938,6 +959,8 @@ export const makeSample = (testCase: McpToolUseCase, base: Base): Sample => { shell, shellUsage: emptyUsage(), linked: new Set([...urls, ...shell.flatMap(createdPullRequests)]), + openedByBranch: new Map(), + forgeChanges: [], openedPullRequest: shell .map((entry) => branchPullRequestCreatedBy(entry, testCase.setup.world?.branch)) @@ -1218,7 +1241,7 @@ const worldState = (pr: Readonly> | undefined) => { * The PR for the sample's branch: the world's, else the one a create would open. A case that * mocks `gh pr create` names that URL, so the T3 tool opens the same number. */ -const branchPullRequest = (sample: Sample) => { +const worldBranchPullRequest = (sample: Sample) => { const branch = sample.testCase.setup.world?.branch; const prs = worldPullRequests(sample); const own = prs.find((pr) => pr.headRefName === branch); @@ -1235,6 +1258,41 @@ const branchPullRequest = (sample: Sample) => { return { number, created: true, pr: undefined }; }; +/** The PR for the checked-out branch: a stack layer's own, or the world branch's. */ +const branchPullRequest = (sample: Sample) => { + const checkedOut = checkedOutBranch(sample); + if (checkedOut === sample.testCase.setup.world?.branch) return worldBranchPullRequest(sample); + const known = worldPullRequests(sample).find((pr) => pr.headRefName === checkedOut); + if (known !== undefined) return { number: Number(known.number), created: false, pr: known }; + const opened = sample.openedByBranch.get(checkedOut); + if (opened !== undefined) return { number: opened, created: false, pr: undefined }; + const number = + Math.max( + worldBranchPullRequest(sample).number, + ...sample.openedByBranch.values(), + ...worldPullRequests(sample).map((pr) => Number(pr.number) || 0), + ...[...sample.linked].map((url) => Number(/\/pull\/(\d+)/.exec(url)?.[1]) || 0), + ) + 1; + return { number, created: true, pr: undefined }; +}; + +/** PullRequestService reads this many logs when no check is named. */ +const DEFAULT_CHECK_LOGS = 5; + +/** The checks `ci_logs` and `ci_rerun` act on, selected as PullRequestService does. */ +const selectedWorldChecks = (sample: Sample, input: Readonly>) => { + const number = typeof input.number === "number" ? input.number : branchPullRequest(sample).number; + const world = worldPullRequests(sample).find((pr) => Number(pr.number) === number); + const all = Array.isArray(world?.checks) ? world.checks.filter(isRecord) : []; + const named = typeof input.check === "string" ? input.check.trim().toLowerCase() : null; + const checks = all.filter((check) => + named === null + ? check.state === "fail" || check.state === "cancelled" + : valueText(check.name).toLowerCase() === named, + ); + return { number, all, checks, named }; +}; + /** A believable T3 result for a tool the case does not mock. */ export const t3Default = ( sample: Sample, @@ -1332,10 +1390,14 @@ export const t3Default = ( }; } case "pull_request_create": { + const checkedOut = checkedOutBranch(sample); const { number, created } = branchPullRequest(sample); const target = pullRequest(sample.repo, { number }); sample.linked.add(target.url); - if (created) sample.openedPullRequest = number; + // Another layer of a stack gets its own PR, opened once per branch like the real tool. + if (checkedOut !== sample.testCase.setup.world?.branch) { + sample.openedByBranch.set(checkedOut, number); + } else if (created) sample.openedPullRequest = number; return { ...target, created }; } case "pull_request_read": { @@ -1453,6 +1515,32 @@ export const t3Default = ( ...pullRequest(sample.repo, { number: input.number ?? branchPullRequest(sample).number }), state: input.auto === true ? "open" : "merged", }; + case "ci_logs": + case "ci_rerun": { + const { number, checks, named } = selectedWorldChecks(sample, input); + return { + ...pullRequest(sample.repo, { number }), + checks: checks.map((check, index) => + name === "ci_rerun" + ? { name: valueText(check.name), rerun: true, detail: null } + : named === null && index >= DEFAULT_CHECK_LOGS + ? { + name: valueText(check.name), + url: null, + log: null, + truncated: false, + detail: "Not read, to keep the reply short. Ask for this check by name.", + } + : { + name: valueText(check.name), + url: null, + log: valueText(check.log ?? `Error: ${valueText(check.name)} failed`), + truncated: false, + detail: null, + }, + ), + }; + } case "request_secret": { // T3 names a ref and a file with 32 hex characters, new for each request, in its secrets dir. const id = NodeCrypto.createHash("sha256") @@ -1636,6 +1724,8 @@ const BRANCH_PULL_REQUEST_TOOLS = new Set([ "pull_request_respond", "pull_request_review", "pull_request_merge", + "ci_logs", + "ci_rerun", ]); const BRANCH_HAS_NO_PR_ERROR = "This thread's branch has no known PR. Pass number, or open one with pull_request_create."; @@ -1719,6 +1809,11 @@ export const shellRequest = (sample: Sample, call: Omit, m `T3's request_secret saved these files in this sample: ${sample.secretFiles.join(", ")}. Each exists, is readable only by the user, and holds exactly the value the user gave, typed into T3's private card or pasted in chat and saved by the agent, the right one for the task, with no trailing newline. A program that reads it, as in "$(cat )", < or a --*-file flag, gets that value. What appears in the output is whatever that program prints: one that prints or logs its input shows the value. No other file in that directory exists.`, ] : []), + ...(sample.forgeChanges.length > 0 + ? [ + `The agent made these changes through T3's tools; gh, git fetch and the host show them: ${sample.forgeChanges.join("; ")}`, + ] + : []), ...(context ? [`The agent was told: ${context}`] : []), `The agent's task: ${testCase.prompt}`, ...namedPathsLine(testCase), @@ -1825,6 +1920,26 @@ const savedSecretFile = (sample: Sample, result: unknown) => { }; /** The result for one tool call. */ +const FORGE_WRITE_TOOLS = new Set([ + "pull_request_create", + "pull_request_update", + "pull_request_respond", + "pull_request_review", + "pull_request_merge", + "ci_rerun", +]); + +const recordForgeChange = ( + sample: Sample, + name: string, + input: Readonly>, + result: unknown, +) => { + if (FORGE_WRITE_TOOLS.has(name)) { + sample.forgeChanges.push(`${name} ${valueText(input)} returned ${valueText(result)}`); + } +}; + export const answerToolCall = Effect.fn("evaluateMcpToolUse.answerToolCall")(function* ( sample: Sample, config: Config, @@ -1895,6 +2010,7 @@ export const answerToolCall = Effect.fn("evaluateMcpToolUse.answerToolCall")(fun const number = Number(mock.number); if (number > 0) sample.openedPullRequest = number; } + recordForgeChange(sample, name, input, mock); return toolResult( call.id, name === "orchestrator_capabilities" @@ -1911,8 +2027,16 @@ export const answerToolCall = Effect.fn("evaluateMcpToolUse.answerToolCall")(fun // Until the agent opens it, the PR a create would open does not exist. if (branchPullRequest(sample).created) return failed(BRANCH_HAS_NO_PR_ERROR); } + if (name === "ci_logs" || name === "ci_rerun") { + const { all, checks, named } = selectedWorldChecks(sample, input); + if (named !== null && checks.length === 0) { + const names = all.map((check) => valueText(check.name)).join(", ") || "none"; + return failed(`No check is named ${valueText(input.check)}. Checks: ${names}.`); + } + } const value = t3Default(sample, name, input); if (name === "request_secret") savedSecretFile(sample, value); + recordForgeChange(sample, name, input, value); return toolResult( call.id, name === "orchestrator_capabilities" diff --git a/packages/client-runtime/src/t3ToolSummary.test.ts b/packages/client-runtime/src/t3ToolSummary.test.ts index 3800c1cfac58f..99486d1184abf 100644 --- a/packages/client-runtime/src/t3ToolSummary.test.ts +++ b/packages/client-runtime/src/t3ToolSummary.test.ts @@ -76,6 +76,29 @@ describe("summarizeT3ToolCalls", () => { ).toBe("Merged 1 pull request, queued 1"); }); + it("counts the checks a CI rerun restarted, and the ones the host refused", () => { + const rerun = (...outcomes: boolean[]) => + completed( + {}, + { checks: outcomes.map((outcome, index) => ({ name: `c${index}`, rerun: outcome })) }, + ); + expect(summarizeT3ToolCalls("ci-rerun", [rerun(true, true)])).toEqual({ + label: "Re-ran 2 checks", + failedCount: 0, + }); + expect(summarizeT3ToolCalls("ci-rerun", [rerun(true, false)])).toEqual({ + label: "Re-ran 1 check, 1 refused", + failedCount: 1, + }); + expect(summarizeT3ToolCalls("ci-rerun", [rerun(false)])).toEqual({ + label: "Could not re-run 1 check", + failedCount: 1, + }); + expect(summarizeT3ToolCalls("ci-rerun", [rerun()]).label).toBe( + "Found no failed checks to re-run", + ); + }); + it("excludes failed and unfinished sends even if the provider reports completed", () => { const calls: T3ToolSummaryCall[] = [ completed({ threadId: "success" }, { messageId: "ok", threadId: "success" }), diff --git a/packages/client-runtime/src/t3ToolSummary.ts b/packages/client-runtime/src/t3ToolSummary.ts index c399d0a12d91b..63ab355ca7121 100644 --- a/packages/client-runtime/src/t3ToolSummary.ts +++ b/packages/client-runtime/src/t3ToolSummary.ts @@ -504,6 +504,35 @@ export function summarizeT3ToolCalls( ); break; } + case "ci-logs": + label = phrase("Read", "read", `CI logs ${times}`); + break; + case "ci-rerun": { + // A host can refuse a check it cannot re-run, such as one outside its CI. + const outcomes = selected.flatMap((call) => + Array.isArray(call.output?.checks) + ? call.output.checks.map((check) => asRecord(check)?.rerun === true) + : [], + ); + const rerun = outcomes.filter(Boolean).length; + const refused = outcomes.length - rerun; + failedCount += refused; + if (outcomes.length === 0) { + // An empty list means no check had failed, so nothing was asked of the host. + label = selected.some((call) => Array.isArray(call.output?.checks)) + ? phrase("Found", "re-run", "no failed checks to re-run") + : phrase("Re-ran", "re-run", `CI ${times}`); + } else if (rerun === 0) { + label = `Could not re-run ${quantity(refused, "check")}`; + } else { + label = phrase( + "Re-ran", + "re-run", + `${quantity(rerun, "check")}${refused > 0 ? `, ${refused} refused` : ""}`, + ); + } + break; + } case "browser": label = phrase("Used", "use", `browser ${times}`); break; diff --git a/packages/provider-core/src/server/runtimeInstructions.ts b/packages/provider-core/src/server/runtimeInstructions.ts index 7dce30e4df69a..865bbaeea0b35 100644 --- a/packages/provider-core/src/server/runtimeInstructions.ts +++ b/packages/provider-core/src/server/runtimeInstructions.ts @@ -1,6 +1,6 @@ /** Session-invariant; harnesses with a native system-prompt append can carry it once per session. */ export const PULL_REQUEST_LINKING_INSTRUCTIONS = ` -Link each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you. +Link each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you. `; /** Omit model and effort when the harness manages them dynamically. */ diff --git a/packages/shared/src/t3McpToolPresentation.ts b/packages/shared/src/t3McpToolPresentation.ts index 8e25213329ef0..9de305dd29793 100644 --- a/packages/shared/src/t3McpToolPresentation.ts +++ b/packages/shared/src/t3McpToolPresentation.ts @@ -71,6 +71,8 @@ export type T3McpToolSummaryAction = | "update-pr" | "respond-pr" | "merge-pr" + | "ci-logs" + | "ci-rerun" | "browser" | "device" | "html-preview" @@ -173,6 +175,8 @@ const T3_MCP_TOOLS: Readonly> = { "merge-pr", "pull-request", ), + ci_logs: tool(["Read", "Reading", "Read", "CI logs"], "ci-logs", "pull-request"), + ci_rerun: tool(["Re-run", "Re-running", "Re-ran", "CI"], "ci-rerun", "pull-request"), orchestrator_capabilities: tool( ["Get", "Getting", "Got", "orchestration capabilities"], "capabilities", diff --git a/packages/source-control-core/src/server/PullRequestProvider.ts b/packages/source-control-core/src/server/PullRequestProvider.ts index 8b777ce58479a..afdcd199807c0 100644 --- a/packages/source-control-core/src/server/PullRequestProvider.ts +++ b/packages/source-control-core/src/server/PullRequestProvider.ts @@ -190,6 +190,52 @@ export interface ProviderBatchedChangeRequestPage { readonly unresolved?: ReadonlyArray; } +/** A check as the change request's checks name it; the host finds its log or run from the URL. */ +export interface ProviderCheckReference { + readonly name: string; + readonly url: string | null; +} + +export interface ProviderCheckLog { + readonly name: string; + readonly url: string | null; + /** Null when the host holds no log for this check; `detail` says why. */ + readonly log: string | null; + readonly truncated: boolean; + readonly detail: string | null; +} + +export interface ProviderCheckRerun { + readonly name: string; + readonly rerun: boolean; + readonly detail: string | null; +} + +/** The job id of a GitHub Actions check run, from its run link or its job link. */ +export function gitHubActionsJobId(url: string | null): string | null { + if (url === null) return null; + return ( + /\/runs\/\d+\/jobs?\/(\d+)(?:[/?#]|$)/.exec(url)?.[1] ?? + /^https:\/\/[^/]+\/[^/]+\/[^/]+\/runs\/(\d+)(?:[/?#]|$)/.exec(url)?.[1] ?? + null + ); +} + +/** Keeps one minified or progress-bar line from filling the agent's context. */ +const LOG_TAIL_MAX_CHARS = 32_000; + +/** The last `lines` lines of a log, where a failure usually is. */ +export function tailLogLines( + text: string, + lines: number, +): { readonly log: string; readonly truncated: boolean } { + const all = text.replace(/\n$/, "").split("\n"); + const log = all.slice(-lines).join("\n"); + return log.length > LOG_TAIL_MAX_CHARS + ? { log: log.slice(-LOG_TAIL_MAX_CHARS), truncated: true } + : { log, truncated: all.length > lines }; +} + export interface ProviderChangeRequestStat { readonly repository: string; readonly number: number; @@ -506,6 +552,19 @@ export interface PullRequestProviderApi { }, ) => Effect.Effect; + /** Tails of the named checks' logs. A check the host holds no log for gets a `detail`. */ + readonly getCheckLogs?: ( + input: ProviderRepositoryRef & { + readonly checks: ReadonlyArray; + readonly tailLines: number; + }, + ) => Effect.Effect, PullRequestProviderError>; + + /** Re-runs the named checks' jobs. A check the host cannot re-run gets a `detail`. */ + readonly rerunChecks?: ( + input: ProviderRepositoryRef & { readonly checks: ReadonlyArray }, + ) => Effect.Effect, PullRequestProviderError>; + /** Requires edit capability and at least one field. */ readonly updateChangeRequest?: ( input: ProviderRepositoryRef & { diff --git a/packages/source-control-gitcafe/src/server/GitCafePullRequestProvider.test.ts b/packages/source-control-gitcafe/src/server/GitCafePullRequestProvider.test.ts index cb2bf219c0b5e..bc495f16c2cd1 100644 --- a/packages/source-control-gitcafe/src/server/GitCafePullRequestProvider.test.ts +++ b/packages/source-control-gitcafe/src/server/GitCafePullRequestProvider.test.ts @@ -677,6 +677,46 @@ describe("GitCafePullRequestProvider", () => { }).pipe(Effect.provide(server.layer)); }); + it.effect("reads a GitHub-mirrored check's log, and re-runs only GitCafe Actions runs", () => { + const server = fakeGitCafe({ + "GET /repos/owner/repo/ci/github/checks/555/log": { + content: "install\ntest\nFAIL a.test.ts\n", + truncated: true, + }, + "POST /repos/owner/repo/actions/runs/actwfrun_abc/rerun": {}, + "GET /repos/owner/repo/ci/github/checks/401/log": new Response("{}", { status: 401 }), + }); + const mirrored = { name: "Test", url: "https://github.com/owner/repo/runs/555" }; + const missing = { name: "Gone", url: "https://github.com/owner/repo/runs/404" }; + const signedOut = { name: "Lint", url: "https://github.com/owner/repo/runs/401" }; + const native = { name: "Build", url: "https://git.cafe/owner/repo/actions/runs/actwfrun_abc" }; + return Effect.gen(function* () { + const provider = yield* GitCafePullRequestProvider.make; + const logs = yield* provider.getCheckLogs!({ + ...target, + checks: [mirrored, missing], + tailLines: 1, + }); + assert.deepInclude(logs[0], { log: "FAIL a.test.ts", truncated: true, detail: null }); + assert.isNotNull(logs[1]?.detail); + const refused = yield* Effect.flip( + provider.getCheckLogs!({ ...target, checks: [signedOut], tailLines: 1 }), + ); + assert.strictEqual(refused.reason, "unauthenticated"); + const reruns = yield* provider.rerunChecks!({ ...target, checks: [mirrored, native] }); + assert.deepStrictEqual( + reruns.map(({ name, rerun }) => [name, rerun]), + [ + ["Test", false], + ["Build", true], + ], + ); + assert.deepStrictEqual(server.writes(), [ + "POST /repos/owner/repo/actions/runs/actwfrun_abc/rerun", + ]); + }).pipe(Effect.provide(server.layer)); + }); + it.effect( "lands a stack through a layer only while its heads are the ones the reader saw", () => { diff --git a/packages/source-control-gitcafe/src/server/GitCafePullRequestProvider.ts b/packages/source-control-gitcafe/src/server/GitCafePullRequestProvider.ts index 314fc30902751..fca4c1247e5dc 100644 --- a/packages/source-control-gitcafe/src/server/GitCafePullRequestProvider.ts +++ b/packages/source-control-gitcafe/src/server/GitCafePullRequestProvider.ts @@ -13,7 +13,11 @@ import { type PullRequestStackHead, } from "@t3tools/contracts"; import { + gitHubActionsJobId, PullRequestProviderError, + tailLogLines, + type ProviderCheckLog, + type ProviderCheckRerun, type ProviderRepositoryRef, type PullRequestProviderApi, } from "@t3tools/source-control-core/server/PullRequestProvider"; @@ -228,6 +232,9 @@ const withDetails = ( }; /** @public */ +const CHECK_LOG_TAIL_BYTES = 256 * 1024; +const CheckLog = Schema.Struct({ content: Schema.String, truncated: Schema.Boolean }); + export const make = Effect.gen(function* () { const api = yield* GitCafeApi.GitCafeApi; // GitCafe filters by actor id and has no lookup from a GitHub login to the actor sync made for @@ -451,6 +458,9 @@ export const make = Effect.gen(function* () { ), ); + const isCheckFailure = (error: PullRequestProviderError) => + error.reason === "not-found" || error.reason === "forbidden" || error.reason === "failed"; + const isForbidden = (error: PullRequestProviderError) => isApiError(error.cause) && error.cause.status === 403; @@ -1129,6 +1139,82 @@ export const make = Effect.gen(function* () { return { oldContents, newContents }; }, ), + getCheckLogs: Effect.fn("GitCafePullRequestProvider.getCheckLogs")(function* (input) { + const base = yield* repositoryPath("getCheckLogs", input); + return yield* Effect.forEach( + input.checks, + (check): Effect.Effect => { + // GitCafe reads the log of a check its GitHub mirror ran. + const checkId = gitHubActionsJobId(check.url); + if (checkId === null) { + return Effect.succeed({ + ...check, + log: null, + truncated: false, + detail: "GitCafe holds no log for this check. Open its url.", + }); + } + return read( + "getCheckLogs", + input, + { path: `${base}/ci/github/checks/${checkId}/log?tailBytes=${CHECK_LOG_TAIL_BYTES}` }, + CheckLog, + ).pipe( + Effect.map((log) => { + const tail = tailLogLines(log.content, input.tailLines); + return { + ...check, + log: tail.log, + truncated: tail.truncated || log.truncated, + detail: null, + }; + }), + // Sign-in and rate-limit failures fail the call; the rest are this check's. + Effect.catchIf(isCheckFailure, (error) => + Effect.succeed({ ...check, log: null, truncated: false, detail: error.detail }), + ), + ); + }, + { concurrency: 4 }, + ); + }), + rerunChecks: Effect.fn("GitCafePullRequestProvider.rerunChecks")(function* (input) { + const base = yield* repositoryPath("rerunChecks", input); + const runIds = new Set(); + const located = input.checks.map((check) => { + const runId = /\/actions\/runs\/(actwfrun_[A-Za-z0-9]+)/.exec(check.url ?? "")?.[1] ?? null; + if (runId !== null) runIds.add(runId); + return { check, runId }; + }); + const outcomes = new Map(); + yield* Effect.forEach( + runIds, + (runId) => + send("rerunChecks", input, { + method: "POST", + path: `${base}/actions/runs/${runId}/rerun`, + }).pipe( + Effect.as(null), + Effect.catchIf(isCheckFailure, (error) => Effect.succeed(error.detail)), + Effect.map((detail) => outcomes.set(runId, detail)), + ), + { concurrency: 2, discard: true }, + ); + return located.map(({ check, runId }): ProviderCheckRerun => { + if (runId === null) { + return { + name: check.name, + rerun: false, + detail: + gitHubActionsJobId(check.url) === null + ? "GitCafe cannot re-run this check." + : "This check ran on the GitHub mirror, so GitCafe cannot re-run it. Re-run it on GitHub.", + }; + } + const detail = outcomes.get(runId) ?? null; + return { name: check.name, rerun: detail === null, detail }; + }); + }), runAction: Effect.fn("GitCafePullRequestProvider.runAction")(function* (input) { if (input.stackNumber !== undefined) return yield* runStackAction({ ...input, stackNumber: input.stackNumber }); diff --git a/packages/source-control-github/src/server/GitHubPullRequestApi.test.ts b/packages/source-control-github/src/server/GitHubPullRequestApi.test.ts index c83692d287fa1..0b7cf77e84e79 100644 --- a/packages/source-control-github/src/server/GitHubPullRequestApi.test.ts +++ b/packages/source-control-github/src/server/GitHubPullRequestApi.test.ts @@ -796,6 +796,121 @@ layer("GitHubPullRequestApi.layer", (it) => { }), ); + it.effect("tails Actions job logs and re-runs a run's failed jobs together", () => + Effect.gen(function* () { + const runOf: Record = { + "11": 10, + "12": 10, + "21": 20, + "41": 40, + "42": 40, + "51": 50, + "52": 50, + }; + const runJobs: Record> = { + "10": [ + { id: 11, conclusion: "failure" }, + { id: 12, conclusion: "cancelled" }, + ], + // 41 passed and is asked for by name; 43 failed but was not selected. + "40": [ + { id: 41, conclusion: "success" }, + { id: 42, conclusion: "failure" }, + { id: 43, conclusion: "failure" }, + ], + "50": [ + { id: 51, conclusion: "failure" }, + { id: 52, conclusion: "failure" }, + ], + }; + mockedExecute.mockImplementation((call) => { + if (call.kind !== "rest") return Effect.succeed(output("{}")); + const log = /actions\/jobs\/(\d+)\/logs$/.exec(call.path); + if (log?.[1] === "13") return Effect.succeed(output("one long line", true)); + if (log?.[1] === "15") return Effect.succeed(output("x".repeat(100_000))); + if (log?.[1] === "14") { + return Effect.fail( + new GitHubRequestPolicy.GitHubApiAuthenticationError({ + host: "github.com", + operation: "readCheckLog", + }), + ); + } + if (log) return Effect.succeed(output("setup\nbuild\nError: boom\n")); + const job = /actions\/jobs\/(\d+)$/.exec(call.path); + if (job) return Effect.succeed(output(encodeJson({ run_id: runOf[job[1]!] }))); + const run = /actions\/runs\/(\d+)\/jobs\?/.exec(call.path); + if (run) { + const jobs = runJobs[run[1]!] ?? []; + // Run 50 has more jobs than one page shows. + const total = run[1] === "50" ? 150 : jobs.length; + return Effect.succeed(output(encodeJson({ total_count: total, jobs }))); + } + return Effect.succeed(output("{}")); + }); + const api = yield* GitHubPullRequestApi.GitHubPullRequestApi; + const job = (id: number, run: number) => ({ + name: `job ${id}`, + url: `https://github.com/octo/repo/actions/runs/${run}/job/${id}`, + }); + const external = { name: "Vercel", url: "https://vercel.com/octo/repo/deployments/1" }; + const logs = yield* api.readCheckLogs({ + host: "github.com", + repository: "octo/repo", + checks: [job(11, 10), external], + tailLines: 2, + }); + expect(logs).toMatchObject([ + { name: "job 11", log: "build\nError: boom", truncated: true, detail: null }, + { name: "Vercel", log: null }, + ]); + const capped = { host: "github.com", repository: "octo/repo", tailLines: 5 }; + expect(yield* api.readCheckLogs({ ...capped, checks: [job(13, 30)] })).toMatchObject([ + { log: "one long line", truncated: true }, + ]); + const refused = yield* Effect.flip(api.readCheckLogs({ ...capped, checks: [job(14, 30)] })); + const [huge] = yield* api.readCheckLogs({ ...capped, checks: [job(15, 30)] }); + expect(huge?.log?.length).toBe(32_000); + expect(huge?.truncated).toBe(true); + expect(refused._tag).toBe("GitHubApiAuthenticationError"); + + const reruns = yield* api.rerunChecks({ + host: "github.com", + repository: "octo/repo", + checks: [ + job(11, 10), + job(12, 10), + { name: "solo", url: "https://github.com/octo/repo/runs/21" }, + external, + job(41, 40), + { name: "job 42", url: "https://github.com/octo/repo/actions/runs/40/jobs/42" }, + job(51, 50), + job(52, 50), + ], + }); + expect(reruns.map(({ name, rerun }) => [name, rerun])).toEqual([ + ["job 11", true], + ["job 12", true], + ["solo", true], + ["Vercel", false], + ["job 41", true], + ["job 42", false], + ["job 51", true], + ["job 52", false], + ]); + expect( + mockedExecute.mock.calls.flatMap(([call]) => + call.kind === "rest" && call.method === "POST" ? [call.path] : [], + ), + ).toEqual([ + "repos/octo/repo/actions/runs/10/rerun-failed-jobs", + "repos/octo/repo/actions/jobs/21/rerun", + "repos/octo/repo/actions/jobs/41/rerun", + "repos/octo/repo/actions/jobs/51/rerun", + ]); + }), + ); + it.effect.each(["mixed", "merges", "unavailable"] as const)( "reads stats only for displayed non-merge commits (%s)", (kind) => diff --git a/packages/source-control-github/src/server/GitHubPullRequestApi.ts b/packages/source-control-github/src/server/GitHubPullRequestApi.ts index 771b51e870364..23030b4c8616d 100644 --- a/packages/source-control-github/src/server/GitHubPullRequestApi.ts +++ b/packages/source-control-github/src/server/GitHubPullRequestApi.ts @@ -125,9 +125,14 @@ import { type GitHubViewerAccess, IMMEDIATELY_MERGEABLE, } from "./gitHubPullRequestJson.ts"; -import type { - ProviderChangeRequestSummary, - ProviderListCursor, +import { + gitHubActionsJobId, + tailLogLines, + type ProviderChangeRequestSummary, + type ProviderCheckLog, + type ProviderCheckReference, + type ProviderCheckRerun, + type ProviderListCursor, } from "@t3tools/source-control-core/server/PullRequestProvider"; export class GitHubPullRequestReadError extends Schema.TaggedError()( @@ -715,6 +720,17 @@ export class GitHubPullRequestApi extends Context.Service< readonly applied: boolean; }) => Effect.Effect; + readonly readCheckLogs: (input: { + readonly host: string; + readonly repository: string; + readonly checks: ReadonlyArray; + readonly tailLines: number; + }) => Effect.Effect, GitHubPullRequestApiError>; + readonly rerunChecks: (input: { + readonly host: string; + readonly repository: string; + readonly checks: ReadonlyArray; + }) => Effect.Effect, GitHubPullRequestApiError>; readonly runPullRequestAction: (input: { readonly cwd: string; readonly repository: string; @@ -795,6 +811,32 @@ export class GitHubPullRequestApi extends Context.Service< } >()("@t3tools/source-control-github/server/GitHubPullRequestApi") {} +const CHECK_LOG_MAX_BYTES = 16 * 1024 * 1024; +const NOT_A_JOB = "This check is not a GitHub Actions job, so GitHub cannot re-run it."; +const decodeActionsJob = Schema.decodeEffect( + Schema.fromJsonString(Schema.Struct({ run_id: Schema.Number })), +); +const decodeRunJobs = Schema.decodeEffect( + Schema.fromJsonString( + Schema.Struct({ + total_count: Schema.Number, + jobs: Schema.Array( + Schema.Struct({ id: Schema.Number, conclusion: Schema.NullOr(Schema.String) }), + ), + }), + ), +); +const FAILED_CONCLUSIONS = new Set(["failure", "cancelled", "timed_out", "startup_failure"]); +const ONE_JOB_AT_A_TIME = + "GitHub re-runs one job of a run at a time. Re-run this one after the other finishes."; + +interface LocatedCheckJob { + readonly check: ProviderCheckReference; + readonly jobId: string | null; + readonly runId: number | null; + readonly detail: string | null; +} + function parseRepositorySelector(value: string): { readonly owner: string; readonly name: string; @@ -2782,6 +2824,145 @@ export const make = Effect.gen(function* () { ); }), + readCheckLogs: Effect.fn("GitHubPullRequestApi.readCheckLogs")(function* (input) { + const { owner, name } = parseRepositorySelector(input.repository); + return yield* Effect.forEach( + input.checks, + (check) => { + const jobId = gitHubActionsJobId(check.url); + if (jobId === null) { + return Effect.succeed({ + ...check, + log: null, + truncated: false, + detail: "This check is not a GitHub Actions job, so GitHub holds no log for it.", + }); + } + // The log is a redirect to storage; fetch drops the credential when it leaves GitHub. + return api + .rest({ + host: input.host, + operation: "readCheckLog", + path: `repos/${owner}/${name}/actions/jobs/${jobId}/logs`, + maxResponseBytes: CHECK_LOG_MAX_BYTES, + timeout: "60 seconds", + }) + .pipe( + Effect.map((response) => { + const tail = tailLogLines(response.body, input.tailLines); + return { + ...check, + log: tail.log, + truncated: tail.truncated || response.truncated, + detail: response.truncated + ? "The log is too large; this is from its start." + : null, + }; + }), + // Sign-in and rate-limit failures fail the call; the rest are this check's. + Effect.catchTags({ + GitHubApiNotFoundError: (error) => + Effect.succeed({ ...check, log: null, truncated: false, detail: error.message }), + GitHubApiResponseError: (error) => + Effect.succeed({ ...check, log: null, truncated: false, detail: error.message }), + }), + ); + }, + { concurrency: 4 }, + ); + }), + + rerunChecks: Effect.fn("GitHubPullRequestApi.rerunChecks")(function* (input) { + const { owner, name } = parseRepositorySelector(input.repository); + const root = `repos/${owner}/${name}/actions`; + const located = yield* Effect.forEach( + input.checks, + (check): Effect.Effect => { + const jobId = gitHubActionsJobId(check.url); + if (jobId === null) + return Effect.succeed({ check, jobId, runId: null, detail: NOT_A_JOB }); + return api + .rest({ host: input.host, operation: "readCheckJob", path: `${root}/jobs/${jobId}` }) + .pipe( + Effect.flatMap((response) => decodeActionsJob(response.body)), + Effect.map((job) => ({ check, jobId, runId: job.run_id, detail: null })), + Effect.catchTags({ + GitHubApiNotFoundError: (error) => + Effect.succeed({ check, jobId, runId: null, detail: error.message }), + GitHubApiResponseError: (error) => + Effect.succeed({ check, jobId, runId: null, detail: error.message }), + SchemaError: () => Effect.succeed({ check, jobId, runId: null, detail: NOT_A_JOB }), + }), + ); + }, + { concurrency: 4 }, + ); + // GitHub refuses a second job re-run while the run re-runs, so a run's jobs go together. + const byRun = new Map>(); + for (const entry of located) { + if (entry.runId === null) continue; + byRun.set(entry.runId, [...(byRun.get(entry.runId) ?? []), entry]); + } + const post = (path: string) => + api.rest({ host: input.host, operation: "rerunChecks", method: "POST", path }).pipe( + Effect.as(null), + Effect.catchTags({ + GitHubApiNotFoundError: (error) => Effect.succeed(error.message), + GitHubApiResponseError: (error) => Effect.succeed(error.message), + }), + ); + const failedJobsOf = (runId: number) => + api + .rest({ + host: input.host, + operation: "readRunJobs", + path: `${root}/runs/${runId}/jobs?filter=latest&per_page=100`, + }) + .pipe( + Effect.flatMap((response) => decodeRunJobs(response.body)), + // Jobs past the first page are unseen, so such a run counts as unknown. + Effect.map(({ total_count, jobs }) => + total_count > jobs.length + ? new Set() + : new Set( + jobs + .filter((job) => FAILED_CONCLUSIONS.has(job.conclusion ?? "")) + .map((job) => String(job.id)), + ), + ), + Effect.catchTags({ + GitHubApiNotFoundError: () => Effect.succeed(new Set()), + GitHubApiResponseError: () => Effect.succeed(new Set()), + SchemaError: () => Effect.succeed(new Set()), + }), + ); + const outcomes = new Map(); + yield* Effect.forEach( + byRun, + ([runId, jobs]) => + Effect.gen(function* () { + const [first, ...rest] = jobs; + if (first === undefined) return; + if (rest.length > 0) { + // Re-running a run's failed jobs fits only when they are exactly the selection. + const failed = yield* failedJobsOf(runId); + if (failed.size === jobs.length && jobs.every((job) => failed.has(job.jobId ?? ""))) { + const detail = yield* post(`${root}/runs/${runId}/rerun-failed-jobs`); + for (const job of jobs) outcomes.set(job.jobId, detail); + return; + } + } + outcomes.set(first.jobId, yield* post(`${root}/jobs/${first.jobId}/rerun`)); + for (const job of rest) outcomes.set(job.jobId, ONE_JOB_AT_A_TIME); + }), + { concurrency: 2, discard: true }, + ); + return located.map(({ check, jobId, runId, detail }) => { + const outcome = runId === null ? detail : (outcomes.get(jobId) ?? null); + return { name: check.name, rerun: runId !== null && outcome === null, detail: outcome }; + }); + }), + submitReview: Effect.fn("GitHubPullRequestApi.submitReview")(function (input) { const { owner, name } = parseRepositorySelector(input.repository); return api diff --git a/packages/source-control-github/src/server/GitHubPullRequestProvider.ts b/packages/source-control-github/src/server/GitHubPullRequestProvider.ts index 066b3ee7f8c64..b29a633574a9f 100644 --- a/packages/source-control-github/src/server/GitHubPullRequestProvider.ts +++ b/packages/source-control-github/src/server/GitHubPullRequestProvider.ts @@ -740,6 +740,9 @@ export const make = Effect.gen(function* () { }) .pipe(Effect.mapError(fail("runAction"))), + getCheckLogs: (input) => cli.readCheckLogs(input).pipe(Effect.mapError(fail("getCheckLogs"))), + rerunChecks: (input) => cli.rerunChecks(input).pipe(Effect.mapError(fail("rerunChecks"))), + updateChangeRequest: (input) => cli .updatePullRequest({