diff --git a/apps/server/src/mcp/McpInvocationContext.ts b/apps/server/src/mcp/McpInvocationContext.ts index 842d18ad9c8b..f8788917da05 100644 --- a/apps/server/src/mcp/McpInvocationContext.ts +++ b/apps/server/src/mcp/McpInvocationContext.ts @@ -136,14 +136,18 @@ export interface McpInvocationScope { /** * T3 Code renders `t3-thread://` links as thread chips. Other MCP clients cannot open them, so * they get the hosted link Copy thread copies, which opens the thread in either app or a browser. + * A known `title` labels the chip for clients that cannot load this environment's threads. */ export const threadLinkFor = ( scope: Pick, threadId: ThreadId, + title?: string, ): string => { const hostedAppUrl = scope.client?.hostedAppUrl; const ref = { environmentId: scope.environmentId, threadId }; - return hostedAppUrl === undefined ? formatThreadLink(ref) : hostedThreadLink(ref, hostedAppUrl); + return hostedAppUrl === undefined + ? formatThreadLink(ref, title) + : hostedThreadLink(ref, hostedAppUrl); }; export class McpInvocationContext extends Context.Service< diff --git a/apps/server/src/mcp/OrchestratorMcpService.ts b/apps/server/src/mcp/OrchestratorMcpService.ts index 4750e9a335f4..a1425d56e8e5 100644 --- a/apps/server/src/mcp/OrchestratorMcpService.ts +++ b/apps/server/src/mcp/OrchestratorMcpService.ts @@ -805,7 +805,7 @@ function listItemFromShell( threadId: shell.id, projectId: shell.projectId, title: shell.title, - link: threadLinkFor(scope, shell.id), + link: threadLinkFor(scope, shell.id, shell.title), createdBy: shell.createdBy, creationSource: shell.creationSource, status: shell.activityRunStatus ?? shell.status, @@ -839,7 +839,7 @@ function threadDetail( threadId: projection.thread.id, projectId: projection.thread.projectId, title: projection.thread.title, - link: threadLinkFor(scope, projection.thread.id), + link: threadLinkFor(scope, projection.thread.id, projection.thread.title), createdBy: projection.thread.createdBy, creationSource: projection.thread.creationSource, status: active?.status ?? latest?.status ?? "idle", @@ -1630,10 +1630,10 @@ const make = Effect.gen(function* () { const result = { taskId: task.id, childThreadId: task.childThreadId, - link: formatThreadLink({ - environmentId: scope.environmentId, - threadId: task.childThreadId, - }), + link: formatThreadLink( + { environmentId: scope.environmentId, threadId: task.childThreadId }, + childControls.thread.title, + ), childRunId: childRun?.id ?? null, childNodeId: task.id, status, @@ -1804,7 +1804,10 @@ const make = Effect.gen(function* () { childThreadId: null, ...(task.remoteChild === undefined ? {} - : { remoteChild: task.remoteChild, link: formatThreadLink(task.remoteChild) }), + : { + remoteChild: task.remoteChild, + link: formatThreadLink(task.remoteChild, task.remoteChild.title), + }), childRunId: null, childNodeId: task.id, status, @@ -2992,7 +2995,10 @@ const make = Effect.gen(function* () { ); return { threadId, - link: formatThreadLink({ environmentId: scope.environmentId, threadId }), + link: formatThreadLink( + { environmentId: scope.environmentId, threadId }, + projection.thread.title, + ), runId: run?.id ?? null, status: run?.status ?? "idle", title: projection.thread.title, @@ -3209,7 +3215,7 @@ const make = Effect.gen(function* () { ); return { threadId: input.threadId, - link: threadLinkFor(scope, input.threadId), + link: threadLinkFor(scope, input.threadId, target.thread.title), messageId, runId: result.run.id, ...(result.run.delegatedTaskId === undefined ? {} : { taskId: result.run.delegatedTaskId }), diff --git a/apps/server/src/mcp/toolkits/project/handlers.test.ts b/apps/server/src/mcp/toolkits/project/handlers.test.ts index 2ecbde990d89..b77df3ac2075 100644 --- a/apps/server/src/mcp/toolkits/project/handlers.test.ts +++ b/apps/server/src/mcp/toolkits/project/handlers.test.ts @@ -221,7 +221,7 @@ it.effect( expect(belowCeiling.result).toMatchObject({ projectId: belowCeiling.projectId, modelSelection: belowCeiling.modelSelection, - link: ``, + link: `[Fix](t3-thread://v2/environment/${belowCeiling.launched[0]?.threadId})`, }); expect(belowCeiling.launched[0]?.runtimeMode).toBe("approval-required"); expect(belowCeiling.launched[0]?.initialMessage?.senderThreadId).toBeUndefined(); diff --git a/apps/server/src/mcp/toolkits/project/handlers.ts b/apps/server/src/mcp/toolkits/project/handlers.ts index 667b878e68d3..ab27b6b5bc4b 100644 --- a/apps/server/src/mcp/toolkits/project/handlers.ts +++ b/apps/server/src/mcp/toolkits/project/handlers.ts @@ -277,7 +277,7 @@ export const layer = McpToolAccess.toLayer(ProjectToolkit, { const run = result.projection.runs.find((run) => run.userMessageId === messageId); return { threadId: thread.id, - link: McpInvocationContext.threadLinkFor(scope, thread.id), + link: McpInvocationContext.threadLinkFor(scope, thread.id, thread.title), projectId: thread.projectId, modelSelection: thread.modelSelection, runId: run?.id ?? null, diff --git a/apps/server/src/mcp/toolkits/thread/handlers.ts b/apps/server/src/mcp/toolkits/thread/handlers.ts index f96831243751..32e45b5d0a1c 100644 --- a/apps/server/src/mcp/toolkits/thread/handlers.ts +++ b/apps/server/src/mcp/toolkits/thread/handlers.ts @@ -134,7 +134,7 @@ export const layer = McpToolAccess.toLayer(ThreadToolkit, { return { matches: matches.map((match) => ({ ...match, - link: McpInvocationContext.threadLinkFor(scope, match.threadId), + link: McpInvocationContext.threadLinkFor(scope, match.threadId, match.title), })), }; }), diff --git a/apps/server/src/peer/RemoteDelegation.test.ts b/apps/server/src/peer/RemoteDelegation.test.ts index 317a11251aff..cb46d63ab623 100644 --- a/apps/server/src/peer/RemoteDelegation.test.ts +++ b/apps/server/src/peer/RemoteDelegation.test.ts @@ -827,7 +827,7 @@ it.effect("a task delegated to the box wakes the laptop's parent when it ends th expect(yield* Ref.get(thread.launched)).toBe(delegated.remoteChild.threadId); // Its link opens the thread there, so the parent can mention it like any other. expect(delegated.link).toBe( - ``, + `[Laptop parent](t3-thread://v2/${box.environmentId}/${delegated.remoteChild.threadId})`, ); // ...naming the parent here, so the thread there links back to it. expect(yield* Ref.get(thread.delegatedFrom)).toEqual({ diff --git a/apps/server/src/peer/RemoteDelegation.ts b/apps/server/src/peer/RemoteDelegation.ts index 221043f35103..1222f029eec5 100644 --- a/apps/server/src/peer/RemoteDelegation.ts +++ b/apps/server/src/peer/RemoteDelegation.ts @@ -260,13 +260,14 @@ const make = Effect.gen(function* () { // the forwarder scopes the launch's, and the record's names it. const key = input.clientRequestId ?? (yield* random.uuidV4()); const task = taskPrompt(input); + const title = input.title ?? parent.thread.title; const launched = yield* forwarding.call( scope, ProjectToolkit.tools.t3_thread_launch, target.environmentId, { projectId, - title: input.title ?? parent.thread.title, + title, modelSelection: { instanceId, model, ...(options === undefined ? {} : { options }) }, ...(runtimeMode === undefined ? {} : { runtimeMode }), ...(interactionMode === undefined ? {} : { interactionMode }), @@ -291,6 +292,7 @@ const make = Effect.gen(function* () { environmentId: target.environmentId, threadId: launched.threadId, label: link?.label ?? target.environmentId, + title, ...(launched.runId === null ? {} : { runId: launched.runId }), }; const recorded = yield* threads diff --git a/evals/mcp-tool-use/cases/orchestration.ndjson b/evals/mcp-tool-use/cases/orchestration.ndjson index 41eb03e0939f..e69f14f7cc54 100644 --- a/evals/mcp-tool-use/cases/orchestration.ndjson +++ b/evals/mcp-tool-use/cases/orchestration.ndjson @@ -16,7 +16,7 @@ {"id": "thread-id-not-read", "area": "orchestration", "kind": "bad", "pattern": "did not read a given thread id", "experience": "The user pasted a thread id and asked why 'this t3 code thread' kept mentioning pi. The agent answered from its own context. The user: 'Use the t3 code mcp to get that thread, that's the id lol'.", "rule": "Intended: read a given thread id with t3_thread_read, with a bounded size.", "privacy": "commit-safe", "setup": {"role": "top", "priorTurns": [{"role": "user", "text": "Is the Muse CLI just a fork or wrapper of the Pi agent?"}, {"role": "assistant", "text": "No. The installed Muse CLI is a native Rust binary built from its own source tree. I searched the provider code for 'pi' to check, and found only T3 Code's separate Pi provider docs.", "toolCalls": [{"tool": "Bash", "input": {"command": "rg -il '\\bpi\\b' src/providers"}, "result": "src/providers/pi/README.md"}, {"tool": "Bash", "input": {"command": "file \"$(which muse)\""}, "result": "/usr/local/bin/muse: ELF 64-bit LSB pie executable, x86-64, dynamically linked, Rust toolchain 1.91, stripped"}]}], "mocks": {"t3_thread_read": {"thread": {"threadId": "11111111-1111-4111-8111-111111111111", "title": "Muse CLI integration and ACP support", "link": "", "status": "idle"}, "items": [{"position": 494, "createdBy": "assistant", "text": "Pi already replays recorded transcripts in tests, so the Muse adapter copies that pattern."}], "nextPosition": 495, "hasMore": false}}, "world": {"files": {"src/providers/pi/README.md": "# Pi provider\n\nRuns the Pi agent through its RPC mode.\n", "src/providers/muse/adapter.ts": "export const museAdapter = makeAdapter({ binary: 'muse' });\n"}}}, "prompt": "11111111-1111-4111-8111-111111111111 why does this t3 code thread keep mentioning pi?", "expect": {"calls": [{"tool": "t3_thread_read", "args": {"threadId": "11111111-1111-4111-8111-111111111111"}}]}, "notes": "The prior turn mentions pi, which is what let the agent think 'this thread' meant itself. The same thread then read with limit 100 and got an 85k-char result (see gaps)."} {"id": "thread-mention-without-link-after-read", "area": "orchestration", "kind": "bad", "pattern": "thread mentioned without t3-thread link", "experience": "Asked to explain what went wrong in a given thread, the agent named it by id or title with no t3-thread:// link.", "rule": "To mention another thread to the user, write its `link` verbatim: />, with no title.", "privacy": "commit-safe", "setup": {"role": "top", "mocks": {"t3_thread_read": {"thread": {"threadId": "11111111-1111-4111-8111-111111111111", "title": "Fix ultrafast pricing", "link": "", "status": "idle"}, "items": [{"position": 210, "createdBy": "assistant", "toolName": "t3_thread_send", "text": "t3_thread_send threadId=44444444-4444-4444-8444-000000000001 message='Round 2. Read only, as before...'"}, {"position": 214, "createdBy": "user", "text": "Use delegate_task please, this should not be a separate thread."}], "nextPosition": 215, "hasMore": false}}}, "prompt": "11111111-1111-4111-8111-111111111111 This thread used the wrong tool. Figure out why.", "expect": {"calls": [{"tool": "t3_thread_read", "args": {"threadId": "11111111-1111-4111-8111-111111111111"}}], "textMust": ["t3-thread://v2/eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee/11111111-1111-4111-8111-111111111111"], "textMustNot": ["t3-thread://v1/"]}, "notes": "Real agent was Codex before the 10-21 untruncation fix, when the link rule often did not reach Codex. May pass on Claude today; keep as a regression check for the rewrite.", "maxSteps": 10} {"id": "found-thread-mentioned-without-link", "area": "orchestration", "kind": "bad", "pattern": "thread mentioned without t3-thread link", "experience": "The agent found yesterday's thread by search and told the user where they left off, but did not link the thread.", "rule": "To mention another thread to the user, write its `link` verbatim: />, with no title.", "privacy": "commit-safe", "setup": {"role": "top", "mocks": {"t3_thread_search": {"matches": [{"threadId": "11111111-1111-4111-8111-111111111111", "projectId": "22222222-2222-4222-8222-222222222222", "title": "Fix macOS camera permission prompt", "snippet": "...the app never asks for camera permission on mac...", "messageCreatedAt": "2026-10-19T21:04:00Z"}]}, "t3_thread_list": {"threads": [{"threadId": "11111111-1111-4111-8111-111111111111", "title": "Fix macOS camera permission prompt", "status": "idle"}]}, "t3_thread_read": {"thread": {"threadId": "11111111-1111-4111-8111-111111111111", "title": "Fix macOS camera permission prompt", "link": "", "status": "idle"}, "items": [{"position": 88, "createdBy": "assistant", "text": "The fix is committed locally on branch fix/camera-entitlement in another clone. No PR is open yet."}], "nextPosition": 89, "hasMore": false}}, "shellMocks": [{"match": "gh pr list", "output": ""}]}, "prompt": "Did I ever make a PR to fix the issue where the app couldn't request camera permission on mac? I had a thread about it yesterday and forgot where we left off.", "expect": {"textMust": ["t3-thread://v2/eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee/11111111-1111-4111-8111-111111111111"], "textMustNot": ["t3-thread://v1/"]}, "notes": "Also seen in another thread ('Found the prior audits (Jul 21, Sep 1, Oct 3 threads ...)' with no links). Original prompt also said 'get it fixed full stop'; dropped to keep the case small."} -{"id": "thread-link-rewritten-with-title", "area": "orchestration", "kind": "bad", "pattern": "thread link rewritten with a title", "experience": "Thread links carried the title the agent wrote, which went stale once the user renamed the thread. The app already shows each linked thread's current title, so maintainers asked that agents write only the stable link.", "rule": "To mention another thread to the user, write its `link` verbatim: />, with no title.", "privacy": "commit-safe", "setup": {"role": "top", "mocks": {"t3_thread_list": {"threads": [{"threadId": "11111111-1111-4111-8111-111111111111", "title": "Fix flaky checkout test", "status": "running"}, {"threadId": "44444444-4444-4444-8444-000000000002", "title": "Upgrade Vite", "status": "idle"}]}}}, "prompt": "Which of my threads in this project are still running?", "expect": {"calls": [{"tool": "t3_thread_list"}], "textMust": [""], "textMustNot": ["\\]\\(t3-thread://", "t3-thread://v1/"]}, "notes": "Source: maintainer request with the v2 thread link change. The old instructions carried a titled [title](t3-thread://v1/) template for threads without a link."} +{"id": "thread-link-rewritten-with-title", "area": "orchestration", "kind": "bad", "pattern": "thread link rewritten with a title", "experience": "Thread links carried the title the agent wrote, which went stale once the user renamed the thread. The app already shows each linked thread's current title, so maintainers asked that agents write only the stable link.", "rule": "To mention another thread to the user, write its `link` verbatim, never a title of its own.", "privacy": "commit-safe", "setup": {"role": "top", "mocks": {"t3_thread_list": {"threads": [{"threadId": "11111111-1111-4111-8111-111111111111", "title": "Fix flaky checkout test", "status": "running"}]}, "t3_thread_read": {"thread": {"threadId": "11111111-1111-4111-8111-111111111111", "title": "Fix flaky checkout test", "status": "running"}, "items": [], "nextPosition": 1, "hasMore": false}}}, "prompt": "Which of my threads in this project are still running?", "expect": {"calls": [{"tool": "t3_thread_list"}], "textMust": ["\\[Fix flaky checkout test\\]\\(t3-thread://v2/eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee/11111111-1111-4111-8111-111111111111\\)"], "textMustNot": ["t3-thread://v1/"]}, "notes": "Source: maintainer request with the v2 thread link change. The old instructions carried a titled [title](t3-thread://v1/) template for threads without a link. The link T3 returns carries the title it knew, so a client without that thread's machine still names it; a client that knows the thread shows its current title instead."} {"id": "cross-provider-delegates-with-options", "area": "orchestration", "kind": "guard", "pattern": "capabilities then cross-provider delegate_task with options", "experience": "Asked for Sol 'high' and Muse Wonder Full 'xhigh' delegates, the agent read capabilities and put effort in target.options.", "rule": "Read the selected model's descriptors from orchestrator_capabilities and put settings in target.options, not only in the prompt.", "privacy": "commit-safe", "setup": {"role": "top", "mocks": {"orchestrator_capabilities": {"parentThreadId": "33333333-3333-4333-8333-333333333333", "inheritedProviderInstanceId": "claudeAgent", "inheritedModel": "claude-sonnet-5-5", "inheritedOptions": [{"id": "effort", "value": "high"}], "runtimeMode": "full-access", "interactionMode": "default", "features": {"appOwnedSubagents": true, "asyncPolling": true, "cancellation": true, "batchThreadCreation": true, "threadManagement": true, "incrementalThreadRead": true, "scheduledTasks": true}, "providers": [{"providerInstanceId": "claudeAgent", "driverKind": "claudeAgent", "displayName": "Claude", "models": [{"id": "claude-opus-5-5", "label": "Claude Opus 5.5", "options": [{"id": "effort", "values": ["low", "medium", "high", "xhigh", "max"], "default": "high"}, {"id": "fastMode", "type": "boolean", "default": false}]}, {"id": "claude-sonnet-5-5", "label": "Claude Sonnet 5.5", "options": [{"id": "effort", "values": ["low", "medium", "high", "xhigh", "max"], "default": "high"}]}, {"id": "claude-haiku-5-5", "label": "Claude Haiku 5.5", "options": [{"id": "effort", "values": ["low", "medium", "high", "xhigh", "max"], "default": "high"}]}]}, {"providerInstanceId": "codex", "driverKind": "codex", "displayName": "Codex", "models": [{"id": "gpt-6.1-sol", "label": "GPT-6.1 Sol", "options": [{"id": "reasoningEffort", "values": ["low", "medium", "high", "xhigh", "max"], "default": "high"}, {"id": "serviceTier", "values": ["default", "priority"], "default": "default"}]}]}, {"providerInstanceId": "muse", "driverKind": "muse", "displayName": "Muse Code", "runtimeModes": ["approval-required", "full-access"], "models": [{"id": "muse-wonder-full", "label": "Muse Wonder Full", "options": [{"id": "reasoningEffort", "values": ["minimal", "low", "medium", "high", "xhigh", "max"], "default": "high"}]}]}, {"providerInstanceId": "grok", "driverKind": "grok", "displayName": "Grok", "models": [{"id": "grok-4.7", "label": "Grok 4.7", "options": []}]}]}, "delegate_task": "default"}}, "prompt": "I want this codebase reviewed by two different models. Spin up a delegate for GPT-6.1 Sol high in Codex and one for Muse Wonder Full at xhigh. Tell them both to explore the codebase and find interesting things, plus some easy wins.", "expect": {"order": ["orchestrator_capabilities", "delegate_task"], "calls": [{"tool": "delegate_task", "args": {"target.providerInstanceId": "codex", "target.model": "gpt-6.1-sol", "target.options.reasoningEffort": "high"}}, {"tool": "delegate_task", "args": {"target.providerInstanceId": "muse", "target.model": "muse-wonder-full", "target.options.reasoningEffort": "xhigh"}}], "notCalls": ["t3_thread_launch", "create_threads", "Agent"]}, "notes": "target.options may be an object or an [{id,value}] array. The runner must normalize arrays to an object before matching 'target.options.'."} {"id": "same-provider-delegate-with-effort", "area": "orchestration", "kind": "guard", "pattern": "model options force delegate_task over native", "experience": "Asked for an Opus 5.5 (xhigh) delegate from a Claude parent, the agent used delegate_task with target.options effort xhigh.", "rule": "When model options are requested ... use delegate_task even for native-supported models. Native subagents cannot reliably apply T3 model options.", "privacy": "commit-safe", "setup": {"role": "top", "mocks": {"orchestrator_capabilities": {"parentThreadId": "33333333-3333-4333-8333-333333333333", "inheritedProviderInstanceId": "claudeAgent", "inheritedModel": "claude-sonnet-5-5", "inheritedOptions": [{"id": "effort", "value": "high"}], "runtimeMode": "full-access", "interactionMode": "default", "features": {"appOwnedSubagents": true, "asyncPolling": true, "cancellation": true, "batchThreadCreation": true, "threadManagement": true, "incrementalThreadRead": true, "scheduledTasks": true}, "providers": [{"providerInstanceId": "claudeAgent", "driverKind": "claudeAgent", "displayName": "Claude", "models": [{"id": "claude-opus-5-5", "label": "Claude Opus 5.5", "options": [{"id": "effort", "values": ["low", "medium", "high", "xhigh", "max"], "default": "high"}, {"id": "fastMode", "type": "boolean", "default": false}]}, {"id": "claude-sonnet-5-5", "label": "Claude Sonnet 5.5", "options": [{"id": "effort", "values": ["low", "medium", "high", "xhigh", "max"], "default": "high"}]}, {"id": "claude-haiku-5-5", "label": "Claude Haiku 5.5", "options": [{"id": "effort", "values": ["low", "medium", "high", "xhigh", "max"], "default": "high"}]}]}, {"providerInstanceId": "codex", "driverKind": "codex", "displayName": "Codex", "models": [{"id": "gpt-6.1-sol", "label": "GPT-6.1 Sol", "options": [{"id": "reasoningEffort", "values": ["low", "medium", "high", "xhigh", "max"], "default": "high"}, {"id": "serviceTier", "values": ["default", "priority"], "default": "default"}]}]}, {"providerInstanceId": "muse", "driverKind": "muse", "displayName": "Muse Code", "runtimeModes": ["approval-required", "full-access"], "models": [{"id": "muse-wonder-full", "label": "Muse Wonder Full", "options": [{"id": "reasoningEffort", "values": ["minimal", "low", "medium", "high", "xhigh", "max"], "default": "high"}]}]}, {"providerInstanceId": "grok", "driverKind": "grok", "displayName": "Grok", "models": [{"id": "grok-4.7", "label": "Grok 4.7", "options": []}]}]}, "delegate_task": "default"}}, "prompt": "Spin up an Opus 5.5 delegate in Claude Code on xhigh to research why the update flow spams 'connected successfully' toasts. Have it write up the causes in priority order.", "expect": {"calls": [{"tool": "delegate_task", "args": {"target.providerInstanceId": "claudeAgent", "target.model": "claude-opus-5-5", "target.options.effort": "xhigh"}}], "notCalls": ["Agent", "Workflow"]}, "notes": "A shorter text that drops the options rule could send this to the native Agent tool with model 'opus' and lose xhigh."} {"id": "async-delegate-then-end-turn", "area": "orchestration", "kind": "guard", "pattern": "async delegate then end turn", "experience": "After starting an async Sol review the agent said its completion would wake the thread and ended the turn.", "rule": "Prefer mode='async' for long work ... the task's completion still wakes this thread. (intended: end the turn; no sleep or task_status loops)", "privacy": "commit-safe", "setup": {"role": "top", "mocks": {"orchestrator_capabilities": {"parentThreadId": "33333333-3333-4333-8333-333333333333", "inheritedProviderInstanceId": "claudeAgent", "inheritedModel": "claude-sonnet-5-5", "inheritedOptions": [{"id": "effort", "value": "high"}], "runtimeMode": "full-access", "interactionMode": "default", "features": {"appOwnedSubagents": true, "asyncPolling": true, "cancellation": true, "batchThreadCreation": true, "threadManagement": true, "incrementalThreadRead": true, "scheduledTasks": true}, "providers": [{"providerInstanceId": "claudeAgent", "driverKind": "claudeAgent", "displayName": "Claude", "models": [{"id": "claude-opus-5-5", "label": "Claude Opus 5.5", "options": [{"id": "effort", "values": ["low", "medium", "high", "xhigh", "max"], "default": "high"}, {"id": "fastMode", "type": "boolean", "default": false}]}, {"id": "claude-sonnet-5-5", "label": "Claude Sonnet 5.5", "options": [{"id": "effort", "values": ["low", "medium", "high", "xhigh", "max"], "default": "high"}]}, {"id": "claude-haiku-5-5", "label": "Claude Haiku 5.5", "options": [{"id": "effort", "values": ["low", "medium", "high", "xhigh", "max"], "default": "high"}]}]}, {"providerInstanceId": "codex", "driverKind": "codex", "displayName": "Codex", "models": [{"id": "gpt-6.1-sol", "label": "GPT-6.1 Sol", "options": [{"id": "reasoningEffort", "values": ["low", "medium", "high", "xhigh", "max"], "default": "high"}, {"id": "serviceTier", "values": ["default", "priority"], "default": "default"}]}]}, {"providerInstanceId": "muse", "driverKind": "muse", "displayName": "Muse Code", "runtimeModes": ["approval-required", "full-access"], "models": [{"id": "muse-wonder-full", "label": "Muse Wonder Full", "options": [{"id": "reasoningEffort", "values": ["minimal", "low", "medium", "high", "xhigh", "max"], "default": "high"}]}]}, {"providerInstanceId": "grok", "driverKind": "grok", "displayName": "Grok", "models": [{"id": "grok-4.7", "label": "Grok 4.7", "options": []}]}]}, "delegate_task": "default", "task_status": "default"}, "world": {"branch": "fix/session-stop", "commits": [{"sha": "3e8d1f0", "subject": "fix(server): stop clears an orphaned task row"}], "diff": "diff --git a/src/session.ts b/src/session.ts\n--- a/src/session.ts\n+++ b/src/session.ts\n@@ -10,1 +10,1 @@\n- if (task.status !== \"running\") return;\n+ if (task.status === \"cancelled\") return emitTaskRow(task);\n", "prs": [{"number": 123, "title": "fix(server): stop clears an orphaned task row", "headRefName": "fix/session-stop"}]}}, "prompt": "Have a GPT-6.1 Sol delegate review the diff on this branch (https://github.com/acme/app/pull/123) before I merge it.", "expect": {"calls": [{"tool": "delegate_task", "args": {"target.model": "gpt-6.1-sol"}}], "maxCalls": {"task_status": 0, "t3_thread_wait": 0}, "shellMustNot": ["\\bsleep\\b"]}, "notes": ""} @@ -25,7 +25,7 @@ {"id": "opus-review-too-via-delegate", "area": "orchestration", "kind": "guard", "pattern": "same-provider review via delegate_task", "experience": "'Have Opus review the things too' from a Claude parent with no extra settings runs a native Opus subagent the parent waits for.", "rule": "Use your native tool when the child uses your provider, a model it offers, and no settings beyond yours, and you need it only in this turn.", "privacy": "commit-safe", "setup": {"role": "top", "priorTurns": [{"role": "user", "text": "Get a Sol review on https://github.com/acme/app/pull/123 before we file anything else."}, {"role": "assistant", "text": "Sol approved PR 123 after one fix to its description.", "toolCalls": [{"tool": "delegate_task", "input": {"title": "Sol review PR 123", "mode": "async", "clientRequestId": "sol-123-r1", "target": {"providerInstanceId": "codex", "model": "gpt-6.1-sol", "options": {"reasoningEffort": "high"}}, "task": "Review https://github.com/acme/app/pull/123. Read only."}, "result": {"taskId": "task-1", "childThreadId": "44444444-4444-4444-8444-000000000001", "childRunId": "run-child-1", "status": "running", "workState": "working", "providerInstanceId": "codex", "model": "gpt-6.1-sol"}}, {"tool": "task_status", "input": {"taskId": "task-1"}, "result": {"taskId": "task-1", "childThreadId": "44444444-4444-4444-8444-000000000001", "status": "completed", "workState": "result_available", "summary": "APPROVE after S1 (description wording)."}}]}], "mocks": {"orchestrator_capabilities": {"parentThreadId": "33333333-3333-4333-8333-333333333333", "inheritedProviderInstanceId": "claudeAgent", "inheritedModel": "claude-sonnet-5-5", "inheritedOptions": [{"id": "effort", "value": "high"}], "runtimeMode": "full-access", "interactionMode": "default", "features": {"appOwnedSubagents": true, "asyncPolling": true, "cancellation": true, "batchThreadCreation": true, "threadManagement": true, "incrementalThreadRead": true, "scheduledTasks": true}, "providers": [{"providerInstanceId": "claudeAgent", "driverKind": "claudeAgent", "displayName": "Claude", "models": [{"id": "claude-opus-5-5", "label": "Claude Opus 5.5", "options": [{"id": "effort", "values": ["low", "medium", "high", "xhigh", "max"], "default": "high"}, {"id": "fastMode", "type": "boolean", "default": false}]}, {"id": "claude-sonnet-5-5", "label": "Claude Sonnet 5.5", "options": [{"id": "effort", "values": ["low", "medium", "high", "xhigh", "max"], "default": "high"}]}, {"id": "claude-haiku-5-5", "label": "Claude Haiku 5.5", "options": [{"id": "effort", "values": ["low", "medium", "high", "xhigh", "max"], "default": "high"}]}]}, {"providerInstanceId": "codex", "driverKind": "codex", "displayName": "Codex", "models": [{"id": "gpt-6.1-sol", "label": "GPT-6.1 Sol", "options": [{"id": "reasoningEffort", "values": ["low", "medium", "high", "xhigh", "max"], "default": "high"}, {"id": "serviceTier", "values": ["default", "priority"], "default": "default"}]}]}, {"providerInstanceId": "muse", "driverKind": "muse", "displayName": "Muse Code", "runtimeModes": ["approval-required", "full-access"], "models": [{"id": "muse-wonder-full", "label": "Muse Wonder Full", "options": [{"id": "reasoningEffort", "values": ["minimal", "low", "medium", "high", "xhigh", "max"], "default": "high"}]}]}, {"providerInstanceId": "grok", "driverKind": "grok", "displayName": "Grok", "models": [{"id": "grok-4.7", "label": "Grok 4.7", "options": []}]}]}, "delegate_task": "default"}, "world": {"prs": [{"number": 123, "title": "fix(web): keep scroll position on thread switch", "headRefName": "fix/scroll"}]}, "harness": "claude"}, "prompt": "Have Opus review the things too, not just Sol", "expect": {"calls": [{"tool": "Agent", "args": {"model": "~opus"}}], "notCalls": ["delegate_task", "t3_thread_send"], "callsMustNot": [{"tool": "Agent", "args": {"run_in_background": true}}]}, "notes": "T3's 'prefer native for same-provider' line pulls toward the native Agent tool here; this stayed correct in the real thread."} {"id": "delegates-then-new-thread-with-model", "area": "orchestration", "kind": "guard", "pattern": "new thread asked: t3_thread_launch with model", "experience": "'Spin up 2 Opus delegates ... then start a new thread using Grok 4.7 to implement' gave 2 delegate_task calls and 1 t3_thread_launch on Grok.", "rule": "t3_thread_launch and create_threads ... Use them only when the user explicitly asks for separate/new/top-level threads.", "privacy": "commit-safe", "setup": {"role": "top", "priorTurns": [{"role": "user", "text": "Spin up 2 Opus 5.5 delegates to explore performance wins in this app."}, {"role": "assistant", "text": "Both Opus delegates are done.\n- Client: the thread list re-renders every row on each streamed token.\n- Server: each event write runs a full projection rebuild.", "toolCalls": [{"tool": "delegate_task", "input": {"title": "Opus: client performance", "mode": "wait", "clientRequestId": "perf-client", "target": {"providerInstanceId": "claudeAgent", "model": "claude-opus-5-5"}, "task": "Find concrete client performance wins. Read only."}, "result": {"taskId": "task-1", "childThreadId": "44444444-4444-4444-8444-000000000001", "childRunId": "run-child-1", "status": "completed", "workState": "result_available", "providerInstanceId": "claudeAgent", "model": "claude-opus-5-5", "summary": "Client: the thread list re-renders every row on each streamed token. Memoize rows by thread id and select only the active thread's stream."}}, {"tool": "delegate_task", "input": {"title": "Opus: server performance", "mode": "wait", "clientRequestId": "perf-server", "target": {"providerInstanceId": "claudeAgent", "model": "claude-opus-5-5"}, "task": "Find concrete server performance wins. Read only."}, "result": {"taskId": "task-2", "childThreadId": "44444444-4444-4444-8444-000000000002", "childRunId": "run-child-2", "status": "completed", "workState": "result_available", "providerInstanceId": "claudeAgent", "model": "claude-opus-5-5", "summary": "Server: each event write runs a full projection rebuild. Apply events to the projection incrementally."}}]}], "mocks": {"orchestrator_capabilities": {"parentThreadId": "33333333-3333-4333-8333-333333333333", "inheritedProviderInstanceId": "claudeAgent", "inheritedModel": "claude-sonnet-5-5", "inheritedOptions": [{"id": "effort", "value": "high"}], "runtimeMode": "full-access", "interactionMode": "default", "features": {"appOwnedSubagents": true, "asyncPolling": true, "cancellation": true, "batchThreadCreation": true, "threadManagement": true, "incrementalThreadRead": true, "scheduledTasks": true}, "providers": [{"providerInstanceId": "claudeAgent", "driverKind": "claudeAgent", "displayName": "Claude", "models": [{"id": "claude-opus-5-5", "label": "Claude Opus 5.5", "options": [{"id": "effort", "values": ["low", "medium", "high", "xhigh", "max"], "default": "high"}, {"id": "fastMode", "type": "boolean", "default": false}]}, {"id": "claude-sonnet-5-5", "label": "Claude Sonnet 5.5", "options": [{"id": "effort", "values": ["low", "medium", "high", "xhigh", "max"], "default": "high"}]}, {"id": "claude-haiku-5-5", "label": "Claude Haiku 5.5", "options": [{"id": "effort", "values": ["low", "medium", "high", "xhigh", "max"], "default": "high"}]}]}, {"providerInstanceId": "codex", "driverKind": "codex", "displayName": "Codex", "models": [{"id": "gpt-6.1-sol", "label": "GPT-6.1 Sol", "options": [{"id": "reasoningEffort", "values": ["low", "medium", "high", "xhigh", "max"], "default": "high"}, {"id": "serviceTier", "values": ["default", "priority"], "default": "default"}]}]}, {"providerInstanceId": "muse", "driverKind": "muse", "displayName": "Muse Code", "runtimeModes": ["approval-required", "full-access"], "models": [{"id": "muse-wonder-full", "label": "Muse Wonder Full", "options": [{"id": "reasoningEffort", "values": ["minimal", "low", "medium", "high", "xhigh", "max"], "default": "high"}]}]}, {"providerInstanceId": "grok", "driverKind": "grok", "displayName": "Grok", "models": [{"id": "grok-4.7", "label": "Grok 4.7", "options": []}]}]}, "t3_thread_launch": {"threadId": "11111111-1111-4111-8111-111111111111", "projectId": "22222222-2222-4222-8222-222222222222", "modelSelection": {"instanceId": "grok", "model": "grok-4.7"}, "status": "starting"}}}, "prompt": "Summarize their thoughts, then start a new thread using Grok 4.7 to implement your findings.", "expect": {"calls": [{"tool": "t3_thread_launch", "args": {"modelSelection.model": "~grok"}}], "notCalls": ["delegate_task", "create_threads"]}, "notes": "Pattern 2 (new thread asked but delegate used) had only 1-2 real cases in 40 asks, so this is a guard. Check the arg path against the current t3_thread_launch schema."} {"id": "subagent-ask-no-new-threads", "area": "orchestration", "kind": "guard", "pattern": "subagent asked: no t3_thread_launch/create_threads", "experience": "'Have Haiku 5.5 subagents do the exploration' must give child tasks, not top-level threads. 0 of 98 subagent asks went wrong, and the user still warns about it.", "rule": "Never use them merely because the user said \"subagent\" or requested parallel delegated work.", "privacy": "commit-safe", "setup": {"role": "top", "mocks": {"orchestrator_capabilities": {"parentThreadId": "33333333-3333-4333-8333-333333333333", "inheritedProviderInstanceId": "claudeAgent", "inheritedModel": "claude-sonnet-5-5", "inheritedOptions": [{"id": "effort", "value": "high"}], "runtimeMode": "full-access", "interactionMode": "default", "features": {"appOwnedSubagents": true, "asyncPolling": true, "cancellation": true, "batchThreadCreation": true, "threadManagement": true, "incrementalThreadRead": true, "scheduledTasks": true}, "providers": [{"providerInstanceId": "claudeAgent", "driverKind": "claudeAgent", "displayName": "Claude", "models": [{"id": "claude-opus-5-5", "label": "Claude Opus 5.5", "options": [{"id": "effort", "values": ["low", "medium", "high", "xhigh", "max"], "default": "high"}, {"id": "fastMode", "type": "boolean", "default": false}]}, {"id": "claude-sonnet-5-5", "label": "Claude Sonnet 5.5", "options": [{"id": "effort", "values": ["low", "medium", "high", "xhigh", "max"], "default": "high"}]}, {"id": "claude-haiku-5-5", "label": "Claude Haiku 5.5", "options": [{"id": "effort", "values": ["low", "medium", "high", "xhigh", "max"], "default": "high"}]}]}, {"providerInstanceId": "codex", "driverKind": "codex", "displayName": "Codex", "models": [{"id": "gpt-6.1-sol", "label": "GPT-6.1 Sol", "options": [{"id": "reasoningEffort", "values": ["low", "medium", "high", "xhigh", "max"], "default": "high"}, {"id": "serviceTier", "values": ["default", "priority"], "default": "default"}]}]}, {"providerInstanceId": "muse", "driverKind": "muse", "displayName": "Muse Code", "runtimeModes": ["approval-required", "full-access"], "models": [{"id": "muse-wonder-full", "label": "Muse Wonder Full", "options": [{"id": "reasoningEffort", "values": ["minimal", "low", "medium", "high", "xhigh", "max"], "default": "high"}]}]}, {"providerInstanceId": "grok", "driverKind": "grok", "displayName": "Grok", "models": [{"id": "grok-4.7", "label": "Grok 4.7", "options": []}]}]}, "delegate_task": "default"}, "world": {"files": {"package.json": "{\n \"name\": \"app\",\n \"private\": true,\n \"packageManager\": \"pnpm@10.4.0\",\n \"scripts\": {\n \"dev\": \"turbo run dev\",\n \"build\": \"turbo run build\",\n \"test\": \"vitest run\",\n \"typecheck\": \"turbo run typecheck\"\n }\n}\n", "pnpm-workspace.yaml": "packages:\n - apps/*\n - packages/*\n", "turbo.json": "{\n \"tasks\": {\n \"build\": { \"outputs\": [\"dist/**\"] },\n \"typecheck\": {},\n \"dev\": { \"cache\": false }\n }\n}\n", "apps/web/package.json": "{ \"name\": \"@app/web\", \"scripts\": { \"dev\": \"vite\", \"build\": \"vite build\" } }\n", "apps/server/package.json": "{ \"name\": \"@app/server\", \"scripts\": { \"dev\": \"tsx watch src/index.ts\", \"build\": \"tsc\" } }\n", ".github/workflows/ci.yml": "name: ci\non: [push, pull_request]\njobs:\n test:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n - run: pnpm install --frozen-lockfile\n - run: pnpm typecheck\n - run: pnpm test\n", "scripts/seed-dev-db.ts": "import { db } from '../apps/server/src/db';\n\n// Seeds the dev database with 20 fake users.\nfor (let i = 0; i < 20; i++) await db.insert('users', { name: `user${i}` });\n", "apps/web/src/main.tsx": "import { createRoot } from 'react-dom/client';\nimport { App } from './App';\n\ncreateRoot(document.getElementById('root')!).render();\n", "apps/web/src/App.tsx": "export function App() {\n return ;\n}\n", "apps/server/src/index.ts": "import { createServer } from './server';\n\ncreateServer().listen(Number(process.env.PORT ?? 3773));\n", "apps/server/src/db.ts": "import { Database } from 'better-sqlite3';\n\nexport const db = new Database(process.env.DB_PATH ?? 'dev.sqlite');\n"}}}, "prompt": "I want your honest thoughts on this project. Do a deep dive and really read through it so you know it well. Have Haiku 5.5 subagents do the exploration.", "expect": {"notCalls": ["t3_thread_launch", "create_threads"]}, "notes": "Narrow on purpose: native vs delegate is graded in the bad cases. The user's preemptive guard: 'Please don't make new threads with the T3 Code MCP, only create delegates, workflows, subagents etc.'."} -{"id": "thread-send-to-named-top-level-thread", "area": "orchestration", "kind": "guard", "pattern": "t3_thread_send to a top-level thread the user names", "experience": "Given another top-level thread id to talk to, the agent used t3_thread_send on it and linked it in the reply.", "rule": "To mention another thread to the user, write its `link` verbatim: />, with no title.", "privacy": "commit-safe", "setup": {"role": "top", "priorTurns": [{"role": "user", "text": "The other agent disagreed:\n\nTheirs is the better plan to execute. Mine is the wider survey. Start from theirs and graft three items from mine.\n\nTheir 5 points: 1) cache tsc output per package, 2) split the web typecheck into two projects, 3) drop the duplicate lint step in CI, 4) run typecheck only for changed packages in CI (memo), 5) measure before and after on main."}, {"role": "assistant", "text": "We now agree on almost everything. I accept points 1, 2, 3, and 5. I disagree with one detail of the memo in point 4: CI must also typecheck the packages that depend on a changed package, not only the changed ones. From my plan I would graft: project references, a warm tsc cache restored in CI, and a per-package timing report."}], "mocks": {"t3_thread_send": {"threadId": "11111111-1111-4111-8111-111111111111", "messageId": "msg-1", "runId": "run-1", "queued": true}, "t3_thread_read": {"thread": {"threadId": "11111111-1111-4111-8111-111111111111", "title": "Faster type checking", "link": "", "status": "idle"}, "items": [{"position": 41, "createdBy": "assistant", "text": "Plan: 1) cache tsc output per package, 2) split the web typecheck, 3) drop the duplicate lint step, 4) memo: typecheck only changed packages in CI, 5) measure on main."}, {"position": 42, "createdBy": "user", "text": "Another agent has a wider survey. I'll bring its answer back here."}], "nextPosition": 43, "hasMore": false}}}, "prompt": "That thread is on this machine, you can talk to it directly with t3 code: 11111111-1111-4111-8111-111111111111", "expect": {"calls": [{"tool": "t3_thread_send", "args": {"threadId": "11111111-1111-4111-8111-111111111111"}}], "notCalls": ["delegate_task", "t3_thread_launch"], "textMust": [""], "callsMustNot": [{"tool": "t3_thread_send", "args": {"freshContext": true}}]}, "notes": "Keeps a rewrite from over-banning t3_thread_send. The ban is only for delegated childThreadIds. An ordinary send keeps the thread's context."} +{"id": "thread-send-to-named-top-level-thread", "area": "orchestration", "kind": "guard", "pattern": "t3_thread_send to a top-level thread the user names", "experience": "Given another top-level thread id to talk to, the agent used t3_thread_send on it and linked it in the reply.", "rule": "To mention another thread to the user, write its `link` verbatim, never a title of its own.", "privacy": "commit-safe", "setup": {"role": "top", "priorTurns": [{"role": "user", "text": "The other agent disagreed:\n\nTheirs is the better plan to execute. Mine is the wider survey. Start from theirs and graft three items from mine.\n\nTheir 5 points: 1) cache tsc output per package, 2) split the web typecheck into two projects, 3) drop the duplicate lint step in CI, 4) run typecheck only for changed packages in CI (memo), 5) measure before and after on main."}, {"role": "assistant", "text": "We now agree on almost everything. I accept points 1, 2, 3, and 5. I disagree with one detail of the memo in point 4: CI must also typecheck the packages that depend on a changed package, not only the changed ones. From my plan I would graft: project references, a warm tsc cache restored in CI, and a per-package timing report."}], "mocks": {"t3_thread_send": {"threadId": "11111111-1111-4111-8111-111111111111", "messageId": "msg-1", "runId": "run-1", "queued": true, "link": "[Faster type checking](t3-thread://v2/eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee/11111111-1111-4111-8111-111111111111)"}, "t3_thread_read": {"thread": {"threadId": "11111111-1111-4111-8111-111111111111", "title": "Faster type checking", "link": "[Faster type checking](t3-thread://v2/eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee/11111111-1111-4111-8111-111111111111)", "status": "idle"}, "items": [{"position": 41, "createdBy": "assistant", "text": "Plan: 1) cache tsc output per package, 2) split the web typecheck, 3) drop the duplicate lint step, 4) memo: typecheck only changed packages in CI, 5) measure on main."}, {"position": 42, "createdBy": "user", "text": "Another agent has a wider survey. I'll bring its answer back here."}], "nextPosition": 43, "hasMore": false}}}, "prompt": "That thread is on this machine, you can talk to it directly with t3 code: 11111111-1111-4111-8111-111111111111", "expect": {"calls": [{"tool": "t3_thread_send", "args": {"threadId": "11111111-1111-4111-8111-111111111111"}}], "notCalls": ["delegate_task", "t3_thread_launch"], "textMust": ["\\[Faster type checking\\]\\(t3-thread://v2/eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee/11111111-1111-4111-8111-111111111111\\)"], "callsMustNot": [{"tool": "t3_thread_send", "args": {"freshContext": true}}]}, "notes": "Keeps a rewrite from over-banning t3_thread_send. The ban is only for delegated childThreadIds. An ordinary send keeps the thread's context."} {"id": "too-many-delegates-cancel-extras", "area": "orchestration", "kind": "guard", "pattern": "volume complaint: cancel, do not add", "experience": "After 'You're doing too much man, we have like 6+ delegates going', the agent stopped the stale review tasks and made the next fix without a new round.", "rule": "none (task_cancel description covers the action)", "privacy": "commit-safe", "setup": {"role": "top", "priorTurns": [{"role": "user", "text": "Take over https://github.com/acme/app/pull/123. Have Sol and Opus delegates audit it hard."}, {"role": "assistant", "text": "Five audits are running on the PR: three Sol (error handling, concurrency, API surface) and two Opus (tests, migration safety).", "toolCalls": [{"tool": "delegate_task", "input": {"title": "Sol audit: error handling", "mode": "async", "clientRequestId": "pr123-sol-error", "target": {"providerInstanceId": "codex", "model": "gpt-6.1-sol", "options": {"reasoningEffort": "high"}}, "task": "Audit https://github.com/acme/app/pull/123 for error handling problems. Report each with file:line. Read only."}, "result": {"taskId": "task-1", "childThreadId": "44444444-4444-4444-8444-000000000001", "childRunId": "run-child-1", "status": "running", "workState": "working", "providerInstanceId": "codex", "model": "gpt-6.1-sol"}}, {"tool": "delegate_task", "input": {"title": "Sol audit: concurrency", "mode": "async", "clientRequestId": "pr123-sol-concurrency", "target": {"providerInstanceId": "codex", "model": "gpt-6.1-sol", "options": {"reasoningEffort": "high"}}, "task": "Audit https://github.com/acme/app/pull/123 for concurrency problems. Report each with file:line. Read only."}, "result": {"taskId": "task-2", "childThreadId": "44444444-4444-4444-8444-000000000002", "childRunId": "run-child-2", "status": "running", "workState": "working", "providerInstanceId": "codex", "model": "gpt-6.1-sol"}}, {"tool": "delegate_task", "input": {"title": "Sol audit: API surface", "mode": "async", "clientRequestId": "pr123-sol-api", "target": {"providerInstanceId": "codex", "model": "gpt-6.1-sol", "options": {"reasoningEffort": "high"}}, "task": "Audit https://github.com/acme/app/pull/123 for API surface problems. Report each with file:line. Read only."}, "result": {"taskId": "task-3", "childThreadId": "44444444-4444-4444-8444-000000000003", "childRunId": "run-child-3", "status": "running", "workState": "working", "providerInstanceId": "codex", "model": "gpt-6.1-sol"}}, {"tool": "delegate_task", "input": {"title": "Opus audit: tests", "mode": "async", "clientRequestId": "pr123-opus-tests", "target": {"providerInstanceId": "claudeAgent", "model": "claude-opus-5-5"}, "task": "Audit https://github.com/acme/app/pull/123 for tests problems. Report each with file:line. Read only."}, "result": {"taskId": "task-4", "childThreadId": "44444444-4444-4444-8444-000000000004", "childRunId": "run-child-4", "status": "running", "workState": "working", "providerInstanceId": "claudeAgent", "model": "claude-opus-5-5"}}, {"tool": "delegate_task", "input": {"title": "Opus audit: migration safety", "mode": "async", "clientRequestId": "pr123-opus-migration", "target": {"providerInstanceId": "claudeAgent", "model": "claude-opus-5-5"}, "task": "Audit https://github.com/acme/app/pull/123 for migration safety problems. Report each with file:line. Read only."}, "result": {"taskId": "task-5", "childThreadId": "44444444-4444-4444-8444-000000000005", "childRunId": "run-child-5", "status": "running", "workState": "working", "providerInstanceId": "claudeAgent", "model": "claude-opus-5-5"}}]}], "mocks": {"task_status": {"status": "running", "workState": "working"}, "task_cancel": {"status": "cancel_requested"}}, "world": {"prs": [{"number": 123, "title": "feat(server): resumable uploads", "headRefName": "feat/resumable-uploads"}]}}, "prompt": "You're doing too much man, we have like 6+ delegates going. We don't need to go that hard.", "expect": {"calls": [{"tool": "task_cancel"}], "maxCalls": {"delegate_task": 0}, "notCalls": ["t3_thread_interrupt"]}, "notes": "Pairs with review-round-per-bot-nit."} {"id": "control-new-thread-view-wording", "area": "orchestration", "kind": "control", "pattern": "'new thread' as product text", "experience": "A UI bug report that says 'new thread view' needs a code fix, not a new T3 thread or delegate.", "rule": "t3_thread_launch and create_threads ... only when the user explicitly asks for separate/new/top-level threads.", "privacy": "commit-safe", "setup": {"role": "top", "world": {"files": {"apps/mobile/src/Sidebar.tsx": "export function Sidebar() {\n return (\n \n \n \n );\n}\n", "apps/mobile/src/NewThreadView.tsx": "export function NewThreadView() {\n return {/* composer */};\n}\n"}}}, "prompt": "On Android foldables the sidebar is visible but tapping threads does nothing in some places, like the new thread view or settings. It makes the sidebar feel broken. Fix it.", "expect": {"notCalls": ["t3_thread_launch", "create_threads", "delegate_task", "orchestrator_capabilities"]}, "notes": ""} {"id": "control-subagent-label-bug-wording", "area": "orchestration", "kind": "control", "pattern": "'subagent' as product text", "experience": "A bug report about how subagent models are labeled is a code task. It needs no delegation.", "rule": "none", "privacy": "commit-safe", "setup": {"role": "top", "world": {"commits": [{"sha": "61b0e9d", "subject": "fix(web): label subagent messages with the subagent's model"}], "files": {"apps/web/src/subagentLabel.ts": "export function subagentModelLabel(run: Run, parent: Thread) {\n return run.model ?? parent.model;\n}\n"}}}, "prompt": "I recently fixed a bug where models were labeled wrong inside subagents that run through Claude Code. I think it came back in a different form. A Sol subagent shows the parent's model name in the UI. Find the cause and fix it.", "expect": {"notCalls": ["delegate_task", "orchestrator_capabilities", "t3_thread_launch", "create_threads", "Workflow"]}, "notes": ""} diff --git a/evals/mcp-tool-use/run.ts b/evals/mcp-tool-use/run.ts index 592ecd98141c..78c5a423ef82 100644 --- a/evals/mcp-tool-use/run.ts +++ b/evals/mcp-tool-use/run.ts @@ -1291,10 +1291,10 @@ const withProductFields = (name: string, value: unknown, child: boolean): unknow return typeof id === "string" ? { ...thread, - link: formatThreadLink({ - environmentId: EVAL_ENVIRONMENT_ID, - threadId: ThreadId.make(id), - }), + link: formatThreadLink( + { environmentId: EVAL_ENVIRONMENT_ID, threadId: ThreadId.make(id) }, + typeof thread.title === "string" ? thread.title : undefined, + ), } : thread; }; diff --git a/packages/contracts/src/orchestrationV2.ts b/packages/contracts/src/orchestrationV2.ts index 77a89ac3520a..ca72b708ad6e 100644 --- a/packages/contracts/src/orchestrationV2.ts +++ b/packages/contracts/src/orchestrationV2.ts @@ -874,6 +874,8 @@ export const OrchestrationV2RemoteTaskChild = Schema.Struct({ environmentId: EnvironmentId, threadId: ThreadId, label: Schema.String, + /** The title it launched with there; absent on tasks recorded before it was kept. */ + title: Schema.optional(Schema.String), /** The run there that carries the task; absent on tasks recorded before it was kept. */ runId: Schema.optional(RunId), /** diff --git a/packages/shared/src/threadLinks.test.ts b/packages/shared/src/threadLinks.test.ts index c935c379a06b..00f4f26c8303 100644 --- a/packages/shared/src/threadLinks.test.ts +++ b/packages/shared/src/threadLinks.test.ts @@ -117,6 +117,16 @@ describe("thread links", () => { relabelThreadLinks("[Kept label]()", local, () => undefined), ).toBe("Kept label"); expect(threadLinkRefs("[a]()", local)).toEqual([ref("there", "t2")]); + for (const threadId of ["mcp:launch:qa(1)", "mcp:launch:qa)"]) { + const thread = ref("there", threadId); + const link = formatThreadLink(thread, "Original"); + expect(link).toBe(`[Original]()`); + expect(threadLinkRefs(link, local)).toEqual([thread]); + expect(relabelThreadLinks(link, local, () => "Renamed")).toBe( + `[Renamed]()`, + ); + expect(relabelThreadLinks(link, local, () => undefined)).toBe("Original"); + } }); it("turns a v2 link it cannot resolve into plain text, never the raw link", () => { diff --git a/packages/shared/src/threadLinks.ts b/packages/shared/src/threadLinks.ts index e13e2d1064e6..a7e38de5003c 100644 --- a/packages/shared/src/threadLinks.ts +++ b/packages/shared/src/threadLinks.ts @@ -13,7 +13,7 @@ const HOSTED_LINK = `${HOSTED_PREFIX.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&")}${H // Code comes first in the alternation so a link written inside a code span or fence is skipped. // A labeled link's destination may be angle-bracketed, as CommonMark allows: [label](). const THREAD_LINK_OUTSIDE_CODE = new RegExp( - String.raw`(?(\`{3,}|~{3,})[\s\S]*?(?:\2|$))|(?(\`+)[^\n]*?\4)|\[(?