Skip to content

Commit b12bc41

Browse files
author
NGUYEN Duc Trung
committed
feat: support custom tools in subagent sessions
Enable custom tools registered on a parent session to be dispatched when called by subagent child sessions. The Copilot CLI creates separate child sessions for subagents, and those sessions carry their own session IDs that are not in the SDK's session registry. Changes: Go SDK (go/) - Track child-to-parent session mappings via subagent.started events - Implement resolveSession() to transparently route child session IDs back to the parent session that owns the tool handlers - Enforce per-agent tool allowlists (CustomAgentConfig.Tools) for both Protocol v2 (tool.call RPC) and Protocol v3 (external_tool.requested broadcast events) - Auto-populate ToolDefinitions on CustomAgentConfig in the session.create and session.resume wire payloads so the CLI can propagate custom tool metadata to child sessions - Preserve child mappings after subagent.completed/failed for in-flight requests; clean up on parent session destroy or client stop Design & Docs - Add cross-SDK design spec (docs/design/subagent-tool-dispatch.md) covering protocol contract, data model, allowlist enforcement, tool advertisement, and language-specific implementation notes - Document tool advertisement and v3 enforcement in go/README.md Tests - Unit tests for session resolution, tool allowlist enforcement, v3 broadcast deny/allow, enrichAgentToolDefinitions, and instance tracking (go/client_subagent_test.go) - Integration test scaffolding for subagent custom tool invocation and denied-tool scenarios (go/internal/e2e/subagent_tool_test.go) - Placeholder E2E snapshots (test/snapshots/subagent_tool/)
1 parent dd42d42 commit b12bc41

File tree

9 files changed

+1879
-24
lines changed

9 files changed

+1879
-24
lines changed

docs/design/subagent-tool-dispatch.md

Lines changed: 368 additions & 0 deletions
Large diffs are not rendered by default.

go/README.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,62 @@ safeLookup := copilot.DefineTool("safe_lookup", "A read-only lookup that needs n
368368
safeLookup.SkipPermission = true
369369
```
370370

371+
### Custom Tools with Subagents
372+
373+
When a session is configured with both custom tools and custom agents (subagents), the
374+
subagents can invoke the parent session's custom tools. The SDK automatically routes
375+
tool calls from child sessions back to the parent session's tool handlers.
376+
377+
#### Tool Access Control
378+
379+
The `Tools` field on `CustomAgentConfig` controls which custom tools each subagent can access:
380+
381+
| `Tools` value | Behavior |
382+
|---------------|----------|
383+
| `nil` (default) | Subagent can access **all** custom tools registered on the parent session |
384+
| `[]string{}` (empty) | Subagent cannot access **any** custom tools |
385+
| `[]string{"tool_a", "tool_b"}` | Subagent can only access the listed tools |
386+
387+
#### Example
388+
389+
```go
390+
session, err := client.CreateSession(ctx, &copilot.SessionConfig{
391+
Tools: []copilot.Tool{
392+
copilot.DefineTool("save_output", "Saves output to storage",
393+
func(params SaveParams, inv copilot.ToolInvocation) (string, error) {
394+
// Handle tool call — works for both direct and subagent invocations
395+
return saveToStorage(params.Content)
396+
}),
397+
copilot.DefineTool("get_data", "Retrieves data from storage",
398+
func(params GetParams, inv copilot.ToolInvocation) (string, error) {
399+
return getData(params.Key)
400+
}),
401+
},
402+
CustomAgents: []copilot.CustomAgentConfig{
403+
{
404+
Name: "researcher",
405+
Description: "Researches topics and saves findings",
406+
Tools: []string{"save_output"}, // Can only use save_output, not get_data
407+
Prompt: "You are a research assistant. Save your findings using save_output.",
408+
},
409+
{
410+
Name: "analyst",
411+
Description: "Analyzes data from storage",
412+
Tools: nil, // Can access ALL custom tools
413+
Prompt: "You are a data analyst.",
414+
},
415+
},
416+
})
417+
```
418+
419+
When `researcher` is invoked as a subagent, it can call `save_output` but not `get_data`.
420+
When `analyst` is invoked, it can call both tools. If a subagent attempts to use a tool
421+
not in its allowlist, the SDK returns a `"Tool '{name}' is not supported by this client instance."` response to the LLM.
422+
423+
> **Tool advertisement:** When custom agents reference tools via the `Tools` allowlist, the SDK automatically includes full tool definitions (`ToolDefinitions`) in the agent configuration sent to the CLI. This enables the CLI to propagate custom tool metadata to child sessions.
424+
>
425+
> **Protocol v3 enforcement:** Protocol v3 broadcast tool events from child sessions are subject to the same allowlist enforcement as Protocol v2 RPC tool calls.
426+
371427
## Streaming
372428

373429
Enable streaming to receive assistant response chunks as they're generated:

0 commit comments

Comments
 (0)