/** * The globally named `send_message` tool: a thin model-facing adapter over * `ctx.subagents.followup()`. It performs no lifecycle routing of its own — * residency and cold resume belong to the subagent service — and it lives apart * from the provider-bound `@deepseek-ai/dsh-tool-subagent` instances so multiple * delegation tools share one control tool. * @module @deepseek-ai/dsh-tool-subagent-control */ import type { Context } from 'cordis' import { defineTool } from '@deepseek-ai/dsh-tools' import type { ContentBlock } from '@deepseek-ai/dsh-llm' import { SessionId } from '@deepseek-ai/dsh-session' import type {} from '@deepseek-ai/dsh-subagent' export const name = 'tool-subagent-control' export const inject = ['tools', 'subagents'] /** * Register the `send_message` tool. * @param ctx - context carrying the tool registry and subagent service. */ export function apply(ctx: Context): void { ctx.tools.register(defineTool({ name: 'send_message', description: 'Send a message to a background subagent by its subagent id, continuing the same conversation. It ' + 'becomes the subagent\'s next turn: if it is still working, the message waits until its current turn ' + 'finishes, so it cannot redirect work already underway. This call returns no answer from the ' + 'subagent — only confirmation that the message was delivered — so use it to give it more work. A ' + 'failure means the message was NOT delivered.', parameters: { subagent_id: { type: 'string', required: true, description: 'The subagent id returned when the background subagent was started.', }, message: { type: 'string', required: true, description: 'The message to deliver to the subagent.', }, }, output: { schema: { type: 'object', additionalProperties: false, properties: { messageId: { type: 'string', required: true }, }, }, render: (args, _value) => [{ type: 'text', text: `message queued as the next turn for subagent ${args.subagent_id}`, }], }, async execute(args, exec) { const parent = exec.agent if (!parent) { // Parent authority requires an exact live calling agent. throw new Error('send_message requires a calling agent (exec.agent was undefined)') } const message: ContentBlock[] = [{ type: 'text', text: args.message }] const messageId = await ctx.subagents.followup( parent, SessionId(args.subagent_id), message, { source: { kind: 'coordinator', senderSessionId: parent.id }, signal: exec.signal, }, ) return { messageId } }, })) }