From 6d5ecf0e3acea93ebede5d60cdf776c9f395337a Mon Sep 17 00:00:00 2001
From: Julius Marminge
Date: Thu, 12 Nov 2026 06:33:00 +0000
Subject: [PATCH 1/2] fix(contracts): delegate_task's options example names
each provider's option id
The target.options description gave {effort:'max'} as its only example, a
Claude option id. Agents that followed it for a Codex target were rejected
with "Unknown option effort" and had to retry. The description now says the
ids come from the target model in orchestrator_capabilities and differ by
provider, with a Claude and a Codex example.
Adds an mcp-tool-use eval case for a Claude parent delegating to Codex at
high effort.
Fixes pingdotgg/t3code#32850
AI-assisted; verified with the tests and tools listed above.
---
apps/server/src/provider/agentContextBudget.json | 2 +-
evals/mcp-tool-use/cases/orchestration.ndjson | 1 +
packages/contracts/src/orchestratorMcp.ts | 2 +-
3 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/apps/server/src/provider/agentContextBudget.json b/apps/server/src/provider/agentContextBudget.json
index 486c4314927ad..e6268d416a2bb 100644
--- a/apps/server/src/provider/agentContextBudget.json
+++ b/apps/server/src/provider/agentContextBudget.json
@@ -1,5 +1,5 @@
{
"claudeSystemAppendBytes": 5597,
"codexAdditionalContextBytes": 7327,
- "mcpToolListJsonBytes": 48131
+ "mcpToolListJsonBytes": 48277
}
diff --git a/evals/mcp-tool-use/cases/orchestration.ndjson b/evals/mcp-tool-use/cases/orchestration.ndjson
index 520bd2abf10d3..fe840be30d945 100644
--- a/evals/mcp-tool-use/cases/orchestration.ndjson
+++ b/evals/mcp-tool-use/cases/orchestration.ndjson
@@ -40,3 +40,4 @@
{"id": "settle-after-delayed-merge-waits", "area": "orchestration", "kind": "guard", "pattern": "settle before delayed work finishes", "experience": "Asked to merge a PR once its checks pass and then settle this thread, an agent following the #26749 settle bullet can settle in the first turn; a settled thread refuses watch_pull_request and drops PR-watch wakes, so the merge never happens.", "rule": "Asked to settle ... a thread, including this one: call t3_thread_organize. (the request is to settle after the merge, not now)", "privacy": "commit-safe", "notes": "From the CodeRabbit finding on #26765. Settling this thread applies at turn end, and Orchestrator.ts rejects watch wakes on a settled thread. Good: watch #123 and end the turn without settling. gh pr merge --auto plus a settle would still lose the wake that confirms the merge.", "setup": {"role": "top", "priorTurns": [{"role": "user", "text": "Okay this looks good to me. Any risk merging it?"}, {"role": "assistant", "text": "Low risk. [#123](https://github.com/acme/app/pull/123) only adds an accessible title to the HTML preview frame, and the new test covers it. Two checks are still running: Check and Test Web.", "toolCalls": [{"tool": "Bash", "input": {"command": "gh pr view 123 --json title,files --jq '.title, .files[].path'"}, "result": "fix(web): HTML preview frame gets an accessible title\napps/web/src/HtmlPreview.tsx\napps/web/src/HtmlPreview.test.tsx"}, {"tool": "Bash", "input": {"command": "gh pr checks 123"}, "result": "Lint\tpass\t41s\nTypecheck\tpass\t1m2s\nCheck\tpending\t0\nTest Web\tpending\t0"}, {"tool": "link_pull_request", "input": {"url": "https://github.com/acme/app/pull/123"}, "result": {"host": "github.com", "repository": "acme/app", "number": 123, "url": "https://github.com/acme/app/pull/123", "alreadyLinked": false}}]}], "mocks": {"watch_pull_request": "default", "link_pull_request": "default", "t3_thread_organize": {"settlesWhenTurnEnds": true}}, "world": {"branch": "fix/html-preview-title", "commits": [{"sha": "abc1234", "subject": "fix(web): HTML preview frame gets an accessible title"}], "diff": "diff --git a/apps/web/src/HtmlPreview.tsx b/apps/web/src/HtmlPreview.tsx\n--- a/apps/web/src/HtmlPreview.tsx\n+++ b/apps/web/src/HtmlPreview.tsx\n@@ -1,3 +1,3 @@\n export function HtmlPreview({ html }: { html: string }) {\n- return ;\n+ return ;\n }\ndiff --git a/apps/web/src/HtmlPreview.test.tsx b/apps/web/src/HtmlPreview.test.tsx\nnew file mode 100644\n--- /dev/null\n+++ b/apps/web/src/HtmlPreview.test.tsx\n+import { expect, it } from 'vitest';\n+import { render, screen } from '@testing-library/react';\n+import { HtmlPreview } from './HtmlPreview';\n+\n+it('gives the preview frame an accessible name', () => {\n+ render(hi
\" />);\n+ expect(screen.getByTitle('HTML preview')).toBeTruthy();\n+});\n", "prs": [{"number": 123, "title": "fix(web): HTML preview frame gets an accessible title", "headRefName": "fix/html-preview-title", "checks": [{"name": "Lint", "state": "pass"}, {"name": "Typecheck", "state": "pass"}, {"name": "Check", "state": "pending"}, {"name": "Test Web", "state": "pending"}]}], "files": {"apps/web/src/HtmlPreview.tsx": "export function HtmlPreview({ html }: { html: string }) {\n return ;\n}\n", "apps/web/src/HtmlPreview.test.tsx": "import { expect, it } from 'vitest';\nimport { render, screen } from '@testing-library/react';\nimport { HtmlPreview } from './HtmlPreview';\n\nit('gives the preview frame an accessible name', () => {\n render(hi\" />);\n expect(screen.getByTitle('HTML preview')).toBeTruthy();\n});\n"}}, "shellMocks": [{"match": "gh pr merge", "output": "✓ Squashed and merged pull request acme/app#123 (fix(web): HTML preview frame gets an accessible title)"}]}, "prompt": "Merge it once its checks pass, then settle this thread.", "expect": {"calls": [{"tool": "watch_pull_request", "args": {"url": "~/pull/123"}}], "notCalls": ["t3_thread_organize", "Monitor", "ScheduleWakeup"], "shellMustNot": ["\\bsleep \\d", "gh pr checks[^\\n]*--watch", "gh run watch", "\\buntil\\b[^\\n]*\\bgh (pr|run|api)\\b", "pr-wait", "gh pr merge(?![^\\n]*--auto)"]}}
{"id": "settle-after-merge-on-wake", "area": "orchestration", "kind": "bad", "pattern": "settle read as plain English for finish", "experience": "Asked to merge a PR once its checks pass and then settle this thread, the agent merged on the checks-passed wake and ended the turn without calling t3_thread_organize (#26749).", "rule": "Asked to settle ... a thread, including this one: call t3_thread_organize.", "privacy": "commit-safe", "notes": "From #26749's delayed pattern. Wake text is copied from pullRequestWatch.ts. Good: merge #123, then settle this thread on the same turn.", "setup": {"role": "top", "priorTurns": [{"role": "user", "text": "Okay this looks good to me. Any risk merging it?"}, {"role": "assistant", "text": "Low risk. [#123](https://github.com/acme/app/pull/123) only adds an accessible title to the HTML preview frame, and the new test covers it. Two checks are still running: Check and Test Web.", "toolCalls": [{"tool": "Bash", "input": {"command": "gh pr view 123 --json title,files --jq '.title, .files[].path'"}, "result": "fix(web): HTML preview frame gets an accessible title\napps/web/src/HtmlPreview.tsx\napps/web/src/HtmlPreview.test.tsx"}, {"tool": "Bash", "input": {"command": "gh pr checks 123"}, "result": "Lint\tpass\t41s\nTypecheck\tpass\t1m2s\nCheck\tpending\t0\nTest Web\tpending\t0"}, {"tool": "link_pull_request", "input": {"url": "https://github.com/acme/app/pull/123"}, "result": {"host": "github.com", "repository": "acme/app", "number": 123, "url": "https://github.com/acme/app/pull/123", "alreadyLinked": false}}]}, {"role": "user", "text": "Merge it once its checks pass, then settle this thread."}, {"role": "assistant", "text": "T3 Code is watching [#123](https://github.com/acme/app/pull/123). I'll merge it once Check and Test Web pass, then settle this thread.", "toolCalls": [{"tool": "watch_pull_request", "input": {"url": "https://github.com/acme/app/pull/123"}, "result": {"host": "github.com", "repository": "acme/app", "number": 123, "url": "https://github.com/acme/app/pull/123", "watching": true, "wasWatching": false}}]}], "mocks": {"watch_pull_request": "default", "link_pull_request": "default", "t3_thread_organize": {"settlesWhenTurnEnds": true}, "unwatch_pull_request": "default"}, "world": {"branch": "fix/html-preview-title", "commits": [{"sha": "abc1234", "subject": "fix(web): HTML preview frame gets an accessible title"}], "diff": "diff --git a/apps/web/src/HtmlPreview.tsx b/apps/web/src/HtmlPreview.tsx\n--- a/apps/web/src/HtmlPreview.tsx\n+++ b/apps/web/src/HtmlPreview.tsx\n@@ -1,3 +1,3 @@\n export function HtmlPreview({ html }: { html: string }) {\n- return ;\n+ return ;\n }\ndiff --git a/apps/web/src/HtmlPreview.test.tsx b/apps/web/src/HtmlPreview.test.tsx\nnew file mode 100644\n--- /dev/null\n+++ b/apps/web/src/HtmlPreview.test.tsx\n+import { expect, it } from 'vitest';\n+import { render, screen } from '@testing-library/react';\n+import { HtmlPreview } from './HtmlPreview';\n+\n+it('gives the preview frame an accessible name', () => {\n+ render(hi\" />);\n+ expect(screen.getByTitle('HTML preview')).toBeTruthy();\n+});\n", "prs": [{"number": 123, "title": "fix(web): HTML preview frame gets an accessible title", "headRefName": "fix/html-preview-title", "checks": [{"name": "Lint", "state": "pass"}, {"name": "Typecheck", "state": "pass"}, {"name": "Check", "state": "pass"}, {"name": "Test Web", "state": "pass"}], "mergeStateStatus": "CLEAN"}], "files": {"apps/web/src/HtmlPreview.tsx": "export function HtmlPreview({ html }: { html: string }) {\n return ;\n}\n", "apps/web/src/HtmlPreview.test.tsx": "import { expect, it } from 'vitest';\nimport { render, screen } from '@testing-library/react';\nimport { HtmlPreview } from './HtmlPreview';\n\nit('gives the preview frame an accessible name', () => {\n render(hi\" />);\n expect(screen.getByTitle('HTML preview')).toBeTruthy();\n});\n"}}, "shellMocks": [{"match": "gh pr merge", "output": "✓ Squashed and merged pull request acme/app#123 (fix(web): HTML preview frame gets an accessible title)"}]}, "prompt": "Update on pull request #123 (https://github.com/acme/app/pull/123), which T3 Code is watching for you:\n- All 4 checks passed on abc1234.\n\nLook into each item and act on it as your task requires. This PR stays linked and watched, so do not link, watch, or list it again. End your turn when you are done, and T3 Code wakes you on the next change. When you hand the work back to the user, call unwatch_pull_request first so the thread returns to their inbox.", "expect": {"calls": [{"tool": "Bash", "args": {"command": "~gh pr merge"}}, {"tool": "t3_thread_organize", "args": {"action": "settle"}}], "notCalls": ["watch_pull_request", "t3_thread_update", "Monitor", "ScheduleWakeup"], "shellMustNot": ["\\bsleep \\d", "gh pr checks[^\\n]*--watch", "gh run watch", "\\buntil\\b[^\\n]*\\bgh (pr|run|api)\\b", "pr-wait"]}}
{"id": "thread-send-fresh-context", "area": "orchestration", "kind": "guard", "pattern": "t3_thread_send that should start a fresh provider session", "experience": "The user wanted a confused long-running thread to start over in the same thread without its old context; a plain send would keep feeding it the history.", "rule": "`freshContext: true` on t3_thread_send starts a new provider session in the same thread, like /clear.", "privacy": "commit-safe", "setup": {"role": "top", "priorTurns": [{"role": "user", "text": "The thread working on the CSV importer keeps reverting to the old column mapping from early in its history."}, {"role": "assistant", "text": "That thread has a long history with three different mappings in it, so the model keeps picking up the stale one."}], "mocks": {"t3_thread_send": {"threadId": "11111111-1111-4111-8111-111111111111", "link": "", "messageId": "msg-1", "runId": "run-1", "status": "starting", "delivery": "started"}}}, "prompt": "Send it the final mapping below and have it start over with a clean context in that same thread, so the old history stops leaking in: thread 11111111-1111-4111-8111-111111111111.\n\nFinal mapping: id -> customer_id, amt -> amount_cents, ts -> created_at.", "expect": {"calls": [{"tool": "t3_thread_send", "args": {"threadId": "11111111-1111-4111-8111-111111111111", "freshContext": true}}], "notCalls": ["delegate_task", "t3_thread_launch", "create_threads"]}, "source": {"request": "juliusmarminge (maintainer) request, 2026-11-03: fresh provider sessions in one T3 thread"}, "notes": "Pairs with thread-send-to-named-top-level-thread, where an ordinary send must not set freshContext."}
+{"id": "delegate-options-use-target-provider-ids", "area": "orchestration", "kind": "bad", "pattern": "delegate options named for the wrong provider", "experience": "delegate_task's options example was {effort:'max'}, a Claude option id. Following it for a Codex target failed with 'Unknown option effort; supported options: reasoningEffort, serviceTier' and the agent had to retry.", "source": {"issue": "https://github.com/pingdotgg/t3code/issues/32850"}, "rule": "target.options: option ids and values come from the target model in orchestrator_capabilities and differ by provider.", "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": {"taskId": "task-1", "childThreadId": "44444444-4444-4444-8444-000000000001", "childRunId": "run-child-1", "status": "running", "workState": "working", "providerInstanceId": "codex", "model": "gpt-6.1-sol"}}, "world": {"files": {"lesson-1/plan.html": "Lesson 1: Variables
\nIn this lesson you will learn what variables is and how to use them good.
\n", "lesson-2/plan.html": "Lesson 2: Loops
\nLoops lets you repeat stuff. We will do for loops and also while loops maybe.
\n"}}}, "prompt": "Have a GPT-6.1 Sol delegate in Codex proofread the lesson plan pages at high effort and fix any typos itself.", "expect": {"calls": [{"tool": "delegate_task", "args": {"target.model": "gpt-6.1-sol", "target.options": "~reasoningEffort"}}], "callsMustNot": [{"tool": "delegate_task", "args": {"target.options": "~\"effort\""}}]}, "notes": "Claude parent, Codex target. '~\"effort\"' matches the Claude id in either options form ({effort:..} or [{id:'effort'}]) but not reasoningEffort."}
diff --git a/packages/contracts/src/orchestratorMcp.ts b/packages/contracts/src/orchestratorMcp.ts
index d1bb9961a0c68..0e2fc901c960a 100644
--- a/packages/contracts/src/orchestratorMcp.ts
+++ b/packages/contracts/src/orchestratorMcp.ts
@@ -109,7 +109,7 @@ export const OrchestratorMcpTarget = Schema.Struct({
options: Schema.optional(
OrchestratorMcpTargetOptions.annotate({
description:
- "Options from `orchestrator_capabilities`, such as {effort:'max'}. Given values override inherited. [] or {} clears.",
+ "The target model's options from `orchestrator_capabilities`. Ids differ by provider: Claude {effort:'max'}, Codex {reasoningEffort:'max'}. Given values override inherited. [] or {} clears.",
}),
),
environmentId: Schema.optional(
From 985e64e7dbb02a2db4ad81ac6ae97b0fa803ccde Mon Sep 17 00:00:00 2001
From: Julius Marminge
Date: Thu, 12 Nov 2026 06:45:05 +0000
Subject: [PATCH 2/2] fix(contracts): keep delegate_task's options text within
the context budget
Drop the provider examples and say only that option ids are per target
model, so the tool list is no larger than main's.
AI-assisted; verified with the tests and tools listed above.
---
apps/server/src/provider/agentContextBudget.json | 2 +-
packages/contracts/src/orchestratorMcp.ts | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/apps/server/src/provider/agentContextBudget.json b/apps/server/src/provider/agentContextBudget.json
index e6268d416a2bb..5cc0259244f16 100644
--- a/apps/server/src/provider/agentContextBudget.json
+++ b/apps/server/src/provider/agentContextBudget.json
@@ -1,5 +1,5 @@
{
"claudeSystemAppendBytes": 5597,
"codexAdditionalContextBytes": 7327,
- "mcpToolListJsonBytes": 48277
+ "mcpToolListJsonBytes": 48125
}
diff --git a/packages/contracts/src/orchestratorMcp.ts b/packages/contracts/src/orchestratorMcp.ts
index 0e2fc901c960a..fb78b273d3ee5 100644
--- a/packages/contracts/src/orchestratorMcp.ts
+++ b/packages/contracts/src/orchestratorMcp.ts
@@ -109,7 +109,7 @@ export const OrchestratorMcpTarget = Schema.Struct({
options: Schema.optional(
OrchestratorMcpTargetOptions.annotate({
description:
- "The target model's options from `orchestrator_capabilities`. Ids differ by provider: Claude {effort:'max'}, Codex {reasoningEffort:'max'}. Given values override inherited. [] or {} clears.",
+ "Option ids per target model, from `orchestrator_capabilities`. Given values override inherited. [] or {} clears.",
}),
),
environmentId: Schema.optional(