diff --git a/packages/agent-core-v2/docs/state-manifest.d.ts b/packages/agent-core-v2/docs/state-manifest.d.ts index c8924cd53cb..0f3da92eff2 100644 --- a/packages/agent-core-v2/docs/state-manifest.d.ts +++ b/packages/agent-core-v2/docs/state-manifest.d.ts @@ -1137,6 +1137,7 @@ export interface AgentStateSnapshot { readonly kind: 'agent'; readonly agentId?: string; readonly subagentType?: string; + readonly parentToolCallId?: string; readonly model?: string; readonly thinkingEffort?: string; readonly taskId: string; diff --git a/packages/agent-core-v2/src/agent/tools/agent/agentTool.ts b/packages/agent-core-v2/src/agent/tools/agent/agentTool.ts index 7401a36283d..fd8c4d625de 100644 --- a/packages/agent-core-v2/src/agent/tools/agent/agentTool.ts +++ b/packages/agent-core-v2/src/agent/tools/agent/agentTool.ts @@ -374,6 +374,7 @@ export class SubagentTool implements ISubagentTool { return { agentId, profileName, + parentToolCallId: toolCallId, model: displayModel, thinkingEffort: this.lifecycle .get(agentId) diff --git a/packages/agent-core-v2/src/agent/tools/agent/subagent-task.ts b/packages/agent-core-v2/src/agent/tools/agent/subagent-task.ts index 38cb41e0fa0..79a10ce1bb8 100644 --- a/packages/agent-core-v2/src/agent/tools/agent/subagent-task.ts +++ b/packages/agent-core-v2/src/agent/tools/agent/subagent-task.ts @@ -27,6 +27,7 @@ type SubagentCompletion = { export type SubagentHandle = { readonly agentId: string; readonly profileName: string; + readonly parentToolCallId?: string; readonly model?: string; readonly thinkingEffort?: string; readonly completion: Promise; @@ -36,6 +37,7 @@ export interface SubagentTaskInfo extends AgentTaskInfoBase { readonly kind: 'agent'; readonly agentId?: string; readonly subagentType?: string; + readonly parentToolCallId?: string; readonly model?: string; readonly thinkingEffort?: string; } @@ -84,6 +86,7 @@ export class SubagentTask implements AgentTask { readonly idPrefix: string = 'agent'; readonly agentId: string; readonly subagentType: string; + readonly parentToolCallId?: string; readonly model?: string; readonly thinkingEffort?: string; @@ -94,6 +97,7 @@ export class SubagentTask implements AgentTask { ) { this.agentId = handle.agentId; this.subagentType = handle.profileName; + this.parentToolCallId = handle.parentToolCallId; this.model = handle.model; this.thinkingEffort = handle.thinkingEffort; } @@ -129,6 +133,7 @@ export class SubagentTask implements AgentTask { kind: 'agent', agentId: this.agentId, subagentType: this.subagentType, + parentToolCallId: this.parentToolCallId, model: this.model, thinkingEffort: this.thinkingEffort, }; diff --git a/packages/agent-core-v2/test/agent/task/taskManager.test.ts b/packages/agent-core-v2/test/agent/task/taskManager.test.ts index 3107a361371..4d12abd434c 100644 --- a/packages/agent-core-v2/test/agent/task/taskManager.test.ts +++ b/packages/agent-core-v2/test/agent/task/taskManager.test.ts @@ -85,6 +85,7 @@ function agentTask( options: { readonly agentId?: string; readonly subagentType?: string; + readonly parentToolCallId?: string; readonly abortController?: AbortController; readonly timeoutMs?: number; } = {}, @@ -92,6 +93,7 @@ function agentTask( const handle: SubagentHandle = { agentId: options.agentId ?? 'agent-child', profileName: options.subagentType ?? 'coder', + parentToolCallId: options.parentToolCallId, completion, }; const task = new SubagentTask( @@ -410,6 +412,7 @@ describe('AgentTaskService', () => { agentTask(new Promise(() => {}), 'investigate bug', { agentId: 'agent-child', subagentType: 'coder', + parentToolCallId: 'call-parent-1', }), ); @@ -420,6 +423,7 @@ describe('AgentTaskService', () => { description: 'investigate bug', agentId: 'agent-child', subagentType: 'coder', + parentToolCallId: 'call-parent-1', status: 'running', }); }); diff --git a/packages/kap-server/src/protocol/task.ts b/packages/kap-server/src/protocol/task.ts index e3e64681208..8e7bfd737d5 100644 --- a/packages/kap-server/src/protocol/task.ts +++ b/packages/kap-server/src/protocol/task.ts @@ -31,5 +31,7 @@ export const taskSchema = z.object({ /** Subagent tasks only: the child's effective thinking effort at spawn. */ thinking_effort: z.string().optional(), agent_id: z.string().optional(), + subagent_type: z.string().optional(), + parent_tool_call_id: z.string().optional(), }); export type Task = z.infer; diff --git a/packages/kap-server/src/routes/tasks.ts b/packages/kap-server/src/routes/tasks.ts index dd22740f0e9..744b54e90e4 100644 --- a/packages/kap-server/src/routes/tasks.ts +++ b/packages/kap-server/src/routes/tasks.ts @@ -378,6 +378,12 @@ function toWireTask( if (info.kind === 'agent' && info.agentId !== undefined) { base.agent_id = info.agentId; } + if (info.kind === 'agent' && info.subagentType !== undefined) { + base.subagent_type = info.subagentType; + } + if (info.kind === 'agent' && info.parentToolCallId !== undefined) { + base.parent_tool_call_id = info.parentToolCallId; + } if (output !== undefined) { base.output_preview = output.preview; base.output_bytes = output.bytes; diff --git a/packages/kap-server/test/tasks.test.ts b/packages/kap-server/test/tasks.test.ts index 791b70cd07e..cd686ab87b4 100644 --- a/packages/kap-server/test/tasks.test.ts +++ b/packages/kap-server/test/tasks.test.ts @@ -36,6 +36,8 @@ interface TaskWire { output_preview?: string; output_bytes?: number; agent_id?: string; + subagent_type?: string; + parent_tool_call_id?: string; } interface ListWire { @@ -159,6 +161,7 @@ describe('server-v2 /api/v1/sessions/{sid}/tasks', () => { kind: 'agent', agentId: 'sub-1', subagentType: 'explore', + parentToolCallId: 'call-parent-1', model: 'provider/secondary', thinkingEffort: 'low', }; @@ -216,6 +219,8 @@ describe('server-v2 /api/v1/sessions/{sid}/tasks', () => { model: 'provider/secondary', // subagent tasks expose the bound display model thinking_effort: 'low', // …and its effective thinking effort agent_id: 'sub-1', + subagent_type: 'explore', + parent_tool_call_id: 'call-parent-1', }); expect(byId.get(agentId)?.command).toBeUndefined(); @@ -227,6 +232,10 @@ describe('server-v2 /api/v1/sessions/{sid}/tasks', () => { }); expect(byId.get(processId)?.agent_id).toBeUndefined(); expect(byId.get(questionId)?.agent_id).toBeUndefined(); + expect(byId.get(processId)?.subagent_type).toBeUndefined(); + expect(byId.get(questionId)?.subagent_type).toBeUndefined(); + expect(byId.get(processId)?.parent_tool_call_id).toBeUndefined(); + expect(byId.get(questionId)?.parent_tool_call_id).toBeUndefined(); }); it('filters the list by wire status', async () => { @@ -264,6 +273,8 @@ describe('server-v2 /api/v1/sessions/{sid}/tasks', () => { session_id: id, kind: 'subagent', agent_id: 'sub-1', + subagent_type: 'explore', + parent_tool_call_id: 'call-parent-1', }); const missing = await getJson(`/api/v1/sessions/${id}/tasks/nope`);