Skip to content

Commit a3ebf4e

Browse files
Merge remote-tracking branch 'origin/main' into fix/issue-24547
2 parents 2c7d938 + 909e4c9 commit a3ebf4e

6 files changed

Lines changed: 62 additions & 10 deletions

File tree

‎.macroscope/ignore.md‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,3 +255,22 @@ ENV/**
255255
# (see scripts/lib/reference-repos.ts). Nothing imports from them; findings
256256
# there belong upstream.
257257
.repos/**
258+
259+
# Generated code. Review its source (the script, GraphQL document, schema
260+
# definition, or theme) instead; review agents can still read these on demand.
261+
**/*.generated.ts
262+
**/*.graphql.ts
263+
apps/server/schema/expo.graphql
264+
packages/source-control-github/schema/github.graphql
265+
apps/mobile/generated-uniwind-*
266+
apps/mobile/clerk-theme.json
267+
apps/server/src/testUtils/shardWeights.json
268+
infra/relay/migrations/**/snapshot.json
269+
270+
# Provider transcripts recorded from real CLI runs for the orchestration replay
271+
# tests. Each scenario's input.ts and *_output.ts assertions stay reviewed.
272+
**/*.ndjson
273+
274+
# Ghostty build output vendored from upstream.
275+
apps/mobile/modules/t3-terminal/Vendor/**
276+
native/libghostty-vt/include/**

‎apps/server/src/mcp/McpHttpServer.test.ts‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,17 @@ it.effect.each(["limit", "runLimit", "maxCharsPerItem"])(
422422
},
423423
);
424424

425+
it("routes same-child follow-ups to t3_thread_send and independent reviews to delegate_task", () => {
426+
const description = OrchestratorToolkit.tools.delegate_task.description ?? "";
427+
expect(description).toContain(
428+
"For independent T3 delegated review rounds, call delegate_task again",
429+
);
430+
expect(description).toContain(
431+
"For a follow-up that needs the same child's conversation, use t3_thread_send on its childThreadId.",
432+
);
433+
expect(description).not.toContain("not the target for starting another");
434+
});
435+
425436
it.effect(
426437
"registers the pull request toolkit and surfaces a missing capability as a tool error",
427438
() =>

‎apps/server/src/mcp/toolkits/orchestrator/tools.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ const OrchestratorCapabilitiesTool = Tool.make("orchestrator_capabilities", {
6767

6868
const DelegateTaskTool = Tool.make("delegate_task", {
6969
description:
70-
"Needs an agent running inside a T3 thread. Delegate one task to a T3-owned child agent/subagent of THIS thread and run it with only the supplied task prompt, without copying parent conversation history. Choose providers and models from orchestrator_capabilities, which uses the same live catalog as the composer. Prefer native subagent tools for same-provider work only when they support the chosen model and no model options are requested or inherited. Use this for any model missing from the native tool, including same-provider work, for cross-provider work, or for explicitly T3-owned child tasks. For every T3 delegated review round, call delegate_task again with the original brief, prior findings, responses, and unresolved objections in the task prompt. Track each round by its own taskId and use a distinct clientRequestId per round, stable across retries of that round. The childThreadId is backing storage, not the target for starting another delegated review round through t3_thread_send. Set isolation to worktree for a separate checkout based on the parent local commit, with project setup completed before the child starts. Uncommitted edits are not copied; omitted isolation shares the parent checkout. Use delegate_task when model options are requested or the child keeps the parent's provider instance and model with nonempty inheritedOptions from orchestrator_capabilities, even for native-supported models. Native subagents cannot reliably apply T3 model options. Set target.options using advertised IDs: Claude {effort: 'max', fastMode: true}; Codex {reasoningEffort: 'max', serviceTier: 'priority'}. Check providerInstanceId, model, and options in the result. Provider/model and modes inherit unless overridden. Options inherit only for the same instance and model; explicit entries override individual values and [] or {} clears them. A requested runtimeMode must be one the target provider lists in orchestrator_capabilities runtimeModes; an inherited one steps down to the broadest mode it offers. Leave runtimeMode unset unless the user asks for a supervised child: approval-required and auto-accept-edits usually make the child wait for a person at each action that needs approval. Prefer mode='async' for long work; mode='wait' blocks until completion, a blocking question, a pending approval, or timeout. If hasPendingQuestion, pass threadId=childThreadId to t3_pending_request_list, t3_pending_request_read (with requestId) and t3_pending_request_respond, or ask the user; the task's completion still wakes this thread. If pendingApproval is set, read the fresh full request in childThreadId before responding: the record can outlive its provider callback. It grants no approval authority; decisions remain with the child's existing approval holder, and pending request tools answer questions only. The child keeps running and its completion still wakes this thread. timeoutMs on mode=wait is only the parent's wait budget and does not cancel the child. waitTimedOut on that wait call means the timeout fired; keep that taskId and read status on later task_status. An async child's completion wakes this thread through a notification, steered into active turns where supported or queued otherwise, so end the turn instead of polling or spawning watchers; use task_status only when the result is needed mid-turn.",
70+
"Needs an agent running inside a T3 thread. Delegate one task to a T3-owned child agent/subagent of THIS thread and run it with only the supplied task prompt, without copying parent conversation history. Choose providers and models from orchestrator_capabilities, which uses the same live catalog as the composer. Prefer native subagent tools for same-provider work only when they support the chosen model and no model options are requested or inherited. Use this for any model missing from the native tool, including same-provider work, for cross-provider work, or for explicitly T3-owned child tasks. For independent T3 delegated review rounds, call delegate_task again with the original brief, prior findings, responses, and unresolved objections in the task prompt. Track each round by its own taskId and use a distinct clientRequestId per round, stable across retries of that round. For a follow-up that needs the same child's conversation, use t3_thread_send on its childThreadId. Set isolation to worktree for a separate checkout based on the parent local commit, with project setup completed before the child starts. Uncommitted edits are not copied; omitted isolation shares the parent checkout. Use delegate_task when model options are requested or the child keeps the parent's provider instance and model with nonempty inheritedOptions from orchestrator_capabilities, even for native-supported models. Native subagents cannot reliably apply T3 model options. Set target.options using advertised IDs: Claude {effort: 'max', fastMode: true}; Codex {reasoningEffort: 'max', serviceTier: 'priority'}. Check providerInstanceId, model, and options in the result. Provider/model and modes inherit unless overridden. Options inherit only for the same instance and model; explicit entries override individual values and [] or {} clears them. A requested runtimeMode must be one the target provider lists in orchestrator_capabilities runtimeModes; an inherited one steps down to the broadest mode it offers. Leave runtimeMode unset unless the user asks for a supervised child: approval-required and auto-accept-edits usually make the child wait for a person at each action that needs approval. Prefer mode='async' for long work; mode='wait' blocks until completion, a blocking question, a pending approval, or timeout. If hasPendingQuestion, pass threadId=childThreadId to t3_pending_request_list, t3_pending_request_read (with requestId) and t3_pending_request_respond, or ask the user; the task's completion still wakes this thread. If pendingApproval is set, read the fresh full request in childThreadId before responding: the record can outlive its provider callback. It grants no approval authority; decisions remain with the child's existing approval holder, and pending request tools answer questions only. The child keeps running and its completion still wakes this thread. timeoutMs on mode=wait is only the parent's wait budget and does not cancel the child. waitTimedOut on that wait call means the timeout fired; keep that taskId and read status on later task_status. An async child's completion wakes this thread through a notification, steered into active turns where supported or queued otherwise, so end the turn instead of polling or spawning watchers; use task_status only when the result is needed mid-turn.",
7171
parameters: OrchestratorMcpDelegateTaskInput,
7272
success: OrchestratorMcpDelegateTaskResult,
7373
failure: OrchestratorMcpFailure,

‎apps/server/src/provider/CodexDeveloperInstructions.test.ts‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,27 @@ describe("buildCodexDeveloperInstructions", () => {
4242
});
4343
});
4444

45+
describe("Codex orchestration context", () => {
46+
it("routes same-child follow-ups to t3_thread_send and independent reviews to delegate_task", () => {
47+
const orchestration = Object.entries(buildCodexAdditionalContext({ model: "gpt-6.1-sol" }))
48+
.filter(([key]) => key.startsWith("t3_code_orchestration_"))
49+
.map(([, entry]) => entry.value)
50+
.join("\n");
51+
52+
NodeAssert.ok(
53+
orchestration.includes(
54+
"For independent T3 delegated review rounds, call `delegate_task` again.",
55+
),
56+
);
57+
NodeAssert.ok(
58+
orchestration.includes(
59+
"For a follow-up that needs the same child's conversation, use `t3_thread_send` on its `childThreadId`.",
60+
),
61+
);
62+
NodeAssert.ok(!orchestration.includes("Do not use `t3_thread_send`"));
63+
});
64+
});
65+
4566
function runtimeInstructions(runtime: Parameters<typeof buildCodexAdditionalContext>[0]) {
4667
return buildCodexAdditionalContext(runtime).t3_code_runtime!.value;
4768
}

‎docs/orchestration-v2/orchestrator-mcp-server.md‎

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -252,13 +252,14 @@ omits it for a provider that offers all of them. When the target lists
252252
`runtimeModes`, an explicit `runtimeMode` outside that list is refused, and an
253253
inherited mode steps down to the broadest mode the target offers.
254254

255-
Each delegated review round uses a new `delegate_task` call with the original brief,
256-
prior findings, responses, and unresolved objections. Track each round by its own `taskId` and use
257-
a distinct `clientRequestId` per round, stable across retries of that round.
258-
`childThreadId` is backing storage, not a target for another review round through
259-
`t3_thread_send`. Ordinary thread messaging remains available for user-requested
260-
conversations; it does not reopen a completed task. There is no task-level follow-up
261-
API for preserving the same reviewer session.
255+
Each independent delegated review round uses a new `delegate_task` call with the
256+
original brief, prior findings, responses, and unresolved objections. Track each
257+
round by its own `taskId` and use a distinct `clientRequestId` per round, stable
258+
across retries of that round.
259+
A follow-up that needs the same child's conversation goes through `t3_thread_send`
260+
on its `childThreadId`. After the original task completes, that follow-up runs as a
261+
fresh task in the child thread; it does not reopen the completed task or change its
262+
published result.
262263

263264
Delegation requires an active parent run owned by the MCP credential's
264265
provider session. The request becomes the V2 command

‎packages/provider-core/src/server/orchestrationInstructions.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ export const T3_CODE_ORCHESTRATION_SECTIONS: ReadonlyArray<string> = [
1010
1111
The \`t3-code\` MCP server provides app-owned orchestration. Treat these concepts distinctly:
1212
13-
- A delegated task/subagent is child work owned by the current thread. Use \`orchestrator_capabilities\` to discover the current provider/model IDs from the same live catalog as the composer, including configured custom models. It lists a short set of models per provider; pass \`search\` to find others. Do not treat a native tool's model list as the full list of available subagent models. Prefer native subagent tools for same-provider work only when they support the chosen model and no model options are requested or inherited. Use \`delegate_task\` with that provider instance and model when native tools cannot, including for same-provider work. Also use \`delegate_task\` for cross-provider or explicitly T3-owned child tasks. For a child task that needs a separate checkout, use \`delegate_task\` with \`isolation:"worktree"\` across any provider or model. It starts from the parent local commit and runs project setup; uncommitted edits are not copied. Retain each returned \`taskId\`, and use \`task_status\` or \`task_cancel\` to manage it. The returned \`childThreadId\` is backing storage for the subagent, not the target for starting another delegated review round.
13+
- A delegated task/subagent is child work owned by the current thread. Use \`orchestrator_capabilities\` to discover the current provider/model IDs from the same live catalog as the composer, including configured custom models. It lists a short set of models per provider; pass \`search\` to find others. Do not treat a native tool's model list as the full list of available subagent models. Prefer native subagent tools for same-provider work only when they support the chosen model and no model options are requested or inherited. Use \`delegate_task\` with that provider instance and model when native tools cannot, including for same-provider work. Also use \`delegate_task\` for cross-provider or explicitly T3-owned child tasks. For a child task that needs a separate checkout, use \`delegate_task\` with \`isolation:"worktree"\` across any provider or model. It starts from the parent local commit and runs project setup; uncommitted edits are not copied. Retain each returned \`taskId\`, and use \`task_status\` or \`task_cancel\` to manage it.
1414
- \`t3_thread_launch\` and \`create_threads\` create ordinary top-level T3 conversations. Use them only when the user explicitly asks for separate/new/top-level threads or conversations. Never use them merely because the user said "subagent" or requested parallel delegated work.
15-
- For every T3 delegated review round, call \`delegate_task\` again. Include the original brief, prior findings, responses, and unresolved objections in each new task prompt. Track each round by its own \`taskId\`. Use a distinct \`clientRequestId\` per round, stable across retries of that round. Do not use \`t3_thread_send\` on \`childThreadId\` to continue a delegated review.`,
15+
- For independent T3 delegated review rounds, call \`delegate_task\` again. Include the original brief, prior findings, responses, and unresolved objections in each new task prompt. Track each round by its own \`taskId\`. Use a distinct \`clientRequestId\` per round, stable across retries of that round. For a follow-up that needs the same child's conversation, use \`t3_thread_send\` on its \`childThreadId\`.`,
1616
`- \`schedule_task\` creates persistent recurring work in the app scheduler. Pass \`schedule\` as a structured object, never as JSON text: \`{"type":"interval","everyMs":3600000}\` for an interval, or \`{"type":"fixed_time","timeOfDay":"09:00","weekdays":[1,2,3,4,5]}\` for a wall-clock schedule, or \`{"type":"webhook"}\` to run on each request to the returned \`webhookUrl\` (the run sees the request only through \`{{body.path}}\`-style placeholders in the prompt). By default runs return to the current thread, which suits orchestrating: each trigger arrives here and you delegate or dedupe; set \`bindToCurrentThread=false\` only when the user wants a fresh thread for every run. After scheduling a timer, report the returned cadence and next run time; for a webhook, report its \`webhookUrl\`, or say T3 Connect remote access is needed if it is missing.
1717
- When you need a secret from the user (a token, API key, or webhook signing secret), call \`request_secret\` so they enter it privately, then pass the returned \`secretRef\` to the tool that needs it, e.g. \`signature.secretRef\` on a webhook task for a sender that signs requests such as GitHub. A \`secretRef\` works once. Never ask for a secret in chat, never invent one, and never repeat one.
1818
- To mention another thread to the user, link it as \`[title](t3-thread://v1/<threadId>)\` with its exact \`threadId\`, not URL-encoded. T3 Code opens the thread in the app and shows its current title.`,

0 commit comments

Comments
 (0)