attocode 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +169 -3
- package/README.md +65 -5
- package/dist/src/adapters.d.ts.map +1 -1
- package/dist/src/adapters.js +15 -11
- package/dist/src/adapters.js.map +1 -1
- package/dist/src/agent.d.ts +44 -98
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +716 -2648
- package/dist/src/agent.js.map +1 -1
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +25 -3
- package/dist/src/cli.js.map +1 -1
- package/dist/src/commands/handler.d.ts.map +1 -1
- package/dist/src/commands/handler.js +11 -3
- package/dist/src/commands/handler.js.map +1 -1
- package/dist/src/commands/init-commands.d.ts.map +1 -1
- package/dist/src/commands/init-commands.js +16 -1
- package/dist/src/commands/init-commands.js.map +1 -1
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +31 -0
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/config/base-types.d.ts +45 -0
- package/dist/src/config/base-types.d.ts.map +1 -0
- package/dist/src/config/base-types.js +9 -0
- package/dist/src/config/base-types.js.map +1 -0
- package/dist/src/config/config-manager.d.ts +35 -0
- package/dist/src/config/config-manager.d.ts.map +1 -0
- package/dist/src/config/config-manager.js +108 -0
- package/dist/src/config/config-manager.js.map +1 -0
- package/dist/src/config/index.d.ts +4 -0
- package/dist/src/config/index.d.ts.map +1 -0
- package/dist/src/config/index.js +3 -0
- package/dist/src/config/index.js.map +1 -0
- package/dist/src/config/schema.d.ts +1546 -0
- package/dist/src/config/schema.d.ts.map +1 -0
- package/dist/src/config/schema.js +268 -0
- package/dist/src/config/schema.js.map +1 -0
- package/dist/src/config.d.ts +4 -1
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +8 -12
- package/dist/src/config.js.map +1 -1
- package/dist/src/core/agent-state-machine.d.ts +131 -0
- package/dist/src/core/agent-state-machine.d.ts.map +1 -0
- package/dist/src/core/agent-state-machine.js +302 -0
- package/dist/src/core/agent-state-machine.js.map +1 -0
- package/dist/src/core/base-manager.d.ts +79 -0
- package/dist/src/core/base-manager.d.ts.map +1 -0
- package/dist/src/core/base-manager.js +170 -0
- package/dist/src/core/base-manager.js.map +1 -0
- package/dist/src/core/completion-analyzer.d.ts +15 -0
- package/dist/src/core/completion-analyzer.d.ts.map +1 -0
- package/dist/src/core/completion-analyzer.js +53 -0
- package/dist/src/core/completion-analyzer.js.map +1 -0
- package/dist/src/core/execution-loop.d.ts +46 -0
- package/dist/src/core/execution-loop.d.ts.map +1 -0
- package/dist/src/core/execution-loop.js +1258 -0
- package/dist/src/core/execution-loop.js.map +1 -0
- package/dist/src/core/index.d.ts +7 -0
- package/dist/src/core/index.d.ts.map +1 -1
- package/dist/src/core/index.js +9 -0
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/core/process-handlers.d.ts.map +1 -1
- package/dist/src/core/process-handlers.js +14 -0
- package/dist/src/core/process-handlers.js.map +1 -1
- package/dist/src/core/protocol/types.d.ts +4 -4
- package/dist/src/core/response-handler.d.ts +16 -0
- package/dist/src/core/response-handler.d.ts.map +1 -0
- package/dist/src/core/response-handler.js +234 -0
- package/dist/src/core/response-handler.js.map +1 -0
- package/dist/src/core/subagent-spawner.d.ts +43 -0
- package/dist/src/core/subagent-spawner.d.ts.map +1 -0
- package/dist/src/core/subagent-spawner.js +966 -0
- package/dist/src/core/subagent-spawner.js.map +1 -0
- package/dist/src/core/tool-executor.d.ts +59 -0
- package/dist/src/core/tool-executor.d.ts.map +1 -0
- package/dist/src/core/tool-executor.js +677 -0
- package/dist/src/core/tool-executor.js.map +1 -0
- package/dist/src/core/types.d.ts +133 -0
- package/dist/src/core/types.d.ts.map +1 -0
- package/dist/src/core/types.js +12 -0
- package/dist/src/core/types.js.map +1 -0
- package/dist/src/defaults.d.ts +8 -3
- package/dist/src/defaults.d.ts.map +1 -1
- package/dist/src/defaults.js +65 -3
- package/dist/src/defaults.js.map +1 -1
- package/dist/src/integrations/agent-registry.d.ts +11 -0
- package/dist/src/integrations/agent-registry.d.ts.map +1 -1
- package/dist/src/integrations/agent-registry.js.map +1 -1
- package/dist/src/integrations/auto-compaction.d.ts.map +1 -1
- package/dist/src/integrations/auto-compaction.js +8 -3
- package/dist/src/integrations/auto-compaction.js.map +1 -1
- package/dist/src/integrations/bash-policy.d.ts +33 -0
- package/dist/src/integrations/bash-policy.d.ts.map +1 -0
- package/dist/src/integrations/bash-policy.js +142 -0
- package/dist/src/integrations/bash-policy.js.map +1 -0
- package/dist/src/integrations/budget-pool.d.ts +7 -0
- package/dist/src/integrations/budget-pool.d.ts.map +1 -1
- package/dist/src/integrations/budget-pool.js +43 -0
- package/dist/src/integrations/budget-pool.js.map +1 -1
- package/dist/src/integrations/codebase-ast.d.ts +52 -0
- package/dist/src/integrations/codebase-ast.d.ts.map +1 -0
- package/dist/src/integrations/codebase-ast.js +457 -0
- package/dist/src/integrations/codebase-ast.js.map +1 -0
- package/dist/src/integrations/codebase-context.d.ts +23 -0
- package/dist/src/integrations/codebase-context.d.ts.map +1 -1
- package/dist/src/integrations/codebase-context.js +230 -17
- package/dist/src/integrations/codebase-context.js.map +1 -1
- package/dist/src/integrations/compaction.d.ts.map +1 -1
- package/dist/src/integrations/compaction.js +14 -6
- package/dist/src/integrations/compaction.js.map +1 -1
- package/dist/src/integrations/context-engineering.d.ts +8 -0
- package/dist/src/integrations/context-engineering.d.ts.map +1 -1
- package/dist/src/integrations/context-engineering.js +19 -0
- package/dist/src/integrations/context-engineering.js.map +1 -1
- package/dist/src/integrations/delegation-protocol.js +2 -2
- package/dist/src/integrations/delegation-protocol.js.map +1 -1
- package/dist/src/integrations/economics.d.ts +67 -1
- package/dist/src/integrations/economics.d.ts.map +1 -1
- package/dist/src/integrations/economics.js +328 -33
- package/dist/src/integrations/economics.js.map +1 -1
- package/dist/src/integrations/edit-validator.d.ts +30 -0
- package/dist/src/integrations/edit-validator.d.ts.map +1 -0
- package/dist/src/integrations/edit-validator.js +85 -0
- package/dist/src/integrations/edit-validator.js.map +1 -0
- package/dist/src/integrations/file-cache.d.ts +7 -0
- package/dist/src/integrations/file-cache.d.ts.map +1 -1
- package/dist/src/integrations/file-cache.js +54 -0
- package/dist/src/integrations/file-cache.js.map +1 -1
- package/dist/src/integrations/health-check.d.ts.map +1 -1
- package/dist/src/integrations/health-check.js +3 -2
- package/dist/src/integrations/health-check.js.map +1 -1
- package/dist/src/integrations/hierarchical-config.d.ts +3 -0
- package/dist/src/integrations/hierarchical-config.d.ts.map +1 -1
- package/dist/src/integrations/hierarchical-config.js +20 -0
- package/dist/src/integrations/hierarchical-config.js.map +1 -1
- package/dist/src/integrations/hooks.d.ts +2 -0
- package/dist/src/integrations/hooks.d.ts.map +1 -1
- package/dist/src/integrations/hooks.js +99 -15
- package/dist/src/integrations/hooks.js.map +1 -1
- package/dist/src/integrations/index.d.ts +10 -1
- package/dist/src/integrations/index.d.ts.map +1 -1
- package/dist/src/integrations/index.js +12 -2
- package/dist/src/integrations/index.js.map +1 -1
- package/dist/src/integrations/logger.d.ts +104 -0
- package/dist/src/integrations/logger.d.ts.map +1 -0
- package/dist/src/integrations/logger.js +219 -0
- package/dist/src/integrations/logger.js.map +1 -0
- package/dist/src/integrations/lsp.d.ts.map +1 -1
- package/dist/src/integrations/lsp.js +5 -4
- package/dist/src/integrations/lsp.js.map +1 -1
- package/dist/src/integrations/mcp-client.d.ts.map +1 -1
- package/dist/src/integrations/mcp-client.js +8 -7
- package/dist/src/integrations/mcp-client.js.map +1 -1
- package/dist/src/integrations/observability.d.ts.map +1 -1
- package/dist/src/integrations/observability.js +5 -4
- package/dist/src/integrations/observability.js.map +1 -1
- package/dist/src/integrations/openrouter-pricing.d.ts.map +1 -1
- package/dist/src/integrations/openrouter-pricing.js +4 -3
- package/dist/src/integrations/openrouter-pricing.js.map +1 -1
- package/dist/src/integrations/persistence.d.ts.map +1 -1
- package/dist/src/integrations/persistence.js +5 -4
- package/dist/src/integrations/persistence.js.map +1 -1
- package/dist/src/integrations/planning.d.ts.map +1 -1
- package/dist/src/integrations/planning.js +5 -4
- package/dist/src/integrations/planning.js.map +1 -1
- package/dist/src/integrations/policy-engine.d.ts +55 -0
- package/dist/src/integrations/policy-engine.d.ts.map +1 -0
- package/dist/src/integrations/policy-engine.js +247 -0
- package/dist/src/integrations/policy-engine.js.map +1 -0
- package/dist/src/integrations/retry.d.ts +1 -0
- package/dist/src/integrations/retry.d.ts.map +1 -1
- package/dist/src/integrations/retry.js.map +1 -1
- package/dist/src/integrations/routing.d.ts.map +1 -1
- package/dist/src/integrations/routing.js +2 -1
- package/dist/src/integrations/routing.js.map +1 -1
- package/dist/src/integrations/safety.d.ts +5 -4
- package/dist/src/integrations/safety.d.ts.map +1 -1
- package/dist/src/integrations/safety.js +45 -20
- package/dist/src/integrations/safety.js.map +1 -1
- package/dist/src/integrations/sandbox/basic.d.ts +7 -0
- package/dist/src/integrations/sandbox/basic.d.ts.map +1 -1
- package/dist/src/integrations/sandbox/basic.js +27 -2
- package/dist/src/integrations/sandbox/basic.js.map +1 -1
- package/dist/src/integrations/sandbox/docker.d.ts.map +1 -1
- package/dist/src/integrations/sandbox/docker.js +2 -1
- package/dist/src/integrations/sandbox/docker.js.map +1 -1
- package/dist/src/integrations/sandbox/index.d.ts +6 -0
- package/dist/src/integrations/sandbox/index.d.ts.map +1 -1
- package/dist/src/integrations/sandbox/index.js +8 -4
- package/dist/src/integrations/sandbox/index.js.map +1 -1
- package/dist/src/integrations/sandbox/landlock.d.ts.map +1 -1
- package/dist/src/integrations/sandbox/landlock.js +3 -0
- package/dist/src/integrations/sandbox/landlock.js.map +1 -1
- package/dist/src/integrations/self-improvement.d.ts.map +1 -1
- package/dist/src/integrations/self-improvement.js +12 -0
- package/dist/src/integrations/self-improvement.js.map +1 -1
- package/dist/src/integrations/session-store.d.ts +1 -0
- package/dist/src/integrations/session-store.d.ts.map +1 -1
- package/dist/src/integrations/session-store.js +1 -0
- package/dist/src/integrations/session-store.js.map +1 -1
- package/dist/src/integrations/shared-blackboard.d.ts +3 -0
- package/dist/src/integrations/shared-blackboard.d.ts.map +1 -1
- package/dist/src/integrations/shared-blackboard.js +47 -0
- package/dist/src/integrations/shared-blackboard.js.map +1 -1
- package/dist/src/integrations/smart-decomposer.d.ts +45 -1
- package/dist/src/integrations/smart-decomposer.d.ts.map +1 -1
- package/dist/src/integrations/smart-decomposer.js +486 -30
- package/dist/src/integrations/smart-decomposer.js.map +1 -1
- package/dist/src/integrations/sqlite-store.d.ts +2 -0
- package/dist/src/integrations/sqlite-store.d.ts.map +1 -1
- package/dist/src/integrations/sqlite-store.js +18 -6
- package/dist/src/integrations/sqlite-store.js.map +1 -1
- package/dist/src/integrations/swarm/failure-classifier.d.ts +11 -0
- package/dist/src/integrations/swarm/failure-classifier.d.ts.map +1 -0
- package/dist/src/integrations/swarm/failure-classifier.js +95 -0
- package/dist/src/integrations/swarm/failure-classifier.js.map +1 -0
- package/dist/src/integrations/swarm/index.d.ts +1 -1
- package/dist/src/integrations/swarm/index.d.ts.map +1 -1
- package/dist/src/integrations/swarm/index.js.map +1 -1
- package/dist/src/integrations/swarm/model-selector.d.ts +15 -0
- package/dist/src/integrations/swarm/model-selector.d.ts.map +1 -1
- package/dist/src/integrations/swarm/model-selector.js +100 -20
- package/dist/src/integrations/swarm/model-selector.js.map +1 -1
- package/dist/src/integrations/swarm/swarm-budget.d.ts +4 -0
- package/dist/src/integrations/swarm/swarm-budget.d.ts.map +1 -1
- package/dist/src/integrations/swarm/swarm-budget.js +6 -0
- package/dist/src/integrations/swarm/swarm-budget.js.map +1 -1
- package/dist/src/integrations/swarm/swarm-config-loader.d.ts +8 -0
- package/dist/src/integrations/swarm/swarm-config-loader.d.ts.map +1 -1
- package/dist/src/integrations/swarm/swarm-config-loader.js +249 -7
- package/dist/src/integrations/swarm/swarm-config-loader.js.map +1 -1
- package/dist/src/integrations/swarm/swarm-event-bridge.d.ts +86 -1
- package/dist/src/integrations/swarm/swarm-event-bridge.d.ts.map +1 -1
- package/dist/src/integrations/swarm/swarm-event-bridge.js +207 -23
- package/dist/src/integrations/swarm/swarm-event-bridge.js.map +1 -1
- package/dist/src/integrations/swarm/swarm-events.d.ts +58 -1
- package/dist/src/integrations/swarm/swarm-events.d.ts.map +1 -1
- package/dist/src/integrations/swarm/swarm-events.js +22 -5
- package/dist/src/integrations/swarm/swarm-events.js.map +1 -1
- package/dist/src/integrations/swarm/swarm-orchestrator.d.ts +147 -8
- package/dist/src/integrations/swarm/swarm-orchestrator.d.ts.map +1 -1
- package/dist/src/integrations/swarm/swarm-orchestrator.js +2179 -132
- package/dist/src/integrations/swarm/swarm-orchestrator.js.map +1 -1
- package/dist/src/integrations/swarm/swarm-quality-gate.d.ts +83 -2
- package/dist/src/integrations/swarm/swarm-quality-gate.d.ts.map +1 -1
- package/dist/src/integrations/swarm/swarm-quality-gate.js +278 -19
- package/dist/src/integrations/swarm/swarm-quality-gate.js.map +1 -1
- package/dist/src/integrations/swarm/swarm-state-store.d.ts +4 -1
- package/dist/src/integrations/swarm/swarm-state-store.d.ts.map +1 -1
- package/dist/src/integrations/swarm/swarm-state-store.js +8 -1
- package/dist/src/integrations/swarm/swarm-state-store.js.map +1 -1
- package/dist/src/integrations/swarm/task-queue.d.ts +54 -0
- package/dist/src/integrations/swarm/task-queue.d.ts.map +1 -1
- package/dist/src/integrations/swarm/task-queue.js +310 -12
- package/dist/src/integrations/swarm/task-queue.js.map +1 -1
- package/dist/src/integrations/swarm/types.d.ts +251 -13
- package/dist/src/integrations/swarm/types.d.ts.map +1 -1
- package/dist/src/integrations/swarm/types.js +70 -8
- package/dist/src/integrations/swarm/types.js.map +1 -1
- package/dist/src/integrations/swarm/worker-pool.d.ts +21 -4
- package/dist/src/integrations/swarm/worker-pool.d.ts.map +1 -1
- package/dist/src/integrations/swarm/worker-pool.js +223 -44
- package/dist/src/integrations/swarm/worker-pool.js.map +1 -1
- package/dist/src/integrations/task-manager.d.ts +33 -1
- package/dist/src/integrations/task-manager.d.ts.map +1 -1
- package/dist/src/integrations/task-manager.js +78 -4
- package/dist/src/integrations/task-manager.js.map +1 -1
- package/dist/src/integrations/tool-recommendation.d.ts +7 -4
- package/dist/src/integrations/tool-recommendation.d.ts.map +1 -1
- package/dist/src/integrations/tool-recommendation.js +58 -5
- package/dist/src/integrations/tool-recommendation.js.map +1 -1
- package/dist/src/integrations/work-log.js +4 -4
- package/dist/src/integrations/work-log.js.map +1 -1
- package/dist/src/main.js +106 -30
- package/dist/src/main.js.map +1 -1
- package/dist/src/modes/repl.d.ts.map +1 -1
- package/dist/src/modes/repl.js +50 -12
- package/dist/src/modes/repl.js.map +1 -1
- package/dist/src/modes/tui.d.ts.map +1 -1
- package/dist/src/modes/tui.js +41 -6
- package/dist/src/modes/tui.js.map +1 -1
- package/dist/src/modes.d.ts.map +1 -1
- package/dist/src/modes.js +4 -27
- package/dist/src/modes.js.map +1 -1
- package/dist/src/observability/tracer.d.ts.map +1 -1
- package/dist/src/observability/tracer.js +2 -1
- package/dist/src/observability/tracer.js.map +1 -1
- package/dist/src/persistence/schema.d.ts.map +1 -1
- package/dist/src/persistence/schema.js +11 -0
- package/dist/src/persistence/schema.js.map +1 -1
- package/dist/src/providers/adapters/anthropic.d.ts.map +1 -1
- package/dist/src/providers/adapters/anthropic.js +3 -2
- package/dist/src/providers/adapters/anthropic.js.map +1 -1
- package/dist/src/providers/adapters/openai.d.ts.map +1 -1
- package/dist/src/providers/adapters/openai.js +3 -2
- package/dist/src/providers/adapters/openai.js.map +1 -1
- package/dist/src/providers/adapters/openrouter.d.ts.map +1 -1
- package/dist/src/providers/adapters/openrouter.js +11 -11
- package/dist/src/providers/adapters/openrouter.js.map +1 -1
- package/dist/src/providers/circuit-breaker.d.ts +1 -0
- package/dist/src/providers/circuit-breaker.d.ts.map +1 -1
- package/dist/src/providers/circuit-breaker.js.map +1 -1
- package/dist/src/providers/provider.d.ts.map +1 -1
- package/dist/src/providers/provider.js +2 -1
- package/dist/src/providers/provider.js.map +1 -1
- package/dist/src/providers/resilient-provider.d.ts.map +1 -1
- package/dist/src/providers/resilient-provider.js +2 -1
- package/dist/src/providers/resilient-provider.js.map +1 -1
- package/dist/src/session-picker.d.ts.map +1 -1
- package/dist/src/session-picker.js +40 -5
- package/dist/src/session-picker.js.map +1 -1
- package/dist/src/shared/budget-tracker.d.ts +65 -0
- package/dist/src/shared/budget-tracker.d.ts.map +1 -0
- package/dist/src/shared/budget-tracker.js +128 -0
- package/dist/src/shared/budget-tracker.js.map +1 -0
- package/dist/src/shared/context-engine.d.ts +64 -0
- package/dist/src/shared/context-engine.d.ts.map +1 -0
- package/dist/src/shared/context-engine.js +117 -0
- package/dist/src/shared/context-engine.js.map +1 -0
- package/dist/src/shared/index.d.ts +12 -0
- package/dist/src/shared/index.d.ts.map +1 -0
- package/dist/src/shared/index.js +12 -0
- package/dist/src/shared/index.js.map +1 -0
- package/dist/src/shared/persistence.d.ts +57 -0
- package/dist/src/shared/persistence.d.ts.map +1 -0
- package/dist/src/shared/persistence.js +168 -0
- package/dist/src/shared/persistence.js.map +1 -0
- package/dist/src/shared/shared-context-state.d.ts +89 -0
- package/dist/src/shared/shared-context-state.d.ts.map +1 -0
- package/dist/src/shared/shared-context-state.js +175 -0
- package/dist/src/shared/shared-context-state.js.map +1 -0
- package/dist/src/shared/shared-economics-state.d.ts +61 -0
- package/dist/src/shared/shared-economics-state.d.ts.map +1 -0
- package/dist/src/shared/shared-economics-state.js +100 -0
- package/dist/src/shared/shared-economics-state.js.map +1 -0
- package/dist/src/tools/agent.d.ts.map +1 -1
- package/dist/src/tools/agent.js +11 -2
- package/dist/src/tools/agent.js.map +1 -1
- package/dist/src/tools/bash.d.ts +1 -1
- package/dist/src/tools/bash.d.ts.map +1 -1
- package/dist/src/tools/bash.js +2 -1
- package/dist/src/tools/bash.js.map +1 -1
- package/dist/src/tools/coercion.d.ts +6 -0
- package/dist/src/tools/coercion.d.ts.map +1 -1
- package/dist/src/tools/coercion.js +13 -0
- package/dist/src/tools/coercion.js.map +1 -1
- package/dist/src/tools/file.d.ts +5 -5
- package/dist/src/tools/file.js +2 -2
- package/dist/src/tools/file.js.map +1 -1
- package/dist/src/tools/permission.d.ts.map +1 -1
- package/dist/src/tools/permission.js +10 -116
- package/dist/src/tools/permission.js.map +1 -1
- package/dist/src/tools/types.d.ts +1 -0
- package/dist/src/tools/types.d.ts.map +1 -1
- package/dist/src/tools/types.js.map +1 -1
- package/dist/src/tracing/trace-collector.d.ts +292 -0
- package/dist/src/tracing/trace-collector.d.ts.map +1 -1
- package/dist/src/tracing/trace-collector.js +249 -5
- package/dist/src/tracing/trace-collector.js.map +1 -1
- package/dist/src/tracing/types.d.ts +200 -1
- package/dist/src/tracing/types.d.ts.map +1 -1
- package/dist/src/tracing/types.js.map +1 -1
- package/dist/src/tricks/failure-evidence.d.ts.map +1 -1
- package/dist/src/tricks/failure-evidence.js +2 -1
- package/dist/src/tricks/failure-evidence.js.map +1 -1
- package/dist/src/tui/app.d.ts +13 -0
- package/dist/src/tui/app.d.ts.map +1 -1
- package/dist/src/tui/app.js +162 -19
- package/dist/src/tui/app.js.map +1 -1
- package/dist/src/tui/components/ErrorBoundary.d.ts.map +1 -1
- package/dist/src/tui/components/ErrorBoundary.js +3 -2
- package/dist/src/tui/components/ErrorBoundary.js.map +1 -1
- package/dist/src/tui/event-display.d.ts.map +1 -1
- package/dist/src/tui/event-display.js +36 -62
- package/dist/src/tui/event-display.js.map +1 -1
- package/dist/src/tui/index.d.ts +4 -0
- package/dist/src/tui/index.d.ts.map +1 -1
- package/dist/src/tui/index.js +17 -0
- package/dist/src/tui/index.js.map +1 -1
- package/dist/src/types.d.ts +214 -1
- package/dist/src/types.d.ts.map +1 -1
- package/package.json +18 -3
|
@@ -0,0 +1,966 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subagent Spawner Module (Phase 2.1)
|
|
3
|
+
*
|
|
4
|
+
* Extracted from ProductionAgent.spawnAgent(), getSubagentBudget(),
|
|
5
|
+
* and spawnAgentsParallel(). Handles the full subagent lifecycle:
|
|
6
|
+
* config, budget, delegation, timeout, cleanup.
|
|
7
|
+
*
|
|
8
|
+
* Uses SubAgentFactory to create subagent instances without importing
|
|
9
|
+
* ProductionAgent directly — avoids circular dependencies.
|
|
10
|
+
*/
|
|
11
|
+
import { getSubagentTimeout, getSubagentMaxIterations, } from '../defaults.js';
|
|
12
|
+
import { calculateTaskSimilarity, SUBAGENT_PLAN_MODE_ADDITION, } from '../modes.js';
|
|
13
|
+
import { SUBAGENT_BUDGET, filterToolsForAgent, isCancellationError, createLinkedToken, createGracefulTimeout, race, createDynamicBudgetPool, buildDelegationPrompt, createMinimalDelegationSpec, getSubagentQualityPrompt, ToolRecommendationEngine, createSubagentSupervisor, createSubagentHandle, } from '../integrations/index.js';
|
|
14
|
+
import { mergeApprovalScopeWithProfile, resolvePolicyProfile, } from '../integrations/policy-engine.js';
|
|
15
|
+
/** Duplicate spawn prevention window (60 seconds). */
|
|
16
|
+
const SPAWN_DEDUP_WINDOW_MS = 60000;
|
|
17
|
+
/**
|
|
18
|
+
* Parse a structured closure report from a subagent's text response.
|
|
19
|
+
* The subagent may have produced JSON in response to a TIMEOUT_WRAPUP_PROMPT.
|
|
20
|
+
*/
|
|
21
|
+
export function parseStructuredClosureReport(text, defaultExitReason, fallbackTask) {
|
|
22
|
+
if (!text) {
|
|
23
|
+
if (fallbackTask) {
|
|
24
|
+
return {
|
|
25
|
+
findings: [],
|
|
26
|
+
actionsTaken: [],
|
|
27
|
+
failures: ['Timeout before producing structured summary'],
|
|
28
|
+
remainingWork: [fallbackTask],
|
|
29
|
+
exitReason: 'timeout_hard',
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
const jsonMatch = text.match(/\{[\s\S]*\}/);
|
|
36
|
+
if (jsonMatch) {
|
|
37
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
38
|
+
if (parsed.findings || parsed.actionsTaken || parsed.failures || parsed.remainingWork) {
|
|
39
|
+
return {
|
|
40
|
+
findings: Array.isArray(parsed.findings) ? parsed.findings : [],
|
|
41
|
+
actionsTaken: Array.isArray(parsed.actionsTaken) ? parsed.actionsTaken : [],
|
|
42
|
+
failures: Array.isArray(parsed.failures) ? parsed.failures : [],
|
|
43
|
+
remainingWork: Array.isArray(parsed.remainingWork) ? parsed.remainingWork : [],
|
|
44
|
+
exitReason: defaultExitReason,
|
|
45
|
+
suggestedNextSteps: Array.isArray(parsed.suggestedNextSteps) ? parsed.suggestedNextSteps : undefined,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// JSON parse failed — fall through to fallback
|
|
52
|
+
}
|
|
53
|
+
if (defaultExitReason !== 'completed') {
|
|
54
|
+
return {
|
|
55
|
+
findings: [text.slice(0, 500)],
|
|
56
|
+
actionsTaken: [],
|
|
57
|
+
failures: ['Did not produce structured JSON summary'],
|
|
58
|
+
remainingWork: fallbackTask ? [fallbackTask] : [],
|
|
59
|
+
exitReason: defaultExitReason === 'timeout_graceful' ? 'timeout_hard' : defaultExitReason,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Get budget for a subagent, using the pooled budget when available.
|
|
66
|
+
* Falls back to the static SUBAGENT_BUDGET if no pool is configured.
|
|
67
|
+
*/
|
|
68
|
+
export function getSubagentBudget(ctx, agentName, constraints) {
|
|
69
|
+
if (constraints?.maxTokens) {
|
|
70
|
+
return {
|
|
71
|
+
budget: { ...SUBAGENT_BUDGET, maxTokens: constraints.maxTokens },
|
|
72
|
+
allocationId: null,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (ctx.budgetPool) {
|
|
76
|
+
const allocationId = `${agentName}-${Date.now()}`;
|
|
77
|
+
const allocation = ctx.budgetPool.reserve(allocationId);
|
|
78
|
+
if (allocation) {
|
|
79
|
+
return {
|
|
80
|
+
budget: {
|
|
81
|
+
...SUBAGENT_BUDGET,
|
|
82
|
+
maxTokens: allocation.tokenBudget,
|
|
83
|
+
softTokenLimit: Math.floor(allocation.tokenBudget * 0.7),
|
|
84
|
+
maxCost: allocation.costBudget,
|
|
85
|
+
},
|
|
86
|
+
allocationId,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
budget: {
|
|
91
|
+
...SUBAGENT_BUDGET,
|
|
92
|
+
maxTokens: 5000,
|
|
93
|
+
softTokenLimit: 3000,
|
|
94
|
+
maxCost: 0.01,
|
|
95
|
+
},
|
|
96
|
+
allocationId: null,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
return { budget: SUBAGENT_BUDGET, allocationId: null };
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Spawn a single subagent to handle a delegated task.
|
|
103
|
+
* Extracted from ProductionAgent.spawnAgent().
|
|
104
|
+
*/
|
|
105
|
+
export async function spawnAgent(agentName, task, ctx, createSubAgent, constraints) {
|
|
106
|
+
if (!ctx.agentRegistry) {
|
|
107
|
+
return {
|
|
108
|
+
success: false,
|
|
109
|
+
output: 'Agent registry not initialized',
|
|
110
|
+
metrics: { tokens: 0, duration: 0, toolCalls: 0 },
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
const agentDef = ctx.agentRegistry.getAgent(agentName);
|
|
114
|
+
if (!agentDef) {
|
|
115
|
+
return {
|
|
116
|
+
success: false,
|
|
117
|
+
output: `Agent not found: ${agentName}`,
|
|
118
|
+
metrics: { tokens: 0, duration: 0, toolCalls: 0 },
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
// DUPLICATE SPAWN PREVENTION with SEMANTIC SIMILARITY
|
|
122
|
+
const isSwarmWorker = agentName.startsWith('swarm-');
|
|
123
|
+
const SEMANTIC_SIMILARITY_THRESHOLD = 0.75;
|
|
124
|
+
const taskKey = `${agentName}:${task.slice(0, 150).toLowerCase().replace(/\s+/g, ' ').trim()}`;
|
|
125
|
+
const now = Date.now();
|
|
126
|
+
// Clean up old entries
|
|
127
|
+
for (const [key, entry] of ctx.spawnedTasks.entries()) {
|
|
128
|
+
if (now - entry.timestamp > SPAWN_DEDUP_WINDOW_MS) {
|
|
129
|
+
ctx.spawnedTasks.delete(key);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
let existingMatch;
|
|
133
|
+
let matchType = 'exact';
|
|
134
|
+
if (!isSwarmWorker) {
|
|
135
|
+
existingMatch = ctx.spawnedTasks.get(taskKey);
|
|
136
|
+
if (!existingMatch) {
|
|
137
|
+
for (const [key, entry] of ctx.spawnedTasks.entries()) {
|
|
138
|
+
if (!key.startsWith(`${agentName}:`))
|
|
139
|
+
continue;
|
|
140
|
+
if (now - entry.timestamp >= SPAWN_DEDUP_WINDOW_MS)
|
|
141
|
+
continue;
|
|
142
|
+
const existingTask = key.slice(agentName.length + 1);
|
|
143
|
+
const similarity = calculateTaskSimilarity(task, existingTask);
|
|
144
|
+
if (similarity >= SEMANTIC_SIMILARITY_THRESHOLD) {
|
|
145
|
+
existingMatch = entry;
|
|
146
|
+
matchType = 'semantic';
|
|
147
|
+
ctx.observability?.logger?.debug('Semantic duplicate detected', {
|
|
148
|
+
agent: agentName,
|
|
149
|
+
newTask: task.slice(0, 80),
|
|
150
|
+
existingTask: existingTask.slice(0, 80),
|
|
151
|
+
similarity: (similarity * 100).toFixed(1) + '%',
|
|
152
|
+
});
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (existingMatch && now - existingMatch.timestamp < SPAWN_DEDUP_WINDOW_MS) {
|
|
159
|
+
ctx.observability?.logger?.warn('Duplicate spawn prevented', {
|
|
160
|
+
agent: agentName,
|
|
161
|
+
task: task.slice(0, 100),
|
|
162
|
+
matchType,
|
|
163
|
+
originalTimestamp: existingMatch.timestamp,
|
|
164
|
+
elapsedMs: now - existingMatch.timestamp,
|
|
165
|
+
});
|
|
166
|
+
const duplicateMessage = `[DUPLICATE SPAWN PREVENTED${matchType === 'semantic' ? ' - SEMANTIC MATCH' : ''}]\n` +
|
|
167
|
+
`This task was already spawned ${Math.round((now - existingMatch.timestamp) / 1000)}s ago.\n` +
|
|
168
|
+
`${existingMatch.queuedChanges > 0
|
|
169
|
+
? `The previous spawn queued ${existingMatch.queuedChanges} change(s) to the pending plan.\n` +
|
|
170
|
+
`These changes are already in your plan - do NOT spawn again.\n`
|
|
171
|
+
: ''}Previous result summary:\n${existingMatch.result.slice(0, 500)}`;
|
|
172
|
+
return {
|
|
173
|
+
success: true,
|
|
174
|
+
output: duplicateMessage,
|
|
175
|
+
metrics: { tokens: 0, duration: 0, toolCalls: 0 },
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
const agentId = `spawn-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
179
|
+
ctx.emit({ type: 'agent.spawn', agentId, name: agentName, task });
|
|
180
|
+
ctx.observability?.logger?.info('Spawning agent', { name: agentName, task });
|
|
181
|
+
const startTime = Date.now();
|
|
182
|
+
const childSessionId = `subagent-${agentName}-${Date.now()}`;
|
|
183
|
+
const childTraceId = `trace-${childSessionId}`;
|
|
184
|
+
let workerResultId;
|
|
185
|
+
try {
|
|
186
|
+
// Filter tools for this agent
|
|
187
|
+
let agentTools = filterToolsForAgent(agentDef, Array.from(ctx.tools.values()));
|
|
188
|
+
// Resolve policy profile
|
|
189
|
+
const inferredTaskType = agentDef.taskType ?? ToolRecommendationEngine.inferTaskType(agentName);
|
|
190
|
+
const policyResolution = resolvePolicyProfile({
|
|
191
|
+
policyEngine: ctx.config.policyEngine,
|
|
192
|
+
requestedProfile: agentDef.policyProfile,
|
|
193
|
+
swarmConfig: isSwarmWorker && ctx.config.swarm && typeof ctx.config.swarm === 'object'
|
|
194
|
+
? ctx.config.swarm
|
|
195
|
+
: undefined,
|
|
196
|
+
taskType: inferredTaskType,
|
|
197
|
+
isSwarmWorker,
|
|
198
|
+
sandboxConfig: ctx.config.sandbox && typeof ctx.config.sandbox === 'object'
|
|
199
|
+
? ctx.config.sandbox
|
|
200
|
+
: undefined,
|
|
201
|
+
});
|
|
202
|
+
ctx.emit({
|
|
203
|
+
type: 'policy.profile.resolved',
|
|
204
|
+
profile: policyResolution.profileName,
|
|
205
|
+
context: isSwarmWorker ? 'swarm' : 'subagent',
|
|
206
|
+
selectionSource: policyResolution.metadata.selectionSource,
|
|
207
|
+
usedLegacyMappings: policyResolution.metadata.usedLegacyMappings,
|
|
208
|
+
legacySources: policyResolution.metadata.legacyMappingSources,
|
|
209
|
+
});
|
|
210
|
+
if (policyResolution.metadata.usedLegacyMappings) {
|
|
211
|
+
ctx.emit({
|
|
212
|
+
type: 'policy.legacy.fallback.used',
|
|
213
|
+
profile: policyResolution.profileName,
|
|
214
|
+
sources: policyResolution.metadata.legacyMappingSources,
|
|
215
|
+
warnings: policyResolution.metadata.warnings,
|
|
216
|
+
});
|
|
217
|
+
ctx.observability?.logger?.warn('Policy legacy mappings used', {
|
|
218
|
+
agent: agentName,
|
|
219
|
+
profile: policyResolution.profileName,
|
|
220
|
+
sources: policyResolution.metadata.legacyMappingSources,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
// Apply tool recommendations
|
|
224
|
+
if (ctx.toolRecommendation && agentTools.length > 15) {
|
|
225
|
+
const taskType = ToolRecommendationEngine.inferTaskType(agentName);
|
|
226
|
+
const recommendations = ctx.toolRecommendation.recommendTools(task, taskType, agentTools.map(t => t.name));
|
|
227
|
+
if (recommendations.length > 0) {
|
|
228
|
+
const recommendedNames = new Set(recommendations.map(r => r.toolName));
|
|
229
|
+
const alwaysKeep = new Set(['spawn_agent', 'spawn_agents_parallel']);
|
|
230
|
+
if (policyResolution.profile.allowedTools) {
|
|
231
|
+
for (const t of policyResolution.profile.allowedTools)
|
|
232
|
+
alwaysKeep.add(t);
|
|
233
|
+
}
|
|
234
|
+
agentTools = agentTools.filter(t => recommendedNames.has(t.name) || alwaysKeep.has(t.name));
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
// Enforce unified tool policy
|
|
238
|
+
if (policyResolution.profile.toolAccessMode === 'whitelist' && policyResolution.profile.allowedTools) {
|
|
239
|
+
const allowed = new Set(policyResolution.profile.allowedTools);
|
|
240
|
+
agentTools = agentTools.filter(t => allowed.has(t.name));
|
|
241
|
+
}
|
|
242
|
+
else if (policyResolution.profile.deniedTools && policyResolution.profile.deniedTools.length > 0) {
|
|
243
|
+
const denied = new Set(policyResolution.profile.deniedTools);
|
|
244
|
+
agentTools = agentTools.filter(t => !denied.has(t.name));
|
|
245
|
+
}
|
|
246
|
+
if (agentTools.length === 0) {
|
|
247
|
+
throw new Error(`Worker '${agentName}' has zero available tools after filtering. Check toolAccessMode and policy profile '${policyResolution.profileName}'.`);
|
|
248
|
+
}
|
|
249
|
+
// Resolve model
|
|
250
|
+
const resolvedModel = (agentDef.model && agentDef.model.includes('/'))
|
|
251
|
+
? agentDef.model
|
|
252
|
+
: ctx.config.model;
|
|
253
|
+
// Persist subagent task lifecycle
|
|
254
|
+
if (ctx.store?.hasWorkerResultsFeature()) {
|
|
255
|
+
try {
|
|
256
|
+
workerResultId = ctx.store.createWorkerResult(agentId, task.slice(0, 500), resolvedModel || 'default');
|
|
257
|
+
}
|
|
258
|
+
catch (storeErr) {
|
|
259
|
+
ctx.observability?.logger?.warn('Failed to create worker result record', {
|
|
260
|
+
agentId,
|
|
261
|
+
error: storeErr.message,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
// Get subagent config with agent-type-specific timeouts and iteration limits
|
|
266
|
+
const subagentConfig = ctx.config.subagent;
|
|
267
|
+
const hasSubagentConfig = subagentConfig !== false && subagentConfig !== undefined;
|
|
268
|
+
// Timeout precedence
|
|
269
|
+
const agentTypeTimeout = getSubagentTimeout(agentName);
|
|
270
|
+
const rawPerTypeTimeout = hasSubagentConfig
|
|
271
|
+
? subagentConfig.timeouts?.[agentName]
|
|
272
|
+
: undefined;
|
|
273
|
+
const rawGlobalTimeout = hasSubagentConfig
|
|
274
|
+
? subagentConfig.defaultTimeout
|
|
275
|
+
: undefined;
|
|
276
|
+
const isValidTimeout = (v) => v !== undefined && Number.isFinite(v) && v > 0;
|
|
277
|
+
const agentDefTimeout = isValidTimeout(agentDef.timeout) ? agentDef.timeout : undefined;
|
|
278
|
+
const perTypeConfigTimeout = isValidTimeout(rawPerTypeTimeout) ? rawPerTypeTimeout : undefined;
|
|
279
|
+
const globalConfigTimeout = isValidTimeout(rawGlobalTimeout) ? rawGlobalTimeout : undefined;
|
|
280
|
+
const subagentTimeout = agentDefTimeout ?? perTypeConfigTimeout ?? agentTypeTimeout ?? globalConfigTimeout ?? 300000;
|
|
281
|
+
// Iteration precedence
|
|
282
|
+
const agentTypeMaxIter = getSubagentMaxIterations(agentName);
|
|
283
|
+
const rawPerTypeMaxIter = hasSubagentConfig
|
|
284
|
+
? subagentConfig.maxIterations?.[agentName]
|
|
285
|
+
: undefined;
|
|
286
|
+
const rawGlobalMaxIter = hasSubagentConfig
|
|
287
|
+
? subagentConfig.defaultMaxIterations
|
|
288
|
+
: undefined;
|
|
289
|
+
const isValidIter = (v) => v !== undefined && Number.isFinite(v) && v > 0 && Number.isInteger(v);
|
|
290
|
+
const perTypeConfigMaxIter = isValidIter(rawPerTypeMaxIter) ? rawPerTypeMaxIter : undefined;
|
|
291
|
+
const globalConfigMaxIter = isValidIter(rawGlobalMaxIter) ? rawGlobalMaxIter : undefined;
|
|
292
|
+
const defaultMaxIterations = agentDef.maxIterations ?? perTypeConfigMaxIter ?? agentTypeMaxIter ?? globalConfigMaxIter ?? 15;
|
|
293
|
+
// BLACKBOARD CONTEXT INJECTION
|
|
294
|
+
let blackboardContext = '';
|
|
295
|
+
let blackboardFindingsCount = 0;
|
|
296
|
+
const parentAgentId = `parent-${Date.now()}`;
|
|
297
|
+
if (ctx.blackboard) {
|
|
298
|
+
ctx.blackboard.post(parentAgentId, {
|
|
299
|
+
topic: 'spawn.parent_context',
|
|
300
|
+
content: `Parent spawning ${agentName} for task: ${task.slice(0, 200)}`,
|
|
301
|
+
type: 'progress',
|
|
302
|
+
confidence: 1,
|
|
303
|
+
metadata: { agentName, taskPreview: task.slice(0, 100) },
|
|
304
|
+
});
|
|
305
|
+
const recentFindings = ctx.blackboard.query({
|
|
306
|
+
limit: 5,
|
|
307
|
+
types: ['discovery', 'analysis', 'progress'],
|
|
308
|
+
minConfidence: 0.7,
|
|
309
|
+
});
|
|
310
|
+
blackboardFindingsCount = recentFindings.length;
|
|
311
|
+
if (recentFindings.length > 0) {
|
|
312
|
+
const findingsSummary = recentFindings
|
|
313
|
+
.map(f => `- [${f.agentId}] ${f.topic}: ${f.content.slice(0, 150)}${f.content.length > 150 ? '...' : ''}`)
|
|
314
|
+
.join('\n');
|
|
315
|
+
blackboardContext = `\n\n**BLACKBOARD CONTEXT (from parent/sibling agents):**\n${findingsSummary}\n`;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
// Check for files already in parent's pending plan
|
|
319
|
+
const currentPlan = ctx.pendingPlanManager.getPendingPlan();
|
|
320
|
+
if (currentPlan && currentPlan.proposedChanges.length > 0) {
|
|
321
|
+
const pendingFiles = currentPlan.proposedChanges
|
|
322
|
+
.filter((c) => c.tool === 'write_file' || c.tool === 'edit_file')
|
|
323
|
+
.map((c) => c.args.path || c.args.file_path)
|
|
324
|
+
.filter(Boolean);
|
|
325
|
+
if (pendingFiles.length > 0) {
|
|
326
|
+
blackboardContext += `\n**FILES ALREADY IN PENDING PLAN (do not duplicate):**\n${pendingFiles.slice(0, 10).join('\n')}\n`;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
// CONSTRAINT INJECTION
|
|
330
|
+
const constraintParts = [];
|
|
331
|
+
const subagentBudgetTokens = constraints?.maxTokens ?? SUBAGENT_BUDGET.maxTokens ?? 100000;
|
|
332
|
+
const subagentBudgetMinutes = Math.round((SUBAGENT_BUDGET.maxDuration ?? 240000) / 60000);
|
|
333
|
+
if (isSwarmWorker) {
|
|
334
|
+
constraintParts.push(`**Execution Mode:** You are a focused worker agent.\n` +
|
|
335
|
+
`- Complete your assigned task using tool calls.\n` +
|
|
336
|
+
`- Your FIRST action must be a tool call (read_file, write_file, edit_file, grep, glob, etc.).\n` +
|
|
337
|
+
`- To create files use write_file. To modify files use edit_file. Do NOT use bash for file operations.\n` +
|
|
338
|
+
`- You will receive a system message if you need to wrap up. Until then, work normally.\n` +
|
|
339
|
+
`- Do NOT produce summaries or reports — produce CODE and FILE CHANGES.`);
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
constraintParts.push(`**RESOURCE AWARENESS (CRITICAL):**\n` +
|
|
343
|
+
`- Token budget: ~${(subagentBudgetTokens / 1000).toFixed(0)}k tokens\n` +
|
|
344
|
+
`- Time limit: ~${subagentBudgetMinutes} minutes\n` +
|
|
345
|
+
`- You will receive warnings at 70% usage. When warned, WRAP UP immediately.\n` +
|
|
346
|
+
`- Do not explore indefinitely - be focused and efficient.\n` +
|
|
347
|
+
`- If approaching limits, summarize findings and return.\n` +
|
|
348
|
+
`- **STRUCTURED WRAPUP:** When told to wrap up, respond with ONLY this JSON (no tool calls):\n` +
|
|
349
|
+
` {"findings":[...], "actionsTaken":[...], "failures":[...], "remainingWork":[...], "suggestedNextSteps":[...]}`);
|
|
350
|
+
}
|
|
351
|
+
if (constraints) {
|
|
352
|
+
if (constraints.focusAreas && constraints.focusAreas.length > 0) {
|
|
353
|
+
constraintParts.push(`**FOCUS AREAS (limit exploration to these paths):**\n${constraints.focusAreas.map(a => ` - ${a}`).join('\n')}`);
|
|
354
|
+
}
|
|
355
|
+
if (constraints.excludeAreas && constraints.excludeAreas.length > 0) {
|
|
356
|
+
constraintParts.push(`**EXCLUDED AREAS (do NOT explore these):**\n${constraints.excludeAreas.map(a => ` - ${a}`).join('\n')}`);
|
|
357
|
+
}
|
|
358
|
+
if (constraints.requiredDeliverables && constraints.requiredDeliverables.length > 0) {
|
|
359
|
+
constraintParts.push(`**REQUIRED DELIVERABLES (you must produce these):**\n${constraints.requiredDeliverables.map(d => ` - ${d}`).join('\n')}`);
|
|
360
|
+
}
|
|
361
|
+
if (constraints.timeboxMinutes) {
|
|
362
|
+
constraintParts.push(`**TIME LIMIT:** ${constraints.timeboxMinutes} minutes (soft limit - wrap up if approaching)`);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
const constraintContext = `\n\n**EXECUTION CONSTRAINTS:**\n${constraintParts.join('\n\n')}\n`;
|
|
366
|
+
// Build delegation-enhanced system prompt
|
|
367
|
+
let delegationContext = '';
|
|
368
|
+
if (ctx.lastComplexityAssessment && ctx.lastComplexityAssessment.tier !== 'simple') {
|
|
369
|
+
const spec = createMinimalDelegationSpec(task, agentName);
|
|
370
|
+
delegationContext = '\n\n' + buildDelegationPrompt(spec);
|
|
371
|
+
}
|
|
372
|
+
const qualityPrompt = '\n\n' + getSubagentQualityPrompt();
|
|
373
|
+
// REPO CONTEXT INJECTION — give subagents a lightweight file structure map
|
|
374
|
+
let repoContextStr = '';
|
|
375
|
+
if (ctx.codebaseContext) {
|
|
376
|
+
const repoMap = ctx.codebaseContext.getRepoMap();
|
|
377
|
+
if (repoMap) {
|
|
378
|
+
// Lightweight repo map: file tree with key symbols (capped at 3000 tokens)
|
|
379
|
+
const { generateLightweightRepoMap } = await import('../integrations/codebase-context.js');
|
|
380
|
+
repoContextStr = '\n\n**REPOSITORY STRUCTURE:**\n' + generateLightweightRepoMap(repoMap, 3000);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
// Inject parent's recently modified files so subagent knows the working context
|
|
384
|
+
let recentFilesStr = '';
|
|
385
|
+
let modifiedFilesList = [];
|
|
386
|
+
if (ctx.economics) {
|
|
387
|
+
const modifiedPaths = ctx.economics.getModifiedFilePaths();
|
|
388
|
+
modifiedFilesList = modifiedPaths.slice(0, 15);
|
|
389
|
+
if (modifiedPaths.length > 0) {
|
|
390
|
+
recentFilesStr = '\n\n**RECENTLY MODIFIED FILES (by parent agent):**\n' +
|
|
391
|
+
modifiedPaths.slice(0, 15).map(f => `- ${f}`).join('\n');
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
// Build subagent system prompt
|
|
395
|
+
const parentMode = ctx.modeManager.getMode();
|
|
396
|
+
const subagentSystemPrompt = parentMode === 'plan'
|
|
397
|
+
? `${agentDef.systemPrompt}\n\n${SUBAGENT_PLAN_MODE_ADDITION}${blackboardContext}${repoContextStr}${recentFilesStr}${constraintContext}${delegationContext}${qualityPrompt}`
|
|
398
|
+
: `${agentDef.systemPrompt}${blackboardContext}${repoContextStr}${recentFilesStr}${constraintContext}${delegationContext}${qualityPrompt}`;
|
|
399
|
+
// Trace context injection for subagent prompt
|
|
400
|
+
ctx.traceCollector?.record({
|
|
401
|
+
type: 'context.injection',
|
|
402
|
+
data: {
|
|
403
|
+
agentId: agentName,
|
|
404
|
+
parentAgentId: ctx.traceCollector.getSessionId() || 'unknown',
|
|
405
|
+
repoMapTokens: Math.ceil(repoContextStr.length / 4),
|
|
406
|
+
blackboardFindings: blackboardFindingsCount,
|
|
407
|
+
modifiedFiles: modifiedFilesList,
|
|
408
|
+
toolCount: agentTools.length,
|
|
409
|
+
model: resolvedModel || 'default',
|
|
410
|
+
},
|
|
411
|
+
});
|
|
412
|
+
// Allocate budget
|
|
413
|
+
const pooledBudget = getSubagentBudget(ctx, agentName, constraints);
|
|
414
|
+
const poolAllocationId = pooledBudget.allocationId;
|
|
415
|
+
const deniedByProfile = new Set(policyResolution.profile.deniedTools ?? []);
|
|
416
|
+
const policyToolPolicies = {};
|
|
417
|
+
for (const toolName of deniedByProfile) {
|
|
418
|
+
policyToolPolicies[toolName] = {
|
|
419
|
+
policy: 'forbidden',
|
|
420
|
+
reason: `Denied by policy profile '${policyResolution.profileName}'`,
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
if ((policyResolution.profile.bashMode ?? 'full') === 'disabled') {
|
|
424
|
+
policyToolPolicies.bash = {
|
|
425
|
+
policy: 'forbidden',
|
|
426
|
+
reason: `Bash is disabled by policy profile '${policyResolution.profileName}'`,
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
// Create the sub-agent via factory (avoids circular import)
|
|
430
|
+
const subAgent = createSubAgent({
|
|
431
|
+
provider: ctx.provider,
|
|
432
|
+
tools: agentTools,
|
|
433
|
+
toolResolver: ctx.toolResolver || undefined,
|
|
434
|
+
mcpToolSummaries: ctx.config.mcpToolSummaries,
|
|
435
|
+
systemPrompt: subagentSystemPrompt,
|
|
436
|
+
model: resolvedModel,
|
|
437
|
+
maxIterations: agentDef.maxIterations || defaultMaxIterations,
|
|
438
|
+
memory: false,
|
|
439
|
+
planning: false,
|
|
440
|
+
reflection: false,
|
|
441
|
+
compaction: {
|
|
442
|
+
enabled: true,
|
|
443
|
+
mode: 'auto',
|
|
444
|
+
tokenThreshold: 40000,
|
|
445
|
+
preserveRecentCount: 4,
|
|
446
|
+
preserveToolResults: false,
|
|
447
|
+
summaryMaxTokens: 500,
|
|
448
|
+
},
|
|
449
|
+
maxContextTokens: 80000,
|
|
450
|
+
observability: ctx.config.observability,
|
|
451
|
+
sandbox: (() => {
|
|
452
|
+
const swarm = ctx.config.swarm;
|
|
453
|
+
const extraCmds = swarm && typeof swarm === 'object' && swarm.permissions?.additionalAllowedCommands;
|
|
454
|
+
const baseSbx = ctx.config.sandbox;
|
|
455
|
+
if (baseSbx && typeof baseSbx === 'object') {
|
|
456
|
+
const sbx = baseSbx;
|
|
457
|
+
const allowedCommands = extraCmds
|
|
458
|
+
? [...(sbx.allowedCommands || []), ...extraCmds]
|
|
459
|
+
: sbx.allowedCommands;
|
|
460
|
+
return {
|
|
461
|
+
...sbx,
|
|
462
|
+
allowedCommands,
|
|
463
|
+
bashMode: policyResolution.profile.bashMode ?? sbx.bashMode,
|
|
464
|
+
bashWriteProtection: policyResolution.profile.bashWriteProtection ?? sbx.bashWriteProtection,
|
|
465
|
+
blockFileCreationViaBash: (policyResolution.profile.bashWriteProtection ?? 'off') === 'block_file_mutation'
|
|
466
|
+
? true
|
|
467
|
+
: sbx.blockFileCreationViaBash,
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
return baseSbx;
|
|
471
|
+
})(),
|
|
472
|
+
// Subagents: raise riskThreshold to 'high' so moderate-risk tools (write_file, edit_file)
|
|
473
|
+
// pass without approval dialogs. High-risk tools (delete_file) still require approval.
|
|
474
|
+
// The scopedApprove paths still constrain WHERE subagents can write.
|
|
475
|
+
humanInLoop: ctx.config.humanInLoop
|
|
476
|
+
? {
|
|
477
|
+
...ctx.config.humanInLoop,
|
|
478
|
+
riskThreshold: 'high',
|
|
479
|
+
}
|
|
480
|
+
: ctx.config.humanInLoop,
|
|
481
|
+
executionPolicy: (() => {
|
|
482
|
+
const hasPolicyOverrides = Object.keys(policyToolPolicies).length > 0;
|
|
483
|
+
if (ctx.config.executionPolicy) {
|
|
484
|
+
return {
|
|
485
|
+
...ctx.config.executionPolicy,
|
|
486
|
+
defaultPolicy: 'allow',
|
|
487
|
+
toolPolicies: {
|
|
488
|
+
...(ctx.config.executionPolicy.toolPolicies ?? {}),
|
|
489
|
+
...policyToolPolicies,
|
|
490
|
+
},
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
if (hasPolicyOverrides) {
|
|
494
|
+
return {
|
|
495
|
+
enabled: true,
|
|
496
|
+
defaultPolicy: 'allow',
|
|
497
|
+
toolPolicies: policyToolPolicies,
|
|
498
|
+
intentAware: false,
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
return { enabled: true, defaultPolicy: 'allow', toolPolicies: {}, intentAware: false };
|
|
502
|
+
})(),
|
|
503
|
+
policyEngine: ctx.config.policyEngine
|
|
504
|
+
? { ...ctx.config.policyEngine, defaultProfile: policyResolution.profileName }
|
|
505
|
+
: ctx.config.policyEngine,
|
|
506
|
+
threads: false,
|
|
507
|
+
hooks: ctx.config.hooks === false ? false : {
|
|
508
|
+
enabled: true,
|
|
509
|
+
builtIn: { logging: false, timing: false, metrics: false },
|
|
510
|
+
custom: [],
|
|
511
|
+
},
|
|
512
|
+
agentId,
|
|
513
|
+
blackboard: ctx.blackboard || undefined,
|
|
514
|
+
fileCache: ctx.fileCache || undefined,
|
|
515
|
+
budget: agentDef.economicsTuning
|
|
516
|
+
? { ...pooledBudget.budget, tuning: agentDef.economicsTuning }
|
|
517
|
+
: pooledBudget.budget,
|
|
518
|
+
sharedContextState: ctx.sharedContextState || undefined,
|
|
519
|
+
sharedEconomicsState: ctx.sharedEconomicsState || undefined,
|
|
520
|
+
});
|
|
521
|
+
// Inherit parent's mode
|
|
522
|
+
if (parentMode !== 'build') {
|
|
523
|
+
subAgent.setMode(parentMode);
|
|
524
|
+
}
|
|
525
|
+
// APPROVAL BATCHING
|
|
526
|
+
const swarmPerms = ctx.config.swarm && typeof ctx.config.swarm === 'object'
|
|
527
|
+
? ctx.config.swarm.permissions : undefined;
|
|
528
|
+
const baseAutoApprove = ['read_file', 'list_files', 'glob', 'grep', 'show_file_history', 'show_session_changes'];
|
|
529
|
+
const baseScopedApprove = isSwarmWorker
|
|
530
|
+
? {
|
|
531
|
+
write_file: { paths: ['src/', 'tests/', 'tools/'] },
|
|
532
|
+
edit_file: { paths: ['src/', 'tests/', 'tools/'] },
|
|
533
|
+
bash: { paths: ['src/', 'tests/', 'tools/'] },
|
|
534
|
+
}
|
|
535
|
+
: {
|
|
536
|
+
write_file: { paths: ['src/', 'tests/', 'tools/'] },
|
|
537
|
+
edit_file: { paths: ['src/', 'tests/', 'tools/'] },
|
|
538
|
+
};
|
|
539
|
+
const baseRequireApproval = isSwarmWorker ? ['delete_file'] : ['bash', 'delete_file'];
|
|
540
|
+
const mergedScope = mergeApprovalScopeWithProfile({
|
|
541
|
+
autoApprove: swarmPerms?.autoApprove
|
|
542
|
+
? [...new Set([...baseAutoApprove, ...swarmPerms.autoApprove])]
|
|
543
|
+
: baseAutoApprove,
|
|
544
|
+
scopedApprove: swarmPerms?.scopedApprove
|
|
545
|
+
? { ...baseScopedApprove, ...swarmPerms.scopedApprove }
|
|
546
|
+
: baseScopedApprove,
|
|
547
|
+
requireApproval: swarmPerms?.requireApproval
|
|
548
|
+
? swarmPerms.requireApproval
|
|
549
|
+
: baseRequireApproval,
|
|
550
|
+
}, policyResolution.profile);
|
|
551
|
+
subAgent.setApprovalScope(mergedScope);
|
|
552
|
+
subAgent.setParentIterations(ctx.getTotalIterations());
|
|
553
|
+
// UNIFIED TRACING
|
|
554
|
+
if (ctx.traceCollector) {
|
|
555
|
+
const subagentTraceView = ctx.traceCollector.createSubagentView({
|
|
556
|
+
parentSessionId: ctx.traceCollector.getSessionId() || 'unknown',
|
|
557
|
+
agentType: agentName,
|
|
558
|
+
spawnedAtIteration: ctx.state.iteration,
|
|
559
|
+
});
|
|
560
|
+
subAgent.setTraceCollector(subagentTraceView);
|
|
561
|
+
}
|
|
562
|
+
// GRACEFUL TIMEOUT with WRAPUP PHASE
|
|
563
|
+
const IDLE_TIMEOUT = agentDef.idleTimeout ?? 120000;
|
|
564
|
+
let WRAPUP_WINDOW = 30000;
|
|
565
|
+
let IDLE_CHECK_INTERVAL = 5000;
|
|
566
|
+
if (ctx.config.subagent) {
|
|
567
|
+
WRAPUP_WINDOW = ctx.config.subagent.wrapupWindowMs ?? WRAPUP_WINDOW;
|
|
568
|
+
IDLE_CHECK_INTERVAL = ctx.config.subagent.idleCheckIntervalMs ?? IDLE_CHECK_INTERVAL;
|
|
569
|
+
}
|
|
570
|
+
const progressAwareTimeout = createGracefulTimeout(subagentTimeout, IDLE_TIMEOUT, WRAPUP_WINDOW, IDLE_CHECK_INTERVAL);
|
|
571
|
+
progressAwareTimeout.onWrapupWarning(() => {
|
|
572
|
+
ctx.emit({
|
|
573
|
+
type: 'subagent.wrapup.started',
|
|
574
|
+
agentId,
|
|
575
|
+
agentType: agentName,
|
|
576
|
+
reason: 'Timeout approaching - graceful wrapup window opened',
|
|
577
|
+
elapsedMs: Date.now() - startTime,
|
|
578
|
+
});
|
|
579
|
+
subAgent.requestWrapup('Timeout approaching — produce structured summary');
|
|
580
|
+
});
|
|
581
|
+
// Forward events from subagent
|
|
582
|
+
const unsubSubAgent = subAgent.subscribe(event => {
|
|
583
|
+
const taggedEvent = { ...event, subagent: agentName, subagentId: agentId };
|
|
584
|
+
ctx.emit(taggedEvent);
|
|
585
|
+
const progressEvents = ['tool.start', 'tool.complete', 'llm.start', 'llm.complete'];
|
|
586
|
+
if (progressEvents.includes(event.type)) {
|
|
587
|
+
progressAwareTimeout.reportProgress();
|
|
588
|
+
}
|
|
589
|
+
});
|
|
590
|
+
// Link parent's cancellation
|
|
591
|
+
const parentSource = ctx.cancellation?.getSource();
|
|
592
|
+
const effectiveSource = parentSource
|
|
593
|
+
? createLinkedToken(parentSource, progressAwareTimeout)
|
|
594
|
+
: progressAwareTimeout;
|
|
595
|
+
subAgent.setExternalCancellation(effectiveSource.token);
|
|
596
|
+
// Pause parent's duration timer
|
|
597
|
+
ctx.economics?.pauseDuration();
|
|
598
|
+
try {
|
|
599
|
+
const result = await race(subAgent.run(task), effectiveSource.token);
|
|
600
|
+
const duration = Date.now() - startTime;
|
|
601
|
+
// Extract subagent's pending plan and merge into parent's plan
|
|
602
|
+
let queuedChangeSummary = '';
|
|
603
|
+
let queuedChangesCount = 0;
|
|
604
|
+
if (subAgent.hasPendingPlan()) {
|
|
605
|
+
const subPlan = subAgent.getPendingPlan();
|
|
606
|
+
if (subPlan && subPlan.proposedChanges.length > 0) {
|
|
607
|
+
queuedChangesCount = subPlan.proposedChanges.length;
|
|
608
|
+
ctx.emit({
|
|
609
|
+
type: 'agent.pending_plan',
|
|
610
|
+
agentId: agentName,
|
|
611
|
+
changes: subPlan.proposedChanges,
|
|
612
|
+
});
|
|
613
|
+
const changeSummaries = subPlan.proposedChanges.map(c => {
|
|
614
|
+
if (c.tool === 'write_file' || c.tool === 'edit_file') {
|
|
615
|
+
const path = c.args.path || c.args.file_path || '(unknown file)';
|
|
616
|
+
return ` - [${c.tool}] ${path}: ${c.reason}`;
|
|
617
|
+
}
|
|
618
|
+
else if (c.tool === 'bash') {
|
|
619
|
+
const cmd = String(c.args.command || '').slice(0, 60);
|
|
620
|
+
return ` - [bash] ${cmd}${String(c.args.command || '').length > 60 ? '...' : ''}: ${c.reason}`;
|
|
621
|
+
}
|
|
622
|
+
return ` - [${c.tool}]: ${c.reason}`;
|
|
623
|
+
});
|
|
624
|
+
queuedChangeSummary = `\n\n[PLAN MODE - CHANGES QUEUED TO PARENT]\n` +
|
|
625
|
+
`The following ${subPlan.proposedChanges.length} change(s) have been queued in the parent's pending plan:\n` +
|
|
626
|
+
changeSummaries.join('\n') + '\n' +
|
|
627
|
+
`\nThese changes are now in YOUR pending plan. The task for this subagent is COMPLETE.\n` +
|
|
628
|
+
`Do NOT spawn another agent for the same task - the changes are already queued.\n` +
|
|
629
|
+
`Use /show-plan to see all pending changes, /approve to execute them.`;
|
|
630
|
+
for (const change of subPlan.proposedChanges) {
|
|
631
|
+
ctx.pendingPlanManager.addProposedChange(change.tool, { ...change.args, _fromSubagent: agentName }, `[${agentName}] ${change.reason}`, change.toolCallId);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
if (subPlan?.explorationSummary) {
|
|
635
|
+
ctx.pendingPlanManager.appendExplorationFinding(`[${agentName}] ${subPlan.explorationSummary}`);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
const finalOutput = queuedChangeSummary
|
|
639
|
+
? (result.response || '') + queuedChangeSummary
|
|
640
|
+
: (result.response || result.error || '');
|
|
641
|
+
const structured = parseStructuredClosureReport(result.response || '', 'completed');
|
|
642
|
+
const subagentFilePaths = subAgent.getModifiedFilePaths();
|
|
643
|
+
const spawnResultFinal = {
|
|
644
|
+
success: result.success,
|
|
645
|
+
output: finalOutput,
|
|
646
|
+
metrics: {
|
|
647
|
+
tokens: result.metrics.totalTokens,
|
|
648
|
+
duration,
|
|
649
|
+
toolCalls: result.metrics.toolCalls,
|
|
650
|
+
},
|
|
651
|
+
structured,
|
|
652
|
+
filesModified: subagentFilePaths,
|
|
653
|
+
};
|
|
654
|
+
// Save to output store
|
|
655
|
+
if (ctx.subagentOutputStore) {
|
|
656
|
+
const outputEntry = {
|
|
657
|
+
id: agentId,
|
|
658
|
+
agentId,
|
|
659
|
+
agentName,
|
|
660
|
+
task,
|
|
661
|
+
fullOutput: finalOutput,
|
|
662
|
+
structured,
|
|
663
|
+
filesModified: subagentFilePaths,
|
|
664
|
+
filesCreated: [],
|
|
665
|
+
timestamp: new Date(),
|
|
666
|
+
tokensUsed: result.metrics.totalTokens,
|
|
667
|
+
durationMs: duration,
|
|
668
|
+
};
|
|
669
|
+
const storeId = ctx.subagentOutputStore.save(outputEntry);
|
|
670
|
+
spawnResultFinal.outputStoreId = storeId;
|
|
671
|
+
}
|
|
672
|
+
if (workerResultId && ctx.store?.hasWorkerResultsFeature()) {
|
|
673
|
+
try {
|
|
674
|
+
ctx.store.completeWorkerResult(workerResultId, {
|
|
675
|
+
fullOutput: finalOutput,
|
|
676
|
+
summary: finalOutput.slice(0, 500),
|
|
677
|
+
artifacts: structured ? [{ type: 'structured_report', data: structured }] : undefined,
|
|
678
|
+
metrics: {
|
|
679
|
+
tokens: result.metrics.totalTokens,
|
|
680
|
+
duration,
|
|
681
|
+
toolCalls: result.metrics.toolCalls,
|
|
682
|
+
},
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
catch (storeErr) {
|
|
686
|
+
ctx.observability?.logger?.warn('Failed to persist worker result', {
|
|
687
|
+
agentId,
|
|
688
|
+
error: storeErr.message,
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
ctx.emit({
|
|
693
|
+
type: 'agent.complete',
|
|
694
|
+
agentId,
|
|
695
|
+
agentType: agentName,
|
|
696
|
+
success: result.success,
|
|
697
|
+
output: finalOutput.slice(0, 500),
|
|
698
|
+
});
|
|
699
|
+
if (progressAwareTimeout.isInWrapupPhase()) {
|
|
700
|
+
ctx.emit({
|
|
701
|
+
type: 'subagent.wrapup.completed',
|
|
702
|
+
agentId,
|
|
703
|
+
agentType: agentName,
|
|
704
|
+
elapsedMs: Date.now() - startTime,
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
// Enhanced tracing
|
|
708
|
+
ctx.traceCollector?.record({
|
|
709
|
+
type: 'subagent.link',
|
|
710
|
+
data: {
|
|
711
|
+
parentSessionId: ctx.traceCollector.getSessionId() || 'unknown',
|
|
712
|
+
childSessionId,
|
|
713
|
+
childTraceId,
|
|
714
|
+
childConfig: {
|
|
715
|
+
agentType: agentName,
|
|
716
|
+
model: resolvedModel || 'default',
|
|
717
|
+
task,
|
|
718
|
+
tools: agentTools.map(t => t.name),
|
|
719
|
+
},
|
|
720
|
+
spawnContext: {
|
|
721
|
+
reason: `Delegated task: ${task.slice(0, 100)}`,
|
|
722
|
+
expectedOutcome: agentDef.description,
|
|
723
|
+
parentIteration: ctx.state.iteration,
|
|
724
|
+
},
|
|
725
|
+
result: {
|
|
726
|
+
success: result.success,
|
|
727
|
+
summary: (result.response || result.error || '').slice(0, 500),
|
|
728
|
+
tokensUsed: result.metrics.totalTokens,
|
|
729
|
+
durationMs: duration,
|
|
730
|
+
},
|
|
731
|
+
},
|
|
732
|
+
});
|
|
733
|
+
unsubSubAgent();
|
|
734
|
+
await subAgent.cleanup();
|
|
735
|
+
ctx.spawnedTasks.set(taskKey, {
|
|
736
|
+
timestamp: Date.now(),
|
|
737
|
+
result: finalOutput,
|
|
738
|
+
queuedChanges: queuedChangesCount,
|
|
739
|
+
});
|
|
740
|
+
return spawnResultFinal;
|
|
741
|
+
}
|
|
742
|
+
catch (err) {
|
|
743
|
+
// Handle cancellation
|
|
744
|
+
if (isCancellationError(err)) {
|
|
745
|
+
const duration = Date.now() - startTime;
|
|
746
|
+
const isUserCancellation = parentSource?.isCancellationRequested;
|
|
747
|
+
const reason = isUserCancellation
|
|
748
|
+
? 'User cancelled'
|
|
749
|
+
: err.reason || `Timed out after ${subagentTimeout}ms`;
|
|
750
|
+
ctx.emit({ type: 'agent.error', agentId, agentType: agentName, error: reason });
|
|
751
|
+
if (!isUserCancellation) {
|
|
752
|
+
ctx.emit({
|
|
753
|
+
type: 'subagent.timeout.hard_kill',
|
|
754
|
+
agentId,
|
|
755
|
+
agentType: agentName,
|
|
756
|
+
reason,
|
|
757
|
+
elapsedMs: Date.now() - startTime,
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
// PRESERVE PARTIAL RESULTS
|
|
761
|
+
const subagentState = subAgent.getState();
|
|
762
|
+
const subagentMetrics = subAgent.getMetrics();
|
|
763
|
+
const assistantMessages = subagentState.messages.filter(m => m.role === 'assistant');
|
|
764
|
+
const lastAssistantMsg = assistantMessages[assistantMessages.length - 1];
|
|
765
|
+
const partialResponse = typeof lastAssistantMsg?.content === 'string'
|
|
766
|
+
? lastAssistantMsg.content
|
|
767
|
+
: '';
|
|
768
|
+
let cancelledQueuedSummary = '';
|
|
769
|
+
if (subAgent.hasPendingPlan()) {
|
|
770
|
+
const subPlan = subAgent.getPendingPlan();
|
|
771
|
+
if (subPlan && subPlan.proposedChanges.length > 0) {
|
|
772
|
+
ctx.emit({
|
|
773
|
+
type: 'agent.pending_plan',
|
|
774
|
+
agentId: agentName,
|
|
775
|
+
changes: subPlan.proposedChanges,
|
|
776
|
+
});
|
|
777
|
+
const changeSummaries = subPlan.proposedChanges.map(c => {
|
|
778
|
+
if (c.tool === 'write_file' || c.tool === 'edit_file') {
|
|
779
|
+
const path = c.args.path || c.args.file_path || '(unknown file)';
|
|
780
|
+
return ` - [${c.tool}] ${path}: ${c.reason}`;
|
|
781
|
+
}
|
|
782
|
+
else if (c.tool === 'bash') {
|
|
783
|
+
const cmd = String(c.args.command || '').slice(0, 60);
|
|
784
|
+
return ` - [bash] ${cmd}...: ${c.reason}`;
|
|
785
|
+
}
|
|
786
|
+
return ` - [${c.tool}]: ${c.reason}`;
|
|
787
|
+
});
|
|
788
|
+
cancelledQueuedSummary = `\n\n[PLAN MODE - CHANGES QUEUED BEFORE CANCELLATION]\n` +
|
|
789
|
+
`${subPlan.proposedChanges.length} change(s) were queued to the parent plan:\n` +
|
|
790
|
+
changeSummaries.join('\n') + '\n' +
|
|
791
|
+
`These changes are preserved in your pending plan.`;
|
|
792
|
+
for (const change of subPlan.proposedChanges) {
|
|
793
|
+
ctx.pendingPlanManager.addProposedChange(change.tool, { ...change.args, _fromSubagent: agentName }, `[${agentName}] ${change.reason}`, change.toolCallId);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
if (subPlan?.explorationSummary) {
|
|
797
|
+
ctx.pendingPlanManager.appendExplorationFinding(`[${agentName}] ${subPlan.explorationSummary}`);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
const subagentFilePaths = subAgent.getModifiedFilePaths();
|
|
801
|
+
unsubSubAgent();
|
|
802
|
+
try {
|
|
803
|
+
await subAgent.cleanup();
|
|
804
|
+
}
|
|
805
|
+
catch {
|
|
806
|
+
// Ignore cleanup errors on cancellation
|
|
807
|
+
}
|
|
808
|
+
const baseOutput = isUserCancellation
|
|
809
|
+
? `Subagent '${agentName}' was cancelled by user.`
|
|
810
|
+
: `Subagent '${agentName}' timed out after ${Math.round(subagentTimeout / 1000)}s.`;
|
|
811
|
+
const partialResultSection = partialResponse
|
|
812
|
+
? `\n\n[PARTIAL RESULTS BEFORE TIMEOUT]\n${partialResponse.slice(0, 2000)}${partialResponse.length > 2000 ? '...(truncated)' : ''}`
|
|
813
|
+
: '';
|
|
814
|
+
ctx.traceCollector?.record({
|
|
815
|
+
type: 'subagent.link',
|
|
816
|
+
data: {
|
|
817
|
+
parentSessionId: ctx.traceCollector.getSessionId() || 'unknown',
|
|
818
|
+
childSessionId,
|
|
819
|
+
childTraceId,
|
|
820
|
+
childConfig: {
|
|
821
|
+
agentType: agentName,
|
|
822
|
+
model: resolvedModel || 'default',
|
|
823
|
+
task,
|
|
824
|
+
tools: agentTools.map(t => t.name),
|
|
825
|
+
},
|
|
826
|
+
spawnContext: {
|
|
827
|
+
reason: `Delegated task: ${task.slice(0, 100)}`,
|
|
828
|
+
expectedOutcome: agentDef.description,
|
|
829
|
+
parentIteration: ctx.state.iteration,
|
|
830
|
+
},
|
|
831
|
+
result: {
|
|
832
|
+
success: false,
|
|
833
|
+
summary: `[TIMEOUT] ${baseOutput}\n${partialResponse.slice(0, 200)}`,
|
|
834
|
+
tokensUsed: subagentMetrics.totalTokens,
|
|
835
|
+
durationMs: duration,
|
|
836
|
+
},
|
|
837
|
+
},
|
|
838
|
+
});
|
|
839
|
+
const exitReason = isUserCancellation ? 'cancelled' : 'timeout_graceful';
|
|
840
|
+
const structured = parseStructuredClosureReport(partialResponse, exitReason, task);
|
|
841
|
+
if (workerResultId && ctx.store?.hasWorkerResultsFeature()) {
|
|
842
|
+
try {
|
|
843
|
+
ctx.store.failWorkerResult(workerResultId, reason);
|
|
844
|
+
}
|
|
845
|
+
catch (storeErr) {
|
|
846
|
+
ctx.observability?.logger?.warn('Failed to mark cancelled worker result as failed', {
|
|
847
|
+
agentId,
|
|
848
|
+
error: storeErr.message,
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
return {
|
|
853
|
+
success: false,
|
|
854
|
+
output: baseOutput + partialResultSection + cancelledQueuedSummary,
|
|
855
|
+
metrics: {
|
|
856
|
+
tokens: subagentMetrics.totalTokens,
|
|
857
|
+
duration,
|
|
858
|
+
toolCalls: subagentMetrics.toolCalls,
|
|
859
|
+
},
|
|
860
|
+
structured,
|
|
861
|
+
filesModified: subagentFilePaths,
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
throw err;
|
|
865
|
+
}
|
|
866
|
+
finally {
|
|
867
|
+
ctx.economics?.resumeDuration();
|
|
868
|
+
effectiveSource.dispose();
|
|
869
|
+
progressAwareTimeout.dispose();
|
|
870
|
+
if (ctx.budgetPool && poolAllocationId) {
|
|
871
|
+
const subMetrics = subAgent.getMetrics();
|
|
872
|
+
ctx.budgetPool.recordUsage(poolAllocationId, subMetrics.totalTokens, subMetrics.estimatedCost);
|
|
873
|
+
ctx.budgetPool.release(poolAllocationId);
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
catch (err) {
|
|
878
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
879
|
+
ctx.emit({ type: 'agent.error', agentId, agentType: agentName, error });
|
|
880
|
+
if (workerResultId && ctx.store?.hasWorkerResultsFeature()) {
|
|
881
|
+
try {
|
|
882
|
+
ctx.store.failWorkerResult(workerResultId, error);
|
|
883
|
+
}
|
|
884
|
+
catch (storeErr) {
|
|
885
|
+
ctx.observability?.logger?.warn('Failed to mark worker result as failed', {
|
|
886
|
+
agentId,
|
|
887
|
+
error: storeErr.message,
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
return {
|
|
892
|
+
success: false,
|
|
893
|
+
output: `Agent error: ${error}`,
|
|
894
|
+
metrics: { tokens: 0, duration: Date.now() - startTime, toolCalls: 0 },
|
|
895
|
+
};
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
/**
|
|
899
|
+
* Spawn multiple agents in parallel to work on independent tasks.
|
|
900
|
+
* Uses DynamicBudgetPool for parallel spawns and SubagentSupervisor for monitoring.
|
|
901
|
+
* Extracted from ProductionAgent.spawnAgentsParallel().
|
|
902
|
+
*/
|
|
903
|
+
export async function spawnAgentsParallel(tasks, ctx, mutators, createSubAgent) {
|
|
904
|
+
ctx.emit({
|
|
905
|
+
type: 'parallel.spawn.start',
|
|
906
|
+
count: tasks.length,
|
|
907
|
+
agents: tasks.map(t => t.agent),
|
|
908
|
+
});
|
|
909
|
+
let settled;
|
|
910
|
+
const originalPool = ctx.budgetPool;
|
|
911
|
+
const supervisor = tasks.length > 1 ? createSubagentSupervisor() : null;
|
|
912
|
+
if (ctx.budgetPool && tasks.length > 1) {
|
|
913
|
+
const poolStats = ctx.budgetPool.getStats();
|
|
914
|
+
const dynamicPool = createDynamicBudgetPool(poolStats.tokensRemaining, 0.1);
|
|
915
|
+
dynamicPool.setExpectedChildren(tasks.length);
|
|
916
|
+
// Temporarily replace the budget pool
|
|
917
|
+
mutators.setBudgetPool(dynamicPool);
|
|
918
|
+
try {
|
|
919
|
+
const promises = tasks.map(({ agent, task }) => {
|
|
920
|
+
const spawnPromise = spawnAgent(agent, task, ctx, createSubAgent);
|
|
921
|
+
if (supervisor) {
|
|
922
|
+
const handle = createSubagentHandle(`parallel-${agent}-${Date.now()}`, agent, task, spawnPromise, {});
|
|
923
|
+
supervisor.add(handle);
|
|
924
|
+
}
|
|
925
|
+
return spawnPromise;
|
|
926
|
+
});
|
|
927
|
+
settled = await Promise.allSettled(promises);
|
|
928
|
+
}
|
|
929
|
+
finally {
|
|
930
|
+
mutators.setBudgetPool(originalPool);
|
|
931
|
+
supervisor?.stop();
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
else {
|
|
935
|
+
const promises = tasks.map(({ agent, task }) => spawnAgent(agent, task, ctx, createSubAgent));
|
|
936
|
+
settled = await Promise.allSettled(promises);
|
|
937
|
+
}
|
|
938
|
+
const results = settled.map((result, i) => {
|
|
939
|
+
if (result.status === 'fulfilled') {
|
|
940
|
+
return result.value;
|
|
941
|
+
}
|
|
942
|
+
const error = result.reason instanceof Error ? result.reason.message : String(result.reason);
|
|
943
|
+
ctx.emit({
|
|
944
|
+
type: 'agent.error',
|
|
945
|
+
agentId: tasks[i].agent,
|
|
946
|
+
error: `Unexpected parallel spawn error: ${error}`,
|
|
947
|
+
});
|
|
948
|
+
return {
|
|
949
|
+
success: false,
|
|
950
|
+
output: `Parallel spawn error: ${error}`,
|
|
951
|
+
metrics: { tokens: 0, duration: 0, toolCalls: 0 },
|
|
952
|
+
};
|
|
953
|
+
});
|
|
954
|
+
ctx.emit({
|
|
955
|
+
type: 'parallel.spawn.complete',
|
|
956
|
+
count: tasks.length,
|
|
957
|
+
successCount: results.filter(r => r.success).length,
|
|
958
|
+
results: results.map((r, i) => ({
|
|
959
|
+
agent: tasks[i].agent,
|
|
960
|
+
success: r.success,
|
|
961
|
+
tokens: r.metrics?.tokens || 0,
|
|
962
|
+
})),
|
|
963
|
+
});
|
|
964
|
+
return results;
|
|
965
|
+
}
|
|
966
|
+
//# sourceMappingURL=subagent-spawner.js.map
|