{"version":3,"file":"agent-delegate.mjs","names":[],"sources":["../../../src/tools/agent-delegate.ts"],"sourcesContent":["/**\n * Agent Delegate Tool — delegates tasks to specialized sub-agents.\n *\n * The main LLM calls this tool to spin up a sub-agent with a custom system\n * prompt, restricted tool access, and a focused task description. The\n * sub-agent runs its own LLM loop (possibly with tool calls), then returns\n * a structured result to the main agent.\n *\n * Actions:\n *   delegate  — Run a task with a named sub-agent\n *   list      — List available sub-agents and their specialties\n *   status    — Check if an API key is configured for sub-agent execution\n */\n\nimport { Type } from '@sinclair/typebox';\nimport { stringEnum, jsonResult, errorResult, readStringParam } from '../lib/tool-helpers.js';\nimport { getAgentPool } from '../services/agent-pool.js';\nimport { executeSubAgent, detectProvider, getApiKey } from '../services/agent-orchestrator.js';\nimport { isDelegationMode } from '../services/policy-types.js';\nimport type { ToolDispatcher } from '../services/sandbox-runtime.js';\n\nconst ACTIONS = ['delegate', 'list', 'status'] as const;\n\nconst AgentDelegateSchema = Type.Object({\n  action: stringEnum(ACTIONS, {\n    description:\n      'delegate: run a task with a sub-agent. ' +\n      'list: show available sub-agents. ' +\n      'status: check sub-agent readiness.',\n  }),\n  agent: Type.Optional(Type.String({\n    description: 'Sub-agent name (e.g. \"strategist\", \"analyst\", \"accountant\", \"risk_manager\") for delegate action.',\n  })),\n  task: Type.Optional(Type.String({\n    description: 'The task description to delegate to the sub-agent. Be specific about what you want analyzed or done.',\n  })),\n});\n\n/**\n * Factory: creates the agent_delegate tool.\n *\n * Requires a dispatcher (for sub-agents to call tools) and a function\n * to get the list of registered tools (for building sub-agent tool schemas).\n */\nexport function createAgentDelegateTool(\n  getDispatcher: () => ToolDispatcher,\n  getRegisteredTools: () => Array<{ name: string; description: string; parameters: any }>,\n) {\n  return {\n    name: 'agent_delegate',\n    label: 'Agent Delegate',\n    ownerOnly: false,\n    description:\n      'Delegate tasks to specialized sub-agents (strategist, analyst, accountant, risk_manager). ' +\n      'Sub-agents run their own LLM with focused expertise and restricted tool access. ' +\n      'Use \"list\" to see available agents, \"delegate\" to run a task.',\n    parameters: AgentDelegateSchema,\n\n    execute: async (_toolCallId: string, args: unknown) => {\n      const params = args as Record<string, unknown>;\n      const action = readStringParam(params, 'action', { required: true })!;\n\n      switch (action) {\n        case 'delegate':\n          return handleDelegate(params, getDispatcher, getRegisteredTools);\n        case 'list':\n          return handleList();\n        case 'status':\n          return handleStatus();\n        default:\n          return errorResult(`Unknown action: ${action}. Use: ${ACTIONS.join(', ')}`);\n      }\n    },\n  };\n}\n\n// ─── Action Handlers ────────────────────────────────────────────────────\n\nasync function handleDelegate(\n  params: Record<string, unknown>,\n  getDispatcher: () => ToolDispatcher,\n  getRegisteredTools: () => Array<{ name: string; description: string; parameters: any }>,\n) {\n  const agentName = readStringParam(params, 'agent');\n  const task = readStringParam(params, 'task');\n\n  if (!agentName) return errorResult('Missing required parameter: agent (name of the sub-agent).');\n  if (!task) return errorResult('Missing required parameter: task (description of what to do).');\n\n  const pool = getAgentPool();\n  const agent = pool.getByName(agentName);\n\n  if (!agent) {\n    const available = pool.getEnabledAgents().map(a => a.name).join(', ');\n    return errorResult(`No sub-agent named \"${agentName}\". Available: ${available}`);\n  }\n\n  if (!agent.enabled) {\n    return errorResult(`Sub-agent \"${agentName}\" is disabled. Use /agents enable ${agentName} to re-enable.`);\n  }\n\n  // Check API key availability\n  const provider = detectProvider();\n  const apiKey = getApiKey(provider);\n  if (!apiKey) {\n    return errorResult(\n      `No LLM API key available for provider \"${provider}\". ` +\n      `Set ANTHROPIC_API_KEY, OPENROUTER_API_KEY, OPENAI_API_KEY, or BANKR_LLM_KEY.`\n    );\n  }\n\n  // Assign ephemeral wallet for sub-delegation (if in delegation mode)\n  let subDelegationInfo: Record<string, unknown> | undefined;\n  if (isDelegationMode()) {\n    try {\n      const wallet = await pool.assignEphemeralWallet(agent.id);\n      if (wallet) {\n        // Attempt to create sub-delegation from any active parent delegation\n        const { getDelegatedPolicies, createSubDelegation } = await import('../services/delegation-service.js');\n        const { getDelegationStore } = await import('../services/delegation-store.js');\n\n        const delegatedPolicies = getDelegatedPolicies('owner');\n        const delegationStore = getDelegationStore();\n\n        for (const policy of delegatedPolicies) {\n          if (!policy.delegation) continue;\n          if (policy.delegation.status !== 'signed' && policy.delegation.status !== 'active') continue;\n\n          const stored = delegationStore.load(policy.id);\n          if (!stored) continue;\n\n          const parentHash = policy.delegation.hash;\n          if (!parentHash || parentHash === '0x') continue;\n\n          const subResult = await createSubDelegation({\n            parentDelegation: stored.delegation,\n            parentHash: parentHash as `0x${string}`,\n            chainId: stored.chainId,\n            subAgentAddress: wallet.address,\n            subAgentPrivateKey: wallet.privateKey,\n          });\n\n          if ('error' in subResult) {\n            // Sub-delegation failed — continue without it\n            subDelegationInfo = { error: subResult.error };\n          } else {\n            subDelegationInfo = {\n              subAgentAddress: wallet.address,\n              parentPolicy: policy.name,\n              chainId: stored.chainId,\n              caveatCount: subResult.delegation.caveats.length,\n            };\n            agent.parentDelegationHash = parentHash as `0x${string}`;\n          }\n          break; // Use the first matching delegation\n        }\n      }\n    } catch {\n      // Sub-delegation is best-effort — don't block execution\n    }\n  }\n\n  // Execute\n  const result = await executeSubAgent(\n    agent,\n    task,\n    getDispatcher(),\n    getRegisteredTools(),\n  );\n\n  // Record usage\n  pool.recordUsage(agent.id);\n\n  return jsonResult({\n    agent: agent.name,\n    model: agent.model,\n    status: result.status,\n    response: result.response,\n    toolCallCount: result.toolCalls.length,\n    toolCalls: result.toolCalls.map(tc => ({\n      tool: tc.tool,\n      durationMs: tc.durationMs,\n      resultPreview: tc.result.slice(0, 100),\n    })),\n    tokensUsed: result.tokensUsed,\n    durationMs: result.durationMs,\n    ...(result.error ? { error: result.error } : {}),\n    ...(subDelegationInfo ? { subDelegation: subDelegationInfo } : {}),\n  });\n}\n\nasync function handleList() {\n  const pool = getAgentPool();\n  const agents = pool.list();\n\n  if (agents.length === 0) {\n    return jsonResult({ agents: [], message: 'No sub-agents defined.' });\n  }\n\n  return jsonResult({\n    agents: agents.map(a => ({\n      name: a.name,\n      label: a.label,\n      description: a.description,\n      model: a.model,\n      enabled: a.enabled,\n      isPreset: a.isPreset,\n      allowedTools: a.allowedTools,\n      usageCount: a.usageCount,\n    })),\n  });\n}\n\nasync function handleStatus() {\n  const provider = detectProvider();\n  const hasKey = !!getApiKey(provider);\n  const pool = getAgentPool();\n  const enabled = pool.getEnabledAgents();\n\n  return jsonResult({\n    ready: hasKey,\n    provider,\n    hasApiKey: hasKey,\n    enabledAgents: enabled.length,\n    agents: enabled.map(a => a.name),\n    message: hasKey\n      ? `Sub-agent system ready. Provider: ${provider}. ${enabled.length} agent(s) available.`\n      : `No API key for \"${provider}\". Set an LLM API key to enable sub-agents.`,\n  });\n}\n"],"mappings":"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"}