You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Copilot ACP requires a lot of custom instructions to work properly in T3Code.
#20933
Making GitHub Copilot CLI (ACP) work well inside T3 Code
TL;DR
Running GitHub Copilot CLI as an ACP agent in T3 Code (the acpRegistry_copilot provider) works, but several T3 features failed or were silently skipped until we added a fairly long block to Copilot's custom instructions (~/.copilot/instructions/*.instructions.md). This post lists each problem, the workaround, and the full instruction block. At the end are some ideas that could make most of these workarounds unnecessary.
Setup: T3 Code (WSL runtime) → Copilot CLI over ACP → Claude/GPT models. Other providers (Claude Code, OpenCode) are configured as delegation targets.
1. Final answers were invisible
Symptom: Copilot often ended a turn by putting the real answer in its task_complete summary, or in text sent in the same message as a tool call. T3 Code doesn't render either, so the user just saw the turn end with no answer.
Workaround: Make Copilot send the answer as a plain text message with no tool calls, and only then call task_complete in a separate response.
2. T3 MCP tools aren't exposed as native tools
Symptom: T3 injects the t3-code MCP server, but in the Copilot ACP harness its tools (orchestrator_capabilities, delegate_task, task_status, t3_thread_launch, link_pull_request, …) never show up in the agent's tool list. Copilot then reported "T3 Code is not available" and fell back to its own tools.
Workaround: Use T3's ACP fallback bridge through bash, and tell the agent that missing native tools does not mean T3 is unavailable:
The output is JSON; the useful payload is in structuredContent.
Bad arguments return -32602 and name the missing key.
Long prompts broke because of shell quoting. Building the JSON with python3 -c 'import json; print(json.dumps(...))' fixed it.
The agent should only report T3 as unavailable if T3_ACP_MCP_NODE is unset and there are no native T3 tools.
3. Subagents didn't show up in T3
Symptom: When asked for "a Claude Code Sonnet subagent", Copilot used its built-in task tool. Those agents run inside Copilot and are invisible in T3's UI. When it did try delegate_task, it often guessed wrong provider or model ids.
Workaround:
If the user names a provider/model or asks for T3 subagents, use delegate_task, not native task.
Call orchestrator_capabilities {} first to get exact ids, and only use providers with canRunChildTask: true.
Give a name → id mapping, because the display names don't match the ids:
"Claude Code X" → claudeAgent, model ids use dashes (claude-sonnet-5-5, not 5.5)
"OpenCode X" → opencode, ids like github-copilot/gpt-6.1-sol
"Copilot X" → acpRegistry_copilot
Show the exact argument shape: {"task":"…","target":{"providerInstanceId":"…","model":"…"},"mode":"async","clientRequestId":"<unique per task+round, stable on retry>"}
Task prompts must be self-contained, since the child doesn't see the parent conversation.
4. Turns ended while delegated tasks were still running
Symptom: Copilot would say "I'll combine the results when they finish" and end its turn, so the results were never collected. Other times it polled in many separate tool calls.
Workaround: Wait in one bounded bash loop. Poll task_status every ~10 s for up to ~6 min, until status is no longer running (completed / result_available). Read the result from structuredContent.summary, falling back to latestTerminalSummary. Use task_cancel for stuck tasks. Never end the turn while delegated tasks are still running.
5. Wrong tools for follow-ups and new threads
Symptom: For review round 2, Copilot sent a message to the child's childThreadId with t3_thread_send. It also sometimes used t3_thread_launch when the user only said "subagent".
Workaround:
Each follow-up round is a new delegate_task call with a new clientRequestId and the full prior context. Never use t3_thread_send on a childThreadId.
Use t3_thread_launch / create_threads only when the user explicitly asks for a new or separate thread. Put the task in message, not prompt, and set workspaceStrategy, e.g. {"type":"root"}.
6. Provider handoff messages were treated as new requests
Symptom: After switching a thread to Copilot from another provider, the first message starts with Context handoff (full_thread_summary). Copilot treated the whole summary as the task. It also believed claims in the summary like "T3 tools are unavailable" (true for the previous provider, false for this one).
Workaround: Treat the handoff as history and act only on the trailing "User message". Recover omitted history with t3_thread_read through the bridge, and re-check any claims that T3 tools are unavailable.
7. Threads stayed marked as "working"
Symptom: Background shells left running by Copilot (dev servers, watchers) kept the T3 thread showing as working after the turn ended.
Workaround: Before ending a turn, run list_bash and stop every shell that's still running, unless the user asked to keep it.
8. PRs weren't linked to the thread
Symptom: PRs created with gh weren't registered with the T3 thread, because link_pull_request wasn't reachable natively.
Workaround: After creating or working on a PR, call link_pull_request with the full PR URL through the bridge.
The full instruction block we use
Click to expand
# Final answers (always, every environment)
The `task_complete` summary and any text in the same response as a tool call are NOT shown to the user (T3 Code hides them). Never put the answer only in the summary. To finish a task:
1. Post the full user-facing answer or results as a plain text response with NO tool calls.
2. Only then, in the next response, call `task_complete` with a one-line summary.
This applies to every task, including quick questions and answers.
# T3 Code
Applies when runtime info says you run in T3 Code, or `T3_ACP_MCP_NODE` is set.
## Calling T3 tools (ACP bridge)
In the Copilot (acpRegistry) harness, the `t3-code` MCP tools (`orchestrator_capabilities`, `delegate_task`, `task_status`, `task_cancel`, `t3_thread_launch`, `t3_thread_read`, `link_pull_request`, ...) are NOT native tools. Missing native tools does not mean T3 is unavailable. Call them through bash:
ELECTRON_RUN_AS_NODE=1 "$T3_ACP_MCP_NODE" ${T3_ACP_MCP_ENTRYPOINT:+"$T3_ACP_MCP_ENTRYPOINT"} acp-mcp-call <tool> '<json-args>'- Output is JSON; read `structuredContent`. Invalid args fail with `-32602` naming the missing key.
- Build JSON for long prompts with `python3 -c 'import json,sys; print(json.dumps(...))'` instead of hand-escaping.
- Only report T3 as unavailable if `T3_ACP_MCP_NODE` is unset AND no native T3 tools exist.
## Subagents via T3- If the user names a provider/model (e.g. "Claude Code Sonnet 5.5", "OpenCode GPT-6.1 Sol") or asks for T3 subagents, use `delegate_task`, not the native `task` tool. Native `task` agents are invisible to T3's UI.
- First call `orchestrator_capabilities``{}` to get exact ids. Mapping: "Claude Code X" → `claudeAgent`, ids use dashes (`claude-sonnet-5-5`); "OpenCode X" → `opencode`, ids like `github-copilot/gpt-6.1-sol`; "Copilot X" → `acpRegistry_copilot`. Only use providers with `canRunChildTask: true`.
-`delegate_task` args: `{"task":"<self-contained prompt>","target":{"providerInstanceId":"...","model":"..."},"mode":"async","clientRequestId":"<unique-per-task-and-round, stable on retry>"}`. Keep the returned `taskId`.
- Task prompts must be self-contained: full context, read-only constraints if applicable, and the expected output format.
- Wait in a single bounded bash loop: poll `task_status``{"taskId":"..."}` every ~10s (cap ~6 min) until `status` is no longer `running`. Done = `completed` / `result_available`. Result text is in `structuredContent.summary` (fallback `latestTerminalSummary`). Use `task_cancel` for stuck tasks.
- Never end the turn while delegated tasks are running unless the user asked for that. Don't write "I'll combine when they finish" and then keep working silently.
- Follow-up/review rounds: call `delegate_task` again with a new `clientRequestId` and the full prior context. Never `t3_thread_send` to a `childThreadId`.
-`t3_thread_launch` / `create_threads` create top-level threads; use only when the user explicitly asks for a new/separate thread. Put the task in `message` and set `workspaceStrategy` (e.g. `{"type":"root"}`).
## Provider handoff
A first message starting with "Context handoff (full_thread_summary)" is history from a previous provider, not a new request. Act on the trailing "User message". Recover omitted history with `t3_thread_read` (via the bridge) using the thread id and pagination hints in the handoff. Claims in the handoff that T3 tools were unavailable may be stale; re-check via the bridge.
# Commands
Before ending a turn, run `list_bash` and stop (`stop_bash`) every shell you started that is still running, unless the user asked for it to keep running. A running shell keeps the T3 thread marked as working.
Ideas that would make most of this unnecessary
Expose t3-code MCP tools natively to Copilot ACP sessions, or detect when they're missing and automatically add the acp-mcp-call instructions to the system prompt. This one change would remove sections 2, 3, 6 (partly), and 8.
Show task_complete summaries and text sent alongside tool calls, at least as a fallback when the turn has no other assistant text (section 1).
Accept model aliases in delegate_task, e.g. claude-sonnet-5.5 → claude-sonnet-5-5, or return a "did you mean" error listing valid ids.
Add a blocking task_wait tool with a timeout, so agents don't need hand-written polling loops (section 4).
Mark handoff messages explicitly, e.g. as a separate system/context block instead of the user message, and leave out provider-specific "tools unavailable" claims (section 6).
Show native Copilot task subagents in T3's UI, or document clearly that only delegate_task children are visible.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Making GitHub Copilot CLI (ACP) work well inside T3 Code
TL;DR
Running GitHub Copilot CLI as an ACP agent in T3 Code (the
acpRegistry_copilotprovider) works, but several T3 features failed or were silently skipped until we added a fairly long block to Copilot's custom instructions (~/.copilot/instructions/*.instructions.md). This post lists each problem, the workaround, and the full instruction block. At the end are some ideas that could make most of these workarounds unnecessary.Setup: T3 Code (WSL runtime) → Copilot CLI over ACP → Claude/GPT models. Other providers (Claude Code, OpenCode) are configured as delegation targets.
1. Final answers were invisible
Symptom: Copilot often ended a turn by putting the real answer in its
task_completesummary, or in text sent in the same message as a tool call. T3 Code doesn't render either, so the user just saw the turn end with no answer.Workaround: Make Copilot send the answer as a plain text message with no tool calls, and only then call
task_completein a separate response.2. T3 MCP tools aren't exposed as native tools
Symptom: T3 injects the
t3-codeMCP server, but in the Copilot ACP harness its tools (orchestrator_capabilities,delegate_task,task_status,t3_thread_launch,link_pull_request, …) never show up in the agent's tool list. Copilot then reported "T3 Code is not available" and fell back to its own tools.Workaround: Use T3's ACP fallback bridge through bash, and tell the agent that missing native tools does not mean T3 is unavailable:
structuredContent.-32602and name the missing key.python3 -c 'import json; print(json.dumps(...))'fixed it.T3_ACP_MCP_NODEis unset and there are no native T3 tools.3. Subagents didn't show up in T3
Symptom: When asked for "a Claude Code Sonnet subagent", Copilot used its built-in
tasktool. Those agents run inside Copilot and are invisible in T3's UI. When it did trydelegate_task, it often guessed wrong provider or model ids.Workaround:
delegate_task, not nativetask.orchestrator_capabilities {}first to get exact ids, and only use providers withcanRunChildTask: true.claudeAgent, model ids use dashes (claude-sonnet-5-5, not5.5)opencode, ids likegithub-copilot/gpt-6.1-solacpRegistry_copilot{"task":"…","target":{"providerInstanceId":"…","model":"…"},"mode":"async","clientRequestId":"<unique per task+round, stable on retry>"}4. Turns ended while delegated tasks were still running
Symptom: Copilot would say "I'll combine the results when they finish" and end its turn, so the results were never collected. Other times it polled in many separate tool calls.
Workaround: Wait in one bounded bash loop. Poll
task_statusevery ~10 s for up to ~6 min, untilstatusis no longerrunning(completed/result_available). Read the result fromstructuredContent.summary, falling back tolatestTerminalSummary. Usetask_cancelfor stuck tasks. Never end the turn while delegated tasks are still running.5. Wrong tools for follow-ups and new threads
Symptom: For review round 2, Copilot sent a message to the child's
childThreadIdwitht3_thread_send. It also sometimes usedt3_thread_launchwhen the user only said "subagent".Workaround:
delegate_taskcall with a newclientRequestIdand the full prior context. Never uset3_thread_sendon achildThreadId.t3_thread_launch/create_threadsonly when the user explicitly asks for a new or separate thread. Put the task inmessage, notprompt, and setworkspaceStrategy, e.g.{"type":"root"}.6. Provider handoff messages were treated as new requests
Symptom: After switching a thread to Copilot from another provider, the first message starts with
Context handoff (full_thread_summary). Copilot treated the whole summary as the task. It also believed claims in the summary like "T3 tools are unavailable" (true for the previous provider, false for this one).Workaround: Treat the handoff as history and act only on the trailing "User message". Recover omitted history with
t3_thread_readthrough the bridge, and re-check any claims that T3 tools are unavailable.7. Threads stayed marked as "working"
Symptom: Background shells left running by Copilot (dev servers, watchers) kept the T3 thread showing as working after the turn ended.
Workaround: Before ending a turn, run
list_bashand stop every shell that's still running, unless the user asked to keep it.8. PRs weren't linked to the thread
Symptom: PRs created with
ghweren't registered with the T3 thread, becauselink_pull_requestwasn't reachable natively.Workaround: After creating or working on a PR, call
link_pull_requestwith the full PR URL through the bridge.The full instruction block we use
Click to expand
Ideas that would make most of this unnecessary
t3-codeMCP tools natively to Copilot ACP sessions, or detect when they're missing and automatically add theacp-mcp-callinstructions to the system prompt. This one change would remove sections 2, 3, 6 (partly), and 8.task_completesummaries and text sent alongside tool calls, at least as a fallback when the turn has no other assistant text (section 1).delegate_task, e.g.claude-sonnet-5.5→claude-sonnet-5-5, or return a "did you mean" error listing valid ids.task_waittool with a timeout, so agents don't need hand-written polling loops (section 4).tasksubagents in T3's UI, or document clearly that onlydelegate_taskchildren are visible.Happy to share more details or test builds.
All reactions