Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,8 @@ describe("ClaudeAdapterV2 native protocol logging", () => {
assert.equal(systemPrompt.type, "preset");
assert.equal(systemPrompt.preset, "claude_code");
assert.include(systemPrompt.append ?? "", "Use `delegate_task`");
assert.notInclude(systemPrompt.append ?? "", "acp-mcp-call");
assert.notInclude(systemPrompt.append ?? "", "mcp__t3_code__");
const logged = ClaudeAdapterV2.loggedClaudeQueryOptions(options);
assert.equal(logged.hasMcpServers, true);
assert.notInclude(JSON.stringify(logged), "secret-claude-token");
Expand Down
12 changes: 12 additions & 0 deletions apps/server/src/provider/CodexDeveloperInstructions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,18 @@ import {
} from "./CodexDeveloperInstructions.ts";

describe("buildCodexDeveloperInstructions", () => {
it("includes the Codex code-mode hint in orchestration context", () => {
const context = buildCodexAdditionalContext({
model: "gpt-5.3-codex",
reasoningEffort: "high",
});

const orchestration = context.t3_code_orchestration?.value ?? "";

NodeAssert.match(orchestration, /tools\.mcp__t3_code__orchestrator_capabilities/);
NodeAssert.doesNotMatch(orchestration, /acp-mcp-call/);
});

it("appends runtime info after the mode instructions", () => {
const instructions = runtimeInstructions({
model: "gpt-5.3-codex",
Expand Down
7 changes: 6 additions & 1 deletion apps/server/src/provider/CodexDeveloperInstructions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
T3_CODE_ORCHESTRATION_INSTRUCTIONS,
} from "./T3OrchestrationInstructions.ts";

const CODEX_CODE_MODE_INSTRUCTIONS = `In Codex code mode, if the T3 tools are not listed, call \`tools.mcp__t3_code__orchestrator_capabilities({})\` before reporting that the capability is absent.`;

const T3_CODE_DEVICE_TOOL_INSTRUCTIONS = `## T3 Code devices

The \`t3-code\` MCP server also exposes \`device_*\` tools for iOS Simulators and Android Emulators on this environment. For mobile verification, call \`device_list\`, then \`device_open\` so the user can watch the device in their Device panel; its result explains how to drive the device. Driving happens through the \`agent-device\` CLI, using the exact launcher path returned by \`device_open\`. Keep the host config and session flags returned by \`device_open\` on every command so concurrent devices stay independent: prefer \`agent-device snapshot -i\` refs over coordinates, and use \`device_screenshot\` when you need to see the screen. Prefer these tools and \`agent-device\` for opening and driving devices. Platform tools such as \`xcrun simctl\` and \`adb\` remain available for anything they do not cover, such as builds, logs, or port forwarding. If \`device_list\` reports a platform as unavailable, say so.`;
Expand Down Expand Up @@ -215,7 +217,10 @@ export function buildCodexAdditionalContext(
const tools = toolInstructions(toolsAvailable);
// Separate keys keep each value under Codex's per-entry token cap.
return {
t3_code_orchestration: { kind: "application", value: T3_CODE_ORCHESTRATION_INSTRUCTIONS },
t3_code_orchestration: {
kind: "application",
value: `${T3_CODE_ORCHESTRATION_INSTRUCTIONS}\n${CODEX_CODE_MODE_INSTRUCTIONS}`,
},
t3_code_runtime: {
kind: "application",
value: buildRuntimeInstructions({ harness: "Codex", ...runtime }),
Expand Down
10 changes: 9 additions & 1 deletion apps/server/src/provider/T3OrchestrationInstructions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ import {
} from "./T3OrchestrationInstructions.ts";

describe("T3 orchestration provider instructions", () => {
it("keeps shared instructions provider-neutral", () => {
assert.notInclude(T3_CODE_ORCHESTRATION_INSTRUCTIONS, "acp-mcp-call");
assert.notInclude(T3_CODE_ORCHESTRATION_INSTRUCTIONS, "T3_ACP_MCP_NODE");
assert.notInclude(T3_CODE_ORCHESTRATION_INSTRUCTIONS, "mcp__t3_code__");
});

it("distinguishes delegated subagents from ordinary top-level threads", () => {
assert.include(T3_CODE_ORCHESTRATION_INSTRUCTIONS, "Use `delegate_task`");
assert.include(T3_CODE_ORCHESTRATION_INSTRUCTIONS, "ordinary top-level T3 conversations");
Expand Down Expand Up @@ -51,7 +57,7 @@ describe("T3 orchestration provider instructions", () => {
assert.equal(t3OrchestrationSystemPrompt(true), T3_CODE_ORCHESTRATION_INSTRUCTIONS);
});

it("gives ACP sessions provider-neutral mode, browser, and orchestration guidance", () => {
it("gives MCP-enabled ACP sessions mode, browser, orchestration, and fallback guidance", () => {
const injected = t3AcpPromptWithInstructions({
prompt: "Inspect the repository.",
state: { interactionMode: "default", hasT3Mcp: true },
Expand All @@ -60,6 +66,7 @@ describe("T3 orchestration provider instructions", () => {
assert.include(injected, "T3 Code interaction mode: Default");
assert.include(injected, "T3 Code collaborative browser");
assert.include(injected, "T3 Code orchestration");
assert.include(injected, "acp-mcp-call");
assert.include(injected, "<user_request>\nInspect the repository.\n</user_request>");
});

Expand All @@ -86,5 +93,6 @@ describe("T3 orchestration provider instructions", () => {
assert.include(withoutMcp, "T3 Code interaction mode: Default");
assert.notInclude(withoutMcp, "T3 Code collaborative browser");
assert.notInclude(withoutMcp, "T3 Code orchestration");
assert.notInclude(withoutMcp, "acp-mcp-call");
});
});
18 changes: 12 additions & 6 deletions apps/server/src/provider/T3OrchestrationInstructions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ For stacked work, set \`baseRef\` to the intended parent branch and \`startFromO

\`t3_thread_launch\` is the single-thread launch tool. Use \`create_threads\` only for a batch of threads intentionally sharing the caller's checkout: it always inherits the caller's project, branch, and worktree and has no workspace override. Asking an agent to run \`git worktree add\` or \`cd\` in its prompt does not update T3's thread binding. Select the workspace in the launch call instead. \`t3_worktree_handoff\` moves the calling thread, not another thread, and cannot move a thread already attached to a worktree.

\`t3_thread_launch\` has no idempotency key. Retain its returned threadId and inspect it with \`t3_thread_read\` / \`t3_thread_wait\`; preparation can still be running after acceptance. If a launch fails or its response is lost, inspect \`t3_thread_list\` before retrying, since a thread may already exist.

Tool names may include a harness-normalized MCP prefix, such as \`mcp__t3_code__delegate_task\`; the semantics are the same. Some harnesses attach optional MCP servers lazily: if an initial tool-catalog scan does not show T3 tools, do not conclude that cross-provider delegation is unavailable. Make one bounded direct attempt using the known T3 tool name on the next tool step. In Codex code mode, for example, call \`tools.mcp__t3_code__orchestrator_capabilities({})\` before reporting that the capability is absent. Keep polling/wait loops bounded, do not duplicate active work, and use stable \`clientRequestId\` values when retrying tools that accept them.

ACP fallback: some ACP agents accept the injected MCP server but fail to expose its tools. When the T3 tools are absent and \`T3_ACP_MCP_NODE\` is present, call the same tools through the terminal: \`ELECTRON_RUN_AS_NODE=1 "$T3_ACP_MCP_NODE" \${T3_ACP_MCP_ENTRYPOINT:+"$T3_ACP_MCP_ENTRYPOINT"} acp-mcp-call orchestrator_capabilities '{}'\` (\`T3_ACP_MCP_ENTRYPOINT\` is unset when T3 runs as a standalone executable). Delegate with \`acp-mcp-call delegate_task '{"task":"...","target":{"providerInstanceId":"...","model":"..."},"mode":"async","clientRequestId":"..."}'\`. This is the supported T3 transport fallback, not an ordinary shell-based substitute for delegation.
\`t3_thread_launch\` has no idempotency key. Retain its returned threadId and inspect it with \`t3_thread_read\` / \`t3_thread_wait\`; preparation can still be running after acceptance. If a launch fails or its response is lost, inspect \`t3_thread_list\` before retrying, since a thread may already exist. Keep polling/wait loops bounded, do not duplicate active work, and use stable \`clientRequestId\` values when retrying tools that accept them.

### Showing visuals

When a chart, table, diagram, image collage, or mockup would say more than prose, build a self-contained HTML page, check it with \`html_preview\`, then publish it with \`html_render\` before your final reply. The reader sees the page above that reply, so don't announce or restate it; add only what it doesn't say.

### Finding T3 tools

Tool names may include a harness-normalized MCP prefix; the semantics are the same. Some harnesses attach optional MCP servers lazily: if an initial tool-catalog scan does not show T3 tools, do not conclude that cross-provider delegation is unavailable. Make one bounded direct attempt using the known T3 tool name on the next tool step.
`;

export const T3_CODE_BROWSER_TOOL_INSTRUCTIONS = `
Expand All @@ -46,6 +46,8 @@ For browser work, first call \`preview_status\`. If no automation-capable previe
Do not switch to global browser skills, Chrome, Node REPL browser automation, standalone Playwright, or agent-browser merely because the preview is initially closed or a first call fails. Use an alternative browser system only when the T3 preview tools are absent, the user explicitly requests another browser, or \`preview_open\` returns an explicit unsupported/unavailable error. A failed T3 preview tool call should be inspected and retried with corrected arguments when the error is actionable.
`;

const T3_CODE_ACP_FALLBACK_INSTRUCTIONS = `ACP fallback: some ACP agents accept the injected MCP server but fail to expose its tools. When the T3 tools are absent and \`T3_ACP_MCP_NODE\` is present, call the same tools through the terminal: \`ELECTRON_RUN_AS_NODE=1 "$T3_ACP_MCP_NODE" \${T3_ACP_MCP_ENTRYPOINT:+"$T3_ACP_MCP_ENTRYPOINT"} acp-mcp-call orchestrator_capabilities '{}'\` (\`T3_ACP_MCP_ENTRYPOINT\` is unset when T3 runs as a standalone executable). Delegate with \`acp-mcp-call delegate_task '{"task":"...","target":{"providerInstanceId":"...","model":"..."},"mode":"async","clientRequestId":"..."}'\`. This is the supported T3 transport fallback, not an ordinary shell-based substitute for delegation.`;

const T3_CODE_ACP_DEFAULT_MODE_INSTRUCTIONS = `## T3 Code interaction mode: Default

Prefer making reasonable assumptions and carrying out the user's request. Ask a concise question only when a missing user decision would materially change the result. Treat this mode as active until T3 Code supplies a different interaction-mode instruction.`;
Expand Down Expand Up @@ -81,7 +83,11 @@ export function t3AcpPromptWithInstructions(input: {
? T3_CODE_ACP_PLAN_MODE_INSTRUCTIONS
: T3_CODE_ACP_DEFAULT_MODE_INSTRUCTIONS,
...(input.state.hasT3Mcp
? [T3_CODE_BROWSER_TOOL_INSTRUCTIONS.trim(), T3_CODE_ORCHESTRATION_INSTRUCTIONS.trim()]
? [
T3_CODE_BROWSER_TOOL_INSTRUCTIONS.trim(),
T3_CODE_ORCHESTRATION_INSTRUCTIONS.trim(),
T3_CODE_ACP_FALLBACK_INSTRUCTIONS,
]
: []),
];
return `<t3_code_instructions>\n${instructions.join("\n\n")}\n</t3_code_instructions>\n\n<user_request>\n${input.prompt}\n</user_request>`;
Expand Down
Loading