macro-agent 0.2.5 → 0.2.6
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/LICENSE +1 -1
- package/README.md +30 -56
- package/dist/acp/websocket-server.d.ts +6 -0
- package/dist/acp/websocket-server.d.ts.map +1 -1
- package/dist/acp/websocket-server.js +13 -0
- package/dist/acp/websocket-server.js.map +1 -1
- package/dist/acp/websocket-stream.d.ts +30 -0
- package/dist/acp/websocket-stream.d.ts.map +1 -0
- package/dist/acp/websocket-stream.js +118 -0
- package/dist/acp/websocket-stream.js.map +1 -0
- package/dist/activity/deduplication.d.ts +85 -0
- package/dist/activity/deduplication.d.ts.map +1 -0
- package/dist/activity/deduplication.js +149 -0
- package/dist/activity/deduplication.js.map +1 -0
- package/dist/activity/index.d.ts +16 -0
- package/dist/activity/index.d.ts.map +1 -0
- package/dist/activity/index.js +17 -0
- package/dist/activity/index.js.map +1 -0
- package/dist/activity/relevance.d.ts +81 -0
- package/dist/activity/relevance.d.ts.map +1 -0
- package/dist/activity/relevance.js +161 -0
- package/dist/activity/relevance.js.map +1 -0
- package/dist/activity/types.d.ts +169 -0
- package/dist/activity/types.d.ts.map +1 -0
- package/dist/activity/types.js +33 -0
- package/dist/activity/types.js.map +1 -0
- package/dist/activity/watcher.d.ts +64 -0
- package/dist/activity/watcher.d.ts.map +1 -0
- package/dist/activity/watcher.js +212 -0
- package/dist/activity/watcher.js.map +1 -0
- package/dist/adapters/federation.d.ts +8 -1
- package/dist/adapters/federation.d.ts.map +1 -1
- package/dist/adapters/federation.js +33 -2
- package/dist/adapters/federation.js.map +1 -1
- package/dist/agent/agent-manager-v2.d.ts.map +1 -1
- package/dist/agent/agent-manager-v2.js +7 -24
- package/dist/agent/agent-manager-v2.js.map +1 -1
- package/dist/agent/wake.d.ts +85 -0
- package/dist/agent/wake.d.ts.map +1 -0
- package/dist/agent/wake.js +278 -0
- package/dist/agent/wake.js.map +1 -0
- package/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +41 -25
- package/dist/api/server.js.map +1 -1
- package/dist/api/types.d.ts +6 -0
- package/dist/api/types.d.ts.map +1 -1
- package/dist/auth/server-auth.d.ts +43 -0
- package/dist/auth/server-auth.d.ts.map +1 -0
- package/dist/auth/server-auth.js +80 -0
- package/dist/auth/server-auth.js.map +1 -0
- package/dist/auth/token.d.ts +4 -0
- package/dist/auth/token.d.ts.map +1 -1
- package/dist/auth/token.js +7 -6
- package/dist/auth/token.js.map +1 -1
- package/dist/boot-v2.d.ts +6 -3
- package/dist/boot-v2.d.ts.map +1 -1
- package/dist/boot-v2.js +44 -25
- package/dist/boot-v2.js.map +1 -1
- package/dist/cognitive/team-lifecycle.d.ts +82 -0
- package/dist/cognitive/team-lifecycle.d.ts.map +1 -0
- package/dist/cognitive/team-lifecycle.js +121 -0
- package/dist/cognitive/team-lifecycle.js.map +1 -0
- package/dist/cognitive/trajectory-extractor.d.ts +25 -0
- package/dist/cognitive/trajectory-extractor.d.ts.map +1 -0
- package/dist/cognitive/trajectory-extractor.js +134 -0
- package/dist/cognitive/trajectory-extractor.js.map +1 -0
- package/dist/control/control-server.d.ts.map +1 -1
- package/dist/control/control-server.js +10 -0
- package/dist/control/control-server.js.map +1 -1
- package/dist/dispatch/mail-inbound-consumer.d.ts.map +1 -1
- package/dist/dispatch/mail-inbound-consumer.js +43 -10
- package/dist/dispatch/mail-inbound-consumer.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/lifecycle/handlers/generic.d.ts +27 -0
- package/dist/lifecycle/handlers/generic.d.ts.map +1 -0
- package/dist/lifecycle/handlers/generic.js +56 -0
- package/dist/lifecycle/handlers/generic.js.map +1 -0
- package/dist/lifecycle/handlers/index.d.ts +51 -0
- package/dist/lifecycle/handlers/index.d.ts.map +1 -0
- package/dist/lifecycle/handlers/index.js +110 -0
- package/dist/lifecycle/handlers/index.js.map +1 -0
- package/dist/lifecycle/handlers/integrator.d.ts +81 -0
- package/dist/lifecycle/handlers/integrator.d.ts.map +1 -0
- package/dist/lifecycle/handlers/integrator.js +451 -0
- package/dist/lifecycle/handlers/integrator.js.map +1 -0
- package/dist/lifecycle/handlers/monitor.d.ts +29 -0
- package/dist/lifecycle/handlers/monitor.d.ts.map +1 -0
- package/dist/lifecycle/handlers/monitor.js +79 -0
- package/dist/lifecycle/handlers/monitor.js.map +1 -0
- package/dist/lifecycle/handlers/worker.d.ts +56 -0
- package/dist/lifecycle/handlers/worker.d.ts.map +1 -0
- package/dist/lifecycle/handlers/worker.js +381 -0
- package/dist/lifecycle/handlers/worker.js.map +1 -0
- package/dist/lifecycle/handlers-v2.d.ts +0 -2
- package/dist/lifecycle/handlers-v2.d.ts.map +1 -1
- package/dist/lifecycle/handlers-v2.js +4 -55
- package/dist/lifecycle/handlers-v2.js.map +1 -1
- package/dist/mail/conversation-map.d.ts +33 -0
- package/dist/mail/conversation-map.d.ts.map +1 -0
- package/dist/mail/conversation-map.js +61 -0
- package/dist/mail/conversation-map.js.map +1 -0
- package/dist/mail/index.d.ts +11 -0
- package/dist/mail/index.d.ts.map +1 -0
- package/dist/mail/index.js +11 -0
- package/dist/mail/index.js.map +1 -0
- package/dist/mail/mail-service.d.ts +85 -0
- package/dist/mail/mail-service.d.ts.map +1 -0
- package/dist/mail/mail-service.js +121 -0
- package/dist/mail/mail-service.js.map +1 -0
- package/dist/mail/stores/eventstore-conversation-store.d.ts +40 -0
- package/dist/mail/stores/eventstore-conversation-store.d.ts.map +1 -0
- package/dist/mail/stores/eventstore-conversation-store.js +131 -0
- package/dist/mail/stores/eventstore-conversation-store.js.map +1 -0
- package/dist/mail/stores/eventstore-participant-store.d.ts +43 -0
- package/dist/mail/stores/eventstore-participant-store.d.ts.map +1 -0
- package/dist/mail/stores/eventstore-participant-store.js +145 -0
- package/dist/mail/stores/eventstore-participant-store.js.map +1 -0
- package/dist/mail/stores/eventstore-thread-store.d.ts +46 -0
- package/dist/mail/stores/eventstore-thread-store.d.ts.map +1 -0
- package/dist/mail/stores/eventstore-thread-store.js +118 -0
- package/dist/mail/stores/eventstore-thread-store.js.map +1 -0
- package/dist/mail/stores/eventstore-turn-store.d.ts +47 -0
- package/dist/mail/stores/eventstore-turn-store.d.ts.map +1 -0
- package/dist/mail/stores/eventstore-turn-store.js +153 -0
- package/dist/mail/stores/eventstore-turn-store.js.map +1 -0
- package/dist/mail/stores/index.d.ts +12 -0
- package/dist/mail/stores/index.d.ts.map +1 -0
- package/dist/mail/stores/index.js +12 -0
- package/dist/mail/stores/index.js.map +1 -0
- package/dist/mail/stores/types.d.ts +146 -0
- package/dist/mail/stores/types.d.ts.map +1 -0
- package/dist/mail/stores/types.js +13 -0
- package/dist/mail/stores/types.js.map +1 -0
- package/dist/mail/turn-recorder.d.ts +30 -0
- package/dist/mail/turn-recorder.d.ts.map +1 -0
- package/dist/mail/turn-recorder.js +98 -0
- package/dist/mail/turn-recorder.js.map +1 -0
- package/dist/map/adapter/acp-over-map.d.ts +115 -0
- package/dist/map/adapter/acp-over-map.d.ts.map +1 -0
- package/dist/map/adapter/acp-over-map.js +1024 -0
- package/dist/map/adapter/acp-over-map.js.map +1 -0
- package/dist/map/adapter/connection-manager.d.ts +150 -0
- package/dist/map/adapter/connection-manager.d.ts.map +1 -0
- package/dist/map/adapter/connection-manager.js +207 -0
- package/dist/map/adapter/connection-manager.js.map +1 -0
- package/dist/map/adapter/event-log.d.ts +87 -0
- package/dist/map/adapter/event-log.d.ts.map +1 -0
- package/dist/map/adapter/event-log.js +122 -0
- package/dist/map/adapter/event-log.js.map +1 -0
- package/dist/map/adapter/event-translator.d.ts +85 -0
- package/dist/map/adapter/event-translator.d.ts.map +1 -0
- package/dist/map/adapter/event-translator.js +295 -0
- package/dist/map/adapter/event-translator.js.map +1 -0
- package/dist/map/adapter/extensions/agent-detection.d.ts +49 -0
- package/dist/map/adapter/extensions/agent-detection.d.ts.map +1 -0
- package/dist/map/adapter/extensions/agent-detection.js +91 -0
- package/dist/map/adapter/extensions/agent-detection.js.map +1 -0
- package/dist/map/adapter/extensions/agent-lifecycle.d.ts +82 -0
- package/dist/map/adapter/extensions/agent-lifecycle.d.ts.map +1 -0
- package/dist/map/adapter/extensions/agent-lifecycle.js +164 -0
- package/dist/map/adapter/extensions/agent-lifecycle.js.map +1 -0
- package/dist/map/adapter/extensions/cognitive.d.ts +27 -0
- package/dist/map/adapter/extensions/cognitive.d.ts.map +1 -0
- package/dist/map/adapter/extensions/cognitive.js +158 -0
- package/dist/map/adapter/extensions/cognitive.js.map +1 -0
- package/dist/map/adapter/extensions/index.d.ts +95 -0
- package/dist/map/adapter/extensions/index.d.ts.map +1 -0
- package/dist/map/adapter/extensions/index.js +228 -0
- package/dist/map/adapter/extensions/index.js.map +1 -0
- package/dist/map/adapter/extensions/mcp-bridge.d.ts +57 -0
- package/dist/map/adapter/extensions/mcp-bridge.d.ts.map +1 -0
- package/dist/map/adapter/extensions/mcp-bridge.js +745 -0
- package/dist/map/adapter/extensions/mcp-bridge.js.map +1 -0
- package/dist/map/adapter/extensions/resume.d.ts +47 -0
- package/dist/map/adapter/extensions/resume.d.ts.map +1 -0
- package/dist/map/adapter/extensions/resume.js +59 -0
- package/dist/map/adapter/extensions/resume.js.map +1 -0
- package/dist/map/adapter/extensions/streams.d.ts +95 -0
- package/dist/map/adapter/extensions/streams.d.ts.map +1 -0
- package/dist/map/adapter/extensions/streams.js +515 -0
- package/dist/map/adapter/extensions/streams.js.map +1 -0
- package/dist/map/adapter/extensions/task.d.ts +40 -0
- package/dist/map/adapter/extensions/task.d.ts.map +1 -0
- package/dist/map/adapter/extensions/task.js +197 -0
- package/dist/map/adapter/extensions/task.js.map +1 -0
- package/dist/map/adapter/extensions/update-metadata.d.ts +29 -0
- package/dist/map/adapter/extensions/update-metadata.d.ts.map +1 -0
- package/dist/map/adapter/extensions/update-metadata.js +67 -0
- package/dist/map/adapter/extensions/update-metadata.js.map +1 -0
- package/dist/map/adapter/extensions/wake.d.ts +60 -0
- package/dist/map/adapter/extensions/wake.d.ts.map +1 -0
- package/dist/map/adapter/extensions/wake.js +144 -0
- package/dist/map/adapter/extensions/wake.js.map +1 -0
- package/dist/map/adapter/extensions/workspace-files.d.ts +42 -0
- package/dist/map/adapter/extensions/workspace-files.d.ts.map +1 -0
- package/dist/map/adapter/extensions/workspace-files.js +338 -0
- package/dist/map/adapter/extensions/workspace-files.js.map +1 -0
- package/dist/map/adapter/extensions/workspace.d.ts +57 -0
- package/dist/map/adapter/extensions/workspace.d.ts.map +1 -0
- package/dist/map/adapter/extensions/workspace.js +81 -0
- package/dist/map/adapter/extensions/workspace.js.map +1 -0
- package/dist/map/adapter/index.d.ts +20 -0
- package/dist/map/adapter/index.d.ts.map +1 -0
- package/dist/map/adapter/index.js +40 -0
- package/dist/map/adapter/index.js.map +1 -0
- package/dist/map/adapter/interface.d.ts +450 -0
- package/dist/map/adapter/interface.d.ts.map +1 -0
- package/dist/map/adapter/interface.js +24 -0
- package/dist/map/adapter/interface.js.map +1 -0
- package/dist/map/adapter/mail-handler-adapter.d.ts +27 -0
- package/dist/map/adapter/mail-handler-adapter.d.ts.map +1 -0
- package/dist/map/adapter/mail-handler-adapter.js +292 -0
- package/dist/map/adapter/mail-handler-adapter.js.map +1 -0
- package/dist/map/adapter/map-adapter.d.ts +200 -0
- package/dist/map/adapter/map-adapter.d.ts.map +1 -0
- package/dist/map/adapter/map-adapter.js +1199 -0
- package/dist/map/adapter/map-adapter.js.map +1 -0
- package/dist/map/adapter/rpc-handler.d.ts +263 -0
- package/dist/map/adapter/rpc-handler.d.ts.map +1 -0
- package/dist/map/adapter/rpc-handler.js +365 -0
- package/dist/map/adapter/rpc-handler.js.map +1 -0
- package/dist/map/adapter/subscription-manager.d.ts +174 -0
- package/dist/map/adapter/subscription-manager.d.ts.map +1 -0
- package/dist/map/adapter/subscription-manager.js +248 -0
- package/dist/map/adapter/subscription-manager.js.map +1 -0
- package/dist/map/adapter/types.d.ts +194 -0
- package/dist/map/adapter/types.d.ts.map +1 -0
- package/dist/map/adapter/types.js +27 -0
- package/dist/map/adapter/types.js.map +1 -0
- package/dist/map/adapter/websocket-integration.d.ts +113 -0
- package/dist/map/adapter/websocket-integration.d.ts.map +1 -0
- package/dist/map/adapter/websocket-integration.js +134 -0
- package/dist/map/adapter/websocket-integration.js.map +1 -0
- package/dist/map/cascade-action-handler.d.ts.map +1 -1
- package/dist/map/cascade-action-handler.js +19 -5
- package/dist/map/cascade-action-handler.js.map +1 -1
- package/dist/map/federation/envelope.d.ts +98 -0
- package/dist/map/federation/envelope.d.ts.map +1 -0
- package/dist/map/federation/envelope.js +160 -0
- package/dist/map/federation/envelope.js.map +1 -0
- package/dist/map/federation/federation-handler.d.ts +50 -0
- package/dist/map/federation/federation-handler.d.ts.map +1 -0
- package/dist/map/federation/federation-handler.js +306 -0
- package/dist/map/federation/federation-handler.js.map +1 -0
- package/dist/map/federation/index.d.ts +14 -0
- package/dist/map/federation/index.d.ts.map +1 -0
- package/dist/map/federation/index.js +13 -0
- package/dist/map/federation/index.js.map +1 -0
- package/dist/map/federation/types.d.ts +239 -0
- package/dist/map/federation/types.d.ts.map +1 -0
- package/dist/map/federation/types.js +23 -0
- package/dist/map/federation/types.js.map +1 -0
- package/dist/map/server.d.ts.map +1 -1
- package/dist/map/server.js +17 -2
- package/dist/map/server.js.map +1 -1
- package/dist/map/types.d.ts +6 -0
- package/dist/map/types.d.ts.map +1 -1
- package/dist/mcp/map-client.d.ts +39 -0
- package/dist/mcp/map-client.d.ts.map +1 -0
- package/dist/mcp/map-client.js +129 -0
- package/dist/mcp/map-client.js.map +1 -0
- package/dist/mcp/mcp-server.d.ts +72 -0
- package/dist/mcp/mcp-server.d.ts.map +1 -0
- package/dist/mcp/mcp-server.js +1027 -0
- package/dist/mcp/mcp-server.js.map +1 -0
- package/dist/mcp/tools/claim_task.d.ts +35 -0
- package/dist/mcp/tools/claim_task.d.ts.map +1 -0
- package/dist/mcp/tools/claim_task.js +58 -0
- package/dist/mcp/tools/claim_task.js.map +1 -0
- package/dist/mcp/tools/done-v2.d.ts +0 -1
- package/dist/mcp/tools/done-v2.d.ts.map +1 -1
- package/dist/mcp/tools/done-v2.js +0 -1
- package/dist/mcp/tools/done-v2.js.map +1 -1
- package/dist/mcp/tools/done.d.ts +102 -0
- package/dist/mcp/tools/done.d.ts.map +1 -0
- package/dist/mcp/tools/done.js +253 -0
- package/dist/mcp/tools/done.js.map +1 -0
- package/dist/mcp/tools/inject_context.d.ts +61 -0
- package/dist/mcp/tools/inject_context.d.ts.map +1 -0
- package/dist/mcp/tools/inject_context.js +123 -0
- package/dist/mcp/tools/inject_context.js.map +1 -0
- package/dist/mcp/tools/list_claimable_tasks.d.ts +38 -0
- package/dist/mcp/tools/list_claimable_tasks.d.ts.map +1 -0
- package/dist/mcp/tools/list_claimable_tasks.js +63 -0
- package/dist/mcp/tools/list_claimable_tasks.js.map +1 -0
- package/dist/mcp/tools/unclaim_task.d.ts +31 -0
- package/dist/mcp/tools/unclaim_task.d.ts.map +1 -0
- package/dist/mcp/tools/unclaim_task.js +47 -0
- package/dist/mcp/tools/unclaim_task.js.map +1 -0
- package/dist/mcp/tools/wait_for_activity.d.ts +60 -0
- package/dist/mcp/tools/wait_for_activity.d.ts.map +1 -0
- package/dist/mcp/tools/wait_for_activity.js +135 -0
- package/dist/mcp/tools/wait_for_activity.js.map +1 -0
- package/dist/monitor/health-check-service.d.ts +143 -0
- package/dist/monitor/health-check-service.d.ts.map +1 -0
- package/dist/monitor/health-check-service.js +240 -0
- package/dist/monitor/health-check-service.js.map +1 -0
- package/dist/monitor/index.d.ts +14 -0
- package/dist/monitor/index.d.ts.map +1 -0
- package/dist/monitor/index.js +14 -0
- package/dist/monitor/index.js.map +1 -0
- package/dist/monitor/stall-detector.d.ts +109 -0
- package/dist/monitor/stall-detector.d.ts.map +1 -0
- package/dist/monitor/stall-detector.js +152 -0
- package/dist/monitor/stall-detector.js.map +1 -0
- package/dist/peer/capability-manager.d.ts +56 -0
- package/dist/peer/capability-manager.d.ts.map +1 -0
- package/dist/peer/capability-manager.js +186 -0
- package/dist/peer/capability-manager.js.map +1 -0
- package/dist/peer/encapsulation-manager.d.ts +190 -0
- package/dist/peer/encapsulation-manager.d.ts.map +1 -0
- package/dist/peer/encapsulation-manager.js +486 -0
- package/dist/peer/encapsulation-manager.js.map +1 -0
- package/dist/peer/federation-manager.d.ts +223 -0
- package/dist/peer/federation-manager.d.ts.map +1 -0
- package/dist/peer/federation-manager.js +528 -0
- package/dist/peer/federation-manager.js.map +1 -0
- package/dist/peer/hierarchy-errors.d.ts +208 -0
- package/dist/peer/hierarchy-errors.d.ts.map +1 -0
- package/dist/peer/hierarchy-errors.js +268 -0
- package/dist/peer/hierarchy-errors.js.map +1 -0
- package/dist/peer/hierarchy-protocol.d.ts +159 -0
- package/dist/peer/hierarchy-protocol.d.ts.map +1 -0
- package/dist/peer/hierarchy-protocol.js +142 -0
- package/dist/peer/hierarchy-protocol.js.map +1 -0
- package/dist/peer/index.d.ts +15 -0
- package/dist/peer/index.d.ts.map +1 -0
- package/dist/peer/index.js +15 -0
- package/dist/peer/index.js.map +1 -0
- package/dist/peer/peer-manager.d.ts +99 -0
- package/dist/peer/peer-manager.d.ts.map +1 -0
- package/dist/peer/peer-manager.js +333 -0
- package/dist/peer/peer-manager.js.map +1 -0
- package/dist/peer/task-delegation.d.ts +189 -0
- package/dist/peer/task-delegation.d.ts.map +1 -0
- package/dist/peer/task-delegation.js +303 -0
- package/dist/peer/task-delegation.js.map +1 -0
- package/dist/peer/transports/index.d.ts +8 -0
- package/dist/peer/transports/index.d.ts.map +1 -0
- package/dist/peer/transports/index.js +8 -0
- package/dist/peer/transports/index.js.map +1 -0
- package/dist/peer/transports/local-transport.d.ts +56 -0
- package/dist/peer/transports/local-transport.d.ts.map +1 -0
- package/dist/peer/transports/local-transport.js +263 -0
- package/dist/peer/transports/local-transport.js.map +1 -0
- package/dist/peer/transports/websocket-transport.d.ts +86 -0
- package/dist/peer/transports/websocket-transport.d.ts.map +1 -0
- package/dist/peer/transports/websocket-transport.js +338 -0
- package/dist/peer/transports/websocket-transport.js.map +1 -0
- package/dist/peer/types.d.ts +268 -0
- package/dist/peer/types.d.ts.map +1 -0
- package/dist/peer/types.js +36 -0
- package/dist/peer/types.js.map +1 -0
- package/dist/router/address-resolver.d.ts +102 -0
- package/dist/router/address-resolver.d.ts.map +1 -0
- package/dist/router/address-resolver.js +198 -0
- package/dist/router/address-resolver.js.map +1 -0
- package/dist/router/broadcast.d.ts +53 -0
- package/dist/router/broadcast.d.ts.map +1 -0
- package/dist/router/broadcast.js +75 -0
- package/dist/router/broadcast.js.map +1 -0
- package/dist/router/channels.d.ts +148 -0
- package/dist/router/channels.d.ts.map +1 -0
- package/dist/router/channels.js +126 -0
- package/dist/router/channels.js.map +1 -0
- package/dist/router/index.d.ts +21 -0
- package/dist/router/index.d.ts.map +1 -0
- package/dist/router/index.js +18 -0
- package/dist/router/index.js.map +1 -0
- package/dist/router/message-router.d.ts +197 -0
- package/dist/router/message-router.d.ts.map +1 -0
- package/dist/router/message-router.js +903 -0
- package/dist/router/message-router.js.map +1 -0
- package/dist/router/message-types.d.ts +183 -0
- package/dist/router/message-types.d.ts.map +1 -0
- package/dist/router/message-types.js +79 -0
- package/dist/router/message-types.js.map +1 -0
- package/dist/router/role-resolver.d.ts +67 -0
- package/dist/router/role-resolver.d.ts.map +1 -0
- package/dist/router/role-resolver.js +106 -0
- package/dist/router/role-resolver.js.map +1 -0
- package/dist/router/signals.d.ts +253 -0
- package/dist/router/signals.d.ts.map +1 -0
- package/dist/router/signals.js +53 -0
- package/dist/router/signals.js.map +1 -0
- package/dist/router/types.d.ts +191 -0
- package/dist/router/types.d.ts.map +1 -0
- package/dist/router/types.js +34 -0
- package/dist/router/types.js.map +1 -0
- package/dist/router/wake.d.ts +111 -0
- package/dist/router/wake.d.ts.map +1 -0
- package/dist/router/wake.js +180 -0
- package/dist/router/wake.js.map +1 -0
- package/dist/server/combined-server.d.ts +95 -0
- package/dist/server/combined-server.d.ts.map +1 -0
- package/dist/server/combined-server.js +335 -0
- package/dist/server/combined-server.js.map +1 -0
- package/dist/steering/index.d.ts +11 -0
- package/dist/steering/index.d.ts.map +1 -0
- package/dist/steering/index.js +11 -0
- package/dist/steering/index.js.map +1 -0
- package/dist/steering/inject.d.ts +39 -0
- package/dist/steering/inject.d.ts.map +1 -0
- package/dist/steering/inject.js +197 -0
- package/dist/steering/inject.js.map +1 -0
- package/dist/steering/types.d.ts +100 -0
- package/dist/steering/types.d.ts.map +1 -0
- package/dist/steering/types.js +11 -0
- package/dist/steering/types.js.map +1 -0
- package/dist/store/backends/index.d.ts +11 -0
- package/dist/store/backends/index.d.ts.map +1 -0
- package/dist/store/backends/index.js +15 -0
- package/dist/store/backends/index.js.map +1 -0
- package/dist/store/backends/json-backend.d.ts +23 -0
- package/dist/store/backends/json-backend.d.ts.map +1 -0
- package/dist/store/backends/json-backend.js +220 -0
- package/dist/store/backends/json-backend.js.map +1 -0
- package/dist/store/backends/memory-backend.d.ts +12 -0
- package/dist/store/backends/memory-backend.d.ts.map +1 -0
- package/dist/store/backends/memory-backend.js +205 -0
- package/dist/store/backends/memory-backend.js.map +1 -0
- package/dist/store/backends/sqlite-backend.d.ts +27 -0
- package/dist/store/backends/sqlite-backend.d.ts.map +1 -0
- package/dist/store/backends/sqlite-backend.js +231 -0
- package/dist/store/backends/sqlite-backend.js.map +1 -0
- package/dist/store/backends/tinybase-backend.d.ts +22 -0
- package/dist/store/backends/tinybase-backend.d.ts.map +1 -0
- package/dist/store/backends/tinybase-backend.js +203 -0
- package/dist/store/backends/tinybase-backend.js.map +1 -0
- package/dist/store/backends/types.d.ts +175 -0
- package/dist/store/backends/types.d.ts.map +1 -0
- package/dist/store/backends/types.js +28 -0
- package/dist/store/backends/types.js.map +1 -0
- package/dist/store/event-store.d.ts +128 -0
- package/dist/store/event-store.d.ts.map +1 -0
- package/dist/store/event-store.js +1751 -0
- package/dist/store/event-store.js.map +1 -0
- package/dist/store/instance.d.ts +290 -0
- package/dist/store/instance.d.ts.map +1 -0
- package/dist/store/instance.js +363 -0
- package/dist/store/instance.js.map +1 -0
- package/dist/store/migrations.d.ts +41 -0
- package/dist/store/migrations.d.ts.map +1 -0
- package/dist/store/migrations.js +79 -0
- package/dist/store/migrations.js.map +1 -0
- package/dist/store/test-utils.d.ts +63 -0
- package/dist/store/test-utils.d.ts.map +1 -0
- package/dist/store/test-utils.js +63 -0
- package/dist/store/test-utils.js.map +1 -0
- package/dist/store/types/config.d.ts +16 -0
- package/dist/store/types/config.d.ts.map +1 -0
- package/dist/store/types/config.js +8 -0
- package/dist/store/types/config.js.map +1 -0
- package/dist/store/types/conversations.d.ts +91 -0
- package/dist/store/types/conversations.d.ts.map +1 -0
- package/dist/store/types/conversations.js +8 -0
- package/dist/store/types/conversations.js.map +1 -0
- package/dist/store/types/sessions.d.ts +44 -0
- package/dist/store/types/sessions.d.ts.map +1 -0
- package/dist/store/types/sessions.js +9 -0
- package/dist/store/types/sessions.js.map +1 -0
- package/dist/store/types.d.ts +115 -0
- package/dist/store/types.d.ts.map +1 -0
- package/dist/store/types.js +6 -0
- package/dist/store/types.js.map +1 -0
- package/dist/task/backend/index.d.ts +93 -0
- package/dist/task/backend/index.d.ts.map +1 -0
- package/dist/task/backend/index.js +178 -0
- package/dist/task/backend/index.js.map +1 -0
- package/dist/task/backend/memory.d.ts +70 -0
- package/dist/task/backend/memory.d.ts.map +1 -0
- package/dist/task/backend/memory.js +621 -0
- package/dist/task/backend/memory.js.map +1 -0
- package/dist/task/backend/opentasks/backend.d.ts +140 -0
- package/dist/task/backend/opentasks/backend.d.ts.map +1 -0
- package/dist/task/backend/opentasks/backend.js +1023 -0
- package/dist/task/backend/opentasks/backend.js.map +1 -0
- package/dist/task/backend/opentasks/client.d.ts +337 -0
- package/dist/task/backend/opentasks/client.d.ts.map +1 -0
- package/dist/task/backend/opentasks/client.js +225 -0
- package/dist/task/backend/opentasks/client.js.map +1 -0
- package/dist/task/backend/opentasks/daemon-manager.d.ts +89 -0
- package/dist/task/backend/opentasks/daemon-manager.d.ts.map +1 -0
- package/dist/task/backend/opentasks/daemon-manager.js +195 -0
- package/dist/task/backend/opentasks/daemon-manager.js.map +1 -0
- package/dist/task/backend/opentasks/index.d.ts +21 -0
- package/dist/task/backend/opentasks/index.d.ts.map +1 -0
- package/dist/task/backend/opentasks/index.js +21 -0
- package/dist/task/backend/opentasks/index.js.map +1 -0
- package/dist/task/backend/opentasks/mapping.d.ts +48 -0
- package/dist/task/backend/opentasks/mapping.d.ts.map +1 -0
- package/dist/task/backend/opentasks/mapping.js +77 -0
- package/dist/task/backend/opentasks/mapping.js.map +1 -0
- package/dist/task/backend/sudocode/__tests__/integration/test-utils.d.ts +54 -0
- package/dist/task/backend/sudocode/__tests__/integration/test-utils.d.ts.map +1 -0
- package/dist/task/backend/sudocode/__tests__/integration/test-utils.js +88 -0
- package/dist/task/backend/sudocode/__tests__/integration/test-utils.js.map +1 -0
- package/dist/task/backend/sudocode/backend.d.ts +155 -0
- package/dist/task/backend/sudocode/backend.d.ts.map +1 -0
- package/dist/task/backend/sudocode/backend.js +942 -0
- package/dist/task/backend/sudocode/backend.js.map +1 -0
- package/dist/task/backend/sudocode/client.d.ts +303 -0
- package/dist/task/backend/sudocode/client.d.ts.map +1 -0
- package/dist/task/backend/sudocode/client.js +101 -0
- package/dist/task/backend/sudocode/client.js.map +1 -0
- package/dist/task/backend/sudocode/index.d.ts +19 -0
- package/dist/task/backend/sudocode/index.d.ts.map +1 -0
- package/dist/task/backend/sudocode/index.js +17 -0
- package/dist/task/backend/sudocode/index.js.map +1 -0
- package/dist/task/backend/sudocode/mapping.d.ts +51 -0
- package/dist/task/backend/sudocode/mapping.d.ts.map +1 -0
- package/dist/task/backend/sudocode/mapping.js +86 -0
- package/dist/task/backend/sudocode/mapping.js.map +1 -0
- package/dist/task/backend/sudocode/server-client.d.ts +56 -0
- package/dist/task/backend/sudocode/server-client.d.ts.map +1 -0
- package/dist/task/backend/sudocode/server-client.js +367 -0
- package/dist/task/backend/sudocode/server-client.js.map +1 -0
- package/dist/task/backend/sudocode/standalone-client.d.ts +91 -0
- package/dist/task/backend/sudocode/standalone-client.d.ts.map +1 -0
- package/dist/task/backend/sudocode/standalone-client.js +476 -0
- package/dist/task/backend/sudocode/standalone-client.js.map +1 -0
- package/dist/task/backend/sudocode/sync-policy.d.ts +166 -0
- package/dist/task/backend/sudocode/sync-policy.d.ts.map +1 -0
- package/dist/task/backend/sudocode/sync-policy.js +221 -0
- package/dist/task/backend/sudocode/sync-policy.js.map +1 -0
- package/dist/task/backend/sudocode/tools.d.ts +87 -0
- package/dist/task/backend/sudocode/tools.d.ts.map +1 -0
- package/dist/task/backend/sudocode/tools.js +743 -0
- package/dist/task/backend/sudocode/tools.js.map +1 -0
- package/dist/task/backend/tool-provider.d.ts +56 -0
- package/dist/task/backend/tool-provider.d.ts.map +1 -0
- package/dist/task/backend/tool-provider.js +424 -0
- package/dist/task/backend/tool-provider.js.map +1 -0
- package/dist/task/backend/types.d.ts +297 -0
- package/dist/task/backend/types.d.ts.map +1 -0
- package/dist/task/backend/types.js +27 -0
- package/dist/task/backend/types.js.map +1 -0
- package/dist/task/backend/unified-tool-provider.d.ts +57 -0
- package/dist/task/backend/unified-tool-provider.d.ts.map +1 -0
- package/dist/task/backend/unified-tool-provider.js +623 -0
- package/dist/task/backend/unified-tool-provider.js.map +1 -0
- package/dist/task/index.d.ts +7 -0
- package/dist/task/index.d.ts.map +1 -0
- package/dist/task/index.js +7 -0
- package/dist/task/index.js.map +1 -0
- package/dist/task/retry-policy.d.ts +89 -0
- package/dist/task/retry-policy.d.ts.map +1 -0
- package/dist/task/retry-policy.js +160 -0
- package/dist/task/retry-policy.js.map +1 -0
- package/dist/task/task-manager.d.ts +70 -0
- package/dist/task/task-manager.d.ts.map +1 -0
- package/dist/task/task-manager.js +319 -0
- package/dist/task/task-manager.js.map +1 -0
- package/dist/task/types.d.ts +72 -0
- package/dist/task/types.d.ts.map +1 -0
- package/dist/task/types.js +33 -0
- package/dist/task/types.js.map +1 -0
- package/dist/teams/team-loader.d.ts +2 -1
- package/dist/teams/team-loader.d.ts.map +1 -1
- package/dist/teams/team-loader.js +2 -1
- package/dist/teams/team-loader.js.map +1 -1
- package/dist/teams/team-manager.d.ts +101 -0
- package/dist/teams/team-manager.d.ts.map +1 -0
- package/dist/teams/team-manager.js +260 -0
- package/dist/teams/team-manager.js.map +1 -0
- package/dist/teams/team-runtime-v2.d.ts +0 -3
- package/dist/teams/team-runtime-v2.d.ts.map +1 -1
- package/dist/teams/team-runtime-v2.js +8 -87
- package/dist/teams/team-runtime-v2.js.map +1 -1
- package/dist/teams/team-runtime.d.ts +245 -0
- package/dist/teams/team-runtime.d.ts.map +1 -0
- package/dist/teams/team-runtime.js +1023 -0
- package/dist/teams/team-runtime.js.map +1 -0
- package/dist/trigger/router/index.d.ts +11 -0
- package/dist/trigger/router/index.d.ts.map +1 -0
- package/dist/trigger/router/index.js +10 -0
- package/dist/trigger/router/index.js.map +1 -0
- package/dist/trigger/router/strategies/ai-router-strategy.d.ts +34 -0
- package/dist/trigger/router/strategies/ai-router-strategy.d.ts.map +1 -0
- package/dist/trigger/router/strategies/ai-router-strategy.js +359 -0
- package/dist/trigger/router/strategies/ai-router-strategy.js.map +1 -0
- package/dist/trigger/router/strategies/direct-strategy.d.ts +32 -0
- package/dist/trigger/router/strategies/direct-strategy.d.ts.map +1 -0
- package/dist/trigger/router/strategies/direct-strategy.js +119 -0
- package/dist/trigger/router/strategies/direct-strategy.js.map +1 -0
- package/dist/trigger/router/strategies/index.d.ts +11 -0
- package/dist/trigger/router/strategies/index.d.ts.map +1 -0
- package/dist/trigger/router/strategies/index.js +11 -0
- package/dist/trigger/router/strategies/index.js.map +1 -0
- package/dist/trigger/router/strategies/role-strategy.d.ts +39 -0
- package/dist/trigger/router/strategies/role-strategy.d.ts.map +1 -0
- package/dist/trigger/router/strategies/role-strategy.js +207 -0
- package/dist/trigger/router/strategies/role-strategy.js.map +1 -0
- package/dist/trigger/router/trigger-router.d.ts +54 -0
- package/dist/trigger/router/trigger-router.d.ts.map +1 -0
- package/dist/trigger/router/trigger-router.js +362 -0
- package/dist/trigger/router/trigger-router.js.map +1 -0
- package/dist/trigger/router/types.d.ts +225 -0
- package/dist/trigger/router/types.d.ts.map +1 -0
- package/dist/trigger/router/types.js +10 -0
- package/dist/trigger/router/types.js.map +1 -0
- package/dist/trigger/sources/cron/cron-service.d.ts.map +1 -1
- package/dist/trigger/sources/cron/cron-service.js +6 -3
- package/dist/trigger/sources/cron/cron-service.js.map +1 -1
- package/dist/trigger/trigger-system.d.ts +77 -0
- package/dist/trigger/trigger-system.d.ts.map +1 -0
- package/dist/trigger/trigger-system.js +84 -0
- package/dist/trigger/trigger-system.js.map +1 -0
- package/dist/util/git-safety.d.ts +33 -0
- package/dist/util/git-safety.d.ts.map +1 -0
- package/dist/util/git-safety.js +94 -0
- package/dist/util/git-safety.js.map +1 -0
- package/dist/workspace/dataplane-adapter.d.ts +260 -0
- package/dist/workspace/dataplane-adapter.d.ts.map +1 -0
- package/dist/workspace/dataplane-adapter.js +416 -0
- package/dist/workspace/dataplane-adapter.js.map +1 -0
- package/dist/workspace/index.d.ts +0 -1
- package/dist/workspace/index.d.ts.map +1 -1
- package/dist/workspace/index.js +0 -2
- package/dist/workspace/index.js.map +1 -1
- package/dist/workspace/landing/direct-push.d.ts.map +1 -1
- package/dist/workspace/landing/direct-push.js +22 -7
- package/dist/workspace/landing/direct-push.js.map +1 -1
- package/dist/workspace/landing/queue-to-branch.d.ts.map +1 -1
- package/dist/workspace/landing/queue-to-branch.js +1 -3
- package/dist/workspace/landing/queue-to-branch.js.map +1 -1
- package/dist/workspace/types.d.ts +0 -13
- package/dist/workspace/types.d.ts.map +1 -1
- package/dist/workspace/workspace-manager.d.ts +0 -11
- package/dist/workspace/workspace-manager.d.ts.map +1 -1
- package/dist/workspace/workspace-manager.js +0 -30
- package/dist/workspace/workspace-manager.js.map +1 -1
- package/package.json +28 -2
- package/templates/teams/self-driving/prompts/grinder.md +1 -0
- package/templates/teams/self-driving/prompts/judge.md +1 -0
- package/templates/teams/self-driving/prompts/planner.md +1 -0
- package/templates/teams/self-driving/roles/grinder.yaml +15 -0
- package/templates/teams/self-driving/roles/judge.yaml +10 -0
- package/templates/teams/self-driving/roles/planner.yaml +16 -0
- package/.claude/commands/openspec/apply.md +0 -23
- package/.claude/commands/openspec/archive.md +0 -27
- package/.claude/commands/openspec/proposal.md +0 -28
- package/.claude/settings.json +0 -133
- package/.gitattributes +0 -3
- package/.opentasks/config.json +0 -9
- package/.opentasks/graph.jsonl +0 -0
- package/.sessionlog/settings.json +0 -4
- package/.sudocode/issues.jsonl +0 -183
- package/.sudocode/specs.jsonl +0 -56
- package/AGENTS.md +0 -18
- package/CLAUDE.md +0 -923
- package/docs/architecture.md +0 -508
- package/docs/configuration.md +0 -323
- package/docs/conflict-recovery.md +0 -472
- package/docs/design/task-dispatcher.md +0 -880
- package/docs/design-subsystem-extraction.md +0 -627
- package/docs/git-cascade-integration-gaps.md +0 -678
- package/docs/implementation-details.md +0 -1127
- package/docs/implementation-summary.md +0 -448
- package/docs/lifecycle-events-design.md +0 -111
- package/docs/mail-integration.md +0 -608
- package/docs/openhive-capability-summary.md +0 -415
- package/docs/openhive-integration.md +0 -415
- package/docs/plan-self-driving-support.md +0 -433
- package/docs/roadmap-v2-gaps.md +0 -216
- package/docs/spec-self-driving-support.md +0 -462
- package/docs/team-templates.md +0 -860
- package/docs/teams.md +0 -306
- package/docs/trigger-system-design.md +0 -333
- package/docs/troubleshooting.md +0 -489
- package/docs/workspace-interfaces.md +0 -731
- package/docs/workspace-redesign-plan.md +0 -302
- package/examples/distributed-peer-communication.ts +0 -202
- package/examples/local-peer-communication.ts +0 -165
- package/index.js +0 -2
- package/mvp_docs/agent-execution-system.md +0 -347
- package/mvp_docs/agent-interface.md +0 -580
- package/mvp_docs/data-structures.md +0 -701
- package/mvp_docs/dynamic-meta-orchestration.md +0 -116
- package/mvp_docs/multi-agent-interaction-design.md +0 -779
- package/mvp_docs/mvp-implementation-plan.md +0 -1148
- package/renovate.json5 +0 -6
- package/scripts/test-acp-integration.ts +0 -422
- package/src/__tests__/boot-v2.test.ts +0 -631
- package/src/__tests__/e2e/acp-over-map-live.e2e.test.ts +0 -496
- package/src/__tests__/e2e/acp-over-map.e2e.test.ts +0 -457
- package/src/__tests__/e2e/agent-lifecycle.e2e.test.ts +0 -770
- package/src/__tests__/e2e/auto-sync.e2e.test.ts +0 -257
- package/src/__tests__/e2e/bootstrap.e2e.test.ts +0 -319
- package/src/__tests__/e2e/cascade-rebase.e2e.test.ts +0 -254
- package/src/__tests__/e2e/cc-swarm-bridge.e2e.test.ts +0 -253
- package/src/__tests__/e2e/cli-run.e2e.test.ts +0 -167
- package/src/__tests__/e2e/cognitive-workspace.e2e.test.ts +0 -282
- package/src/__tests__/e2e/dispatch-coordination.e2e.test.ts +0 -495
- package/src/__tests__/e2e/dispatch-live.e2e.test.ts +0 -564
- package/src/__tests__/e2e/dispatch-opentasks.e2e.test.ts +0 -496
- package/src/__tests__/e2e/dispatch-phase2-live.e2e.test.ts +0 -456
- package/src/__tests__/e2e/dispatch-phase2.e2e.test.ts +0 -386
- package/src/__tests__/e2e/dispatch.e2e.test.ts +0 -376
- package/src/__tests__/e2e/done-scenarios.e2e.test.ts +0 -322
- package/src/__tests__/e2e/live-agent.e2e.test.ts +0 -1330
- package/src/__tests__/e2e/map-server.e2e.test.ts +0 -128
- package/src/__tests__/e2e/map-sidecar.e2e.test.ts +0 -139
- package/src/__tests__/e2e/opentasks-integration.e2e.test.ts +0 -280
- package/src/__tests__/e2e/pull-mode.e2e.test.ts +0 -632
- package/src/__tests__/e2e/resume-continue.e2e.test.ts +0 -312
- package/src/__tests__/e2e/self-driving-v3.e2e.test.ts +0 -197
- package/src/__tests__/e2e/spawn-resolver.e2e.test.ts +0 -200
- package/src/__tests__/e2e/swarmkit-integration.e2e.test.ts +0 -562
- package/src/__tests__/e2e/swarmkit-live.e2e.test.ts +0 -1049
- package/src/__tests__/e2e/trajectory-tracking.e2e.test.ts +0 -258
- package/src/__tests__/e2e/trigger-wake.e2e.test.ts +0 -257
- package/src/__tests__/e2e/workspace-lifecycle.e2e.test.ts +0 -383
- package/src/__tests__/e2e/workspace-v3.e2e.test.ts +0 -413
- package/src/acp/__tests__/claude-code-replay.test.ts +0 -225
- package/src/acp/__tests__/macro-agent.test.ts +0 -387
- package/src/acp/__tests__/session-mapper.test.ts +0 -152
- package/src/acp/__tests__/websocket-server.test.ts +0 -194
- package/src/acp/claude-code-replay.ts +0 -208
- package/src/acp/index.ts +0 -32
- package/src/acp/macro-agent.ts +0 -995
- package/src/acp/map-bridge.ts +0 -193
- package/src/acp/session-mapper.ts +0 -116
- package/src/acp/types.ts +0 -99
- package/src/acp/websocket-server.ts +0 -213
- package/src/adapters/__tests__/federation.test.ts +0 -256
- package/src/adapters/__tests__/inbox-adapter.test.ts +0 -316
- package/src/adapters/__tests__/tasks-adapter.test.ts +0 -270
- package/src/adapters/federation.ts +0 -185
- package/src/adapters/inbox-adapter.ts +0 -292
- package/src/adapters/inbox-client-adapter.ts +0 -173
- package/src/adapters/index.ts +0 -44
- package/src/adapters/opentasks-daemon.ts +0 -252
- package/src/adapters/tasks-adapter.ts +0 -330
- package/src/adapters/types.ts +0 -286
- package/src/agent/__tests__/agent-manager-topology.test.ts +0 -73
- package/src/agent/__tests__/agent-manager-v2.completion-signal.test.ts +0 -284
- package/src/agent/__tests__/agent-manager-v2.permission-interception.test.ts +0 -296
- package/src/agent/__tests__/agent-manager-v2.permissions.test.ts +0 -233
- package/src/agent/__tests__/agent-manager-v2.test.ts +0 -614
- package/src/agent/__tests__/agent-store.test.ts +0 -465
- package/src/agent/__tests__/task-ref-resolution.test.ts +0 -231
- package/src/agent/agent-manager-v2.ts +0 -2187
- package/src/agent/agent-manager.ts +0 -403
- package/src/agent/agent-store.ts +0 -409
- package/src/agent/index.ts +0 -7
- package/src/agent/prompts/coordinator-signals.ts +0 -115
- package/src/agent/prompts/index.ts +0 -7
- package/src/agent/system-prompt.ts +0 -298
- package/src/agent/types.ts +0 -480
- package/src/agent-detection/__tests__/command-builder.test.ts +0 -336
- package/src/agent-detection/__tests__/detector.test.ts +0 -768
- package/src/agent-detection/__tests__/registry.test.ts +0 -254
- package/src/agent-detection/command-builder.ts +0 -90
- package/src/agent-detection/detector.ts +0 -307
- package/src/agent-detection/index.ts +0 -36
- package/src/agent-detection/registry.ts +0 -200
- package/src/agent-detection/types.ts +0 -184
- package/src/api/__tests__/server.test.ts +0 -292
- package/src/api/index.ts +0 -8
- package/src/api/server.ts +0 -380
- package/src/api/types.ts +0 -39
- package/src/auth/__tests__/token.test.ts +0 -100
- package/src/auth/index.ts +0 -1
- package/src/auth/token.ts +0 -82
- package/src/boot-v2.ts +0 -1343
- package/src/cli/__tests__/stable-instance-id.test.ts +0 -57
- package/src/cli/acp.ts +0 -101
- package/src/cli/inbox-mcp-proxy.ts +0 -56
- package/src/cli/index.ts +0 -566
- package/src/cli/mcp.ts +0 -305
- package/src/cli/parse-args.ts +0 -54
- package/src/cli/stable-instance-id.ts +0 -14
- package/src/cognitive/__tests__/macro-agent-backend.test.ts +0 -673
- package/src/cognitive/__tests__/session-converter.test.ts +0 -423
- package/src/cognitive/__tests__/workspace-handler.test.ts +0 -221
- package/src/cognitive/analyst-role.ts +0 -59
- package/src/cognitive/index.ts +0 -49
- package/src/cognitive/macro-agent-backend.ts +0 -402
- package/src/cognitive/session-converter.ts +0 -151
- package/src/cognitive/types.ts +0 -187
- package/src/cognitive/workspace-handler.ts +0 -163
- package/src/config/index.ts +0 -9
- package/src/config/project-config.ts +0 -294
- package/src/control/__tests__/control-resilience.test.ts +0 -251
- package/src/control/__tests__/control-socket.test.ts +0 -240
- package/src/control/control-client.ts +0 -337
- package/src/control/control-server.ts +0 -298
- package/src/control/index.ts +0 -17
- package/src/control/types.ts +0 -95
- package/src/dispatch/CLAUDE.md +0 -129
- package/src/dispatch/__tests__/loadout-translation.test.ts +0 -141
- package/src/dispatch/__tests__/mail-inbound-consumer.integration.test.ts +0 -519
- package/src/dispatch/__tests__/mail-inbound-consumer.test.ts +0 -800
- package/src/dispatch/__tests__/mail-inbound-reuse-consumer.test.ts +0 -575
- package/src/dispatch/__tests__/permission-evaluator.test.ts +0 -196
- package/src/dispatch/__tests__/permission-overlay.test.ts +0 -56
- package/src/dispatch/__tests__/permissions-handler.test.ts +0 -168
- package/src/dispatch/__tests__/spawn-agent-handler.test.ts +0 -282
- package/src/dispatch/loadout-translation.ts +0 -138
- package/src/dispatch/mail-inbound-consumer.ts +0 -560
- package/src/dispatch/mail-inbound-reuse-consumer.ts +0 -479
- package/src/dispatch/permission-evaluator.ts +0 -191
- package/src/dispatch/permission-overlay.ts +0 -89
- package/src/dispatch/permissions-handler.ts +0 -112
- package/src/dispatch/spawn-agent-handler.ts +0 -160
- package/src/index.ts +0 -117
- package/src/integrations/__tests__/context-builder.test.ts +0 -218
- package/src/integrations/__tests__/sessionlog.test.ts +0 -498
- package/src/integrations/__tests__/skilltree.test.ts +0 -136
- package/src/integrations/context-builder.ts +0 -280
- package/src/integrations/sessionlog.ts +0 -194
- package/src/integrations/skilltree.ts +0 -184
- package/src/lifecycle/__tests__/cascade-consolidation.test.ts +0 -240
- package/src/lifecycle/__tests__/handlers-v2.test.ts +0 -565
- package/src/lifecycle/cascade.ts +0 -335
- package/src/lifecycle/cleanup.ts +0 -373
- package/src/lifecycle/handlers-v2.ts +0 -508
- package/src/lifecycle/index.ts +0 -76
- package/src/lifecycle/types.ts +0 -260
- package/src/map/__tests__/cascade-bridge.test.ts +0 -229
- package/src/map/__tests__/cascade-diff-server.test.ts +0 -434
- package/src/map/__tests__/emit-event.test.ts +0 -71
- package/src/map/__tests__/lifecycle-bridge.test.ts +0 -430
- package/src/map/__tests__/mail-bridge.test.ts +0 -321
- package/src/map/__tests__/permission-forwarding.test.ts +0 -143
- package/src/map/__tests__/sidecar-diff-install-smoke.test.ts +0 -90
- package/src/map/__tests__/sidecar-integration.test.ts +0 -190
- package/src/map/__tests__/task-bridge.test.ts +0 -153
- package/src/map/__tests__/trajectory-reporter.test.ts +0 -173
- package/src/map/acp-bridge.ts +0 -293
- package/src/map/cascade-action-handler.ts +0 -205
- package/src/map/cascade-bridge.ts +0 -339
- package/src/map/cascade-diff-server.ts +0 -404
- package/src/map/cc-swarm-hooks.ts +0 -242
- package/src/map/coordination-handler.ts +0 -232
- package/src/map/index.ts +0 -18
- package/src/map/lifecycle-bridge.ts +0 -236
- package/src/map/mail-bridge.ts +0 -263
- package/src/map/repo-workspace.ts +0 -82
- package/src/map/server.ts +0 -758
- package/src/map/sidecar.ts +0 -826
- package/src/map/task-bridge.ts +0 -89
- package/src/map/trajectory-reporter.ts +0 -124
- package/src/map/types.ts +0 -304
- package/src/mcp/__tests__/mcp-server-v2.test.ts +0 -236
- package/src/mcp/index.ts +0 -6
- package/src/mcp/mcp-server-v2.ts +0 -485
- package/src/mcp/tools/done-v2.ts +0 -213
- package/src/mcp/types.ts +0 -432
- package/src/metrics/__tests__/metrics.test.ts +0 -205
- package/src/metrics/index.ts +0 -13
- package/src/metrics/metrics.ts +0 -151
- package/src/metrics/types.ts +0 -63
- package/src/roles/__tests__/config-loader.test.ts +0 -540
- package/src/roles/builtin/coordinator.ts +0 -75
- package/src/roles/builtin/generic.ts +0 -39
- package/src/roles/builtin/index.ts +0 -51
- package/src/roles/builtin/integrator.ts +0 -62
- package/src/roles/builtin/monitor.ts +0 -65
- package/src/roles/builtin/worker.ts +0 -134
- package/src/roles/capabilities.ts +0 -257
- package/src/roles/config-loader.ts +0 -405
- package/src/roles/index.ts +0 -141
- package/src/roles/registry.ts +0 -713
- package/src/roles/types.ts +0 -344
- package/src/store/index.ts +0 -8
- package/src/store/types/agents.ts +0 -69
- package/src/store/types/events.ts +0 -91
- package/src/store/types/index.ts +0 -9
- package/src/store/types/messages.ts +0 -32
- package/src/store/types/primitives.ts +0 -9
- package/src/store/types/tasks.ts +0 -124
- package/src/teams/__tests__/team-manager-v2.test.ts +0 -425
- package/src/teams/__tests__/team-runtime-v2.test.ts +0 -402
- package/src/teams/index.ts +0 -29
- package/src/teams/seed-defaults.ts +0 -83
- package/src/teams/team-loader.ts +0 -476
- package/src/teams/team-manager-v2.ts +0 -343
- package/src/teams/team-runtime-v2.ts +0 -943
- package/src/teams/types.ts +0 -276
- package/src/trigger/__tests__/cron-service.test.ts +0 -548
- package/src/trigger/__tests__/scheduler.test.ts +0 -371
- package/src/trigger/__tests__/system-event-queue.test.ts +0 -147
- package/src/trigger/__tests__/trigger-system-v2.test.ts +0 -257
- package/src/trigger/__tests__/wake-manager.test.ts +0 -356
- package/src/trigger/__tests__/webhook-handler.test.ts +0 -567
- package/src/trigger/index.ts +0 -110
- package/src/trigger/queue/index.ts +0 -21
- package/src/trigger/queue/system-event-queue.ts +0 -232
- package/src/trigger/queue/types.ts +0 -149
- package/src/trigger/sources/cron/cron-service.ts +0 -499
- package/src/trigger/sources/cron/index.ts +0 -41
- package/src/trigger/sources/cron/scheduler.ts +0 -344
- package/src/trigger/sources/cron/types.ts +0 -333
- package/src/trigger/sources/index.ts +0 -10
- package/src/trigger/sources/webhook/index.ts +0 -25
- package/src/trigger/sources/webhook/types.ts +0 -205
- package/src/trigger/sources/webhook/webhook-handler.ts +0 -410
- package/src/trigger/strategies/ai-router.ts +0 -170
- package/src/trigger/trigger-system-v2.ts +0 -536
- package/src/trigger/types.ts +0 -228
- package/src/trigger/wake/index.ts +0 -22
- package/src/trigger/wake/types.ts +0 -162
- package/src/trigger/wake/wake-manager.ts +0 -390
- package/src/workspace/__tests__/git-cascade-adapter.test.ts +0 -563
- package/src/workspace/__tests__/land-dispatch.test.ts +0 -214
- package/src/workspace/__tests__/self-driving-yaml.test.ts +0 -122
- package/src/workspace/__tests__/shared-worktree-refcount.test.ts +0 -154
- package/src/workspace/__tests__/standalone-mode.test.ts +0 -118
- package/src/workspace/__tests__/workspace-manager-v3.test.ts +0 -245
- package/src/workspace/__tests__/yaml-schema.test.ts +0 -210
- package/src/workspace/config.ts +0 -165
- package/src/workspace/git-cascade-adapter.ts +0 -1213
- package/src/workspace/index.ts +0 -110
- package/src/workspace/landing/__tests__/strategies.test.ts +0 -184
- package/src/workspace/landing/direct-push.ts +0 -91
- package/src/workspace/landing/index.ts +0 -40
- package/src/workspace/landing/merge-to-parent.ts +0 -229
- package/src/workspace/landing/optimistic-push.ts +0 -36
- package/src/workspace/landing/queue-to-branch.ts +0 -108
- package/src/workspace/merge-queue/__tests__/conflict-resolution-git.e2e.test.ts +0 -641
- package/src/workspace/merge-queue/__tests__/conflict-resolution.test.ts +0 -707
- package/src/workspace/merge-queue/__tests__/merge-queue-concurrent.test.ts +0 -628
- package/src/workspace/merge-queue/__tests__/merge-queue-edge-cases.test.ts +0 -1217
- package/src/workspace/merge-queue/__tests__/merge-queue.test.ts +0 -506
- package/src/workspace/merge-queue/__tests__/real-git-operations.e2e.test.ts +0 -556
- package/src/workspace/merge-queue/__tests__/resolver-flow.test.ts +0 -472
- package/src/workspace/merge-queue/index.ts +0 -38
- package/src/workspace/merge-queue/merge-queue.ts +0 -585
- package/src/workspace/merge-queue/schema.ts +0 -98
- package/src/workspace/merge-queue/types.ts +0 -316
- package/src/workspace/pool/__tests__/worktree-pool.integration.test.ts +0 -1281
- package/src/workspace/pool/__tests__/worktree-pool.test.ts +0 -590
- package/src/workspace/pool/index.ts +0 -26
- package/src/workspace/pool/types.ts +0 -276
- package/src/workspace/pool/worktree-pool.ts +0 -659
- package/src/workspace/recovery/__tests__/auto-resolve-integration.test.ts +0 -127
- package/src/workspace/recovery/__tests__/spawn-resolver.test.ts +0 -139
- package/src/workspace/recovery/__tests__/strategies.test.ts +0 -145
- package/src/workspace/recovery/abandon.ts +0 -51
- package/src/workspace/recovery/auto-resolve.ts +0 -119
- package/src/workspace/recovery/defer.ts +0 -23
- package/src/workspace/recovery/escalate.ts +0 -30
- package/src/workspace/recovery/index.ts +0 -58
- package/src/workspace/recovery/spawn-resolver.ts +0 -152
- package/src/workspace/recovery/types.ts +0 -54
- package/src/workspace/strategies/index.ts +0 -18
- package/src/workspace/strategies/optimistic.ts +0 -139
- package/src/workspace/strategies/queue.ts +0 -81
- package/src/workspace/strategies/registry.ts +0 -89
- package/src/workspace/strategies/trunk.ts +0 -123
- package/src/workspace/strategies/types.ts +0 -145
- package/src/workspace/topology/__tests__/yaml-driven.test.ts +0 -345
- package/src/workspace/topology/index.ts +0 -18
- package/src/workspace/topology/no-workspace.ts +0 -39
- package/src/workspace/topology/types.ts +0 -116
- package/src/workspace/topology/yaml-driven.ts +0 -316
- package/src/workspace/types-v3.ts +0 -162
- package/src/workspace/types.ts +0 -541
- package/src/workspace/workspace-manager.ts +0 -1577
- package/src/workspace/yaml-schema.ts +0 -216
- package/test_fixtures/README.md +0 -425
- package/test_fixtures/fixtures/behaviors/coordinators.ts +0 -196
- package/test_fixtures/fixtures/behaviors/index.ts +0 -62
- package/test_fixtures/fixtures/behaviors/integrators.ts +0 -143
- package/test_fixtures/fixtures/behaviors/monitors.ts +0 -199
- package/test_fixtures/fixtures/behaviors/workers.ts +0 -348
- package/test_fixtures/fixtures/index.ts +0 -7
- package/test_fixtures/fixtures/projects/index.ts +0 -23
- package/test_fixtures/fixtures/projects/project-with-specs.ts +0 -44
- package/test_fixtures/fixtures/projects/typescript-project.ts +0 -200
- package/test_fixtures/fixtures/repos/index.ts +0 -6
- package/test_fixtures/fixtures/repos/temp-repo-factory.ts +0 -299
- package/test_fixtures/fixtures/repos/types.ts +0 -174
- package/test_fixtures/harness/assertions/harness-assertions.ts +0 -752
- package/test_fixtures/harness/assertions/index.ts +0 -8
- package/test_fixtures/harness/index.ts +0 -15
- package/test_fixtures/harness/simulator/agent-simulator.ts +0 -768
- package/test_fixtures/harness/simulator/behavior-executor.ts +0 -580
- package/test_fixtures/harness/simulator/index.ts +0 -14
- package/test_fixtures/harness/simulator/types.ts +0 -459
- package/test_fixtures/harness/test-harness.ts +0 -770
- package/test_fixtures/harness/timing/event-stepper.ts +0 -344
- package/test_fixtures/harness/timing/index.ts +0 -8
- package/tsconfig.json +0 -21
- package/vitest.config.ts +0 -30
- package/vitest.e2e.config.ts +0 -28
- package/vitest.setup.ts +0 -12
|
@@ -1,2187 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AgentManager V2 — Uses AgentStore + InboxAdapter + TasksAdapter
|
|
3
|
-
*
|
|
4
|
-
* Replaces EventStore/MessageRouter dependencies with the adapter layer.
|
|
5
|
-
* Messages flow through agent-inbox, tasks through opentasks,
|
|
6
|
-
* agent lifecycle state in AgentStore (minimal SQLite).
|
|
7
|
-
*
|
|
8
|
-
* @module agent/agent-manager-v2
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { nanoid } from "nanoid";
|
|
12
|
-
import {
|
|
13
|
-
uniqueNamesGenerator,
|
|
14
|
-
adjectives,
|
|
15
|
-
animals,
|
|
16
|
-
} from "unique-names-generator";
|
|
17
|
-
import {
|
|
18
|
-
AgentFactory,
|
|
19
|
-
type Session,
|
|
20
|
-
type AgentHandle,
|
|
21
|
-
type ExtendedSessionUpdate,
|
|
22
|
-
type PermissionMode,
|
|
23
|
-
} from "acp-factory";
|
|
24
|
-
import {
|
|
25
|
-
AgentStore,
|
|
26
|
-
type AgentRecord,
|
|
27
|
-
type SessionRecord,
|
|
28
|
-
} from "./agent-store.js";
|
|
29
|
-
import type {
|
|
30
|
-
AgentId,
|
|
31
|
-
TaskId,
|
|
32
|
-
Timestamp,
|
|
33
|
-
Agent,
|
|
34
|
-
AgentState,
|
|
35
|
-
} from "../store/types/index.js";
|
|
36
|
-
import type {
|
|
37
|
-
SpawnAgentOptions,
|
|
38
|
-
SpawnedAgent,
|
|
39
|
-
AgentFilter,
|
|
40
|
-
AgentHierarchy,
|
|
41
|
-
AgentHierarchyNode,
|
|
42
|
-
HierarchyOptions,
|
|
43
|
-
ActiveSession,
|
|
44
|
-
AgentStopReason,
|
|
45
|
-
HeadManagerOptions,
|
|
46
|
-
SystemPromptContext,
|
|
47
|
-
AgentLifecycleCallback,
|
|
48
|
-
AgentLifecycleEvent,
|
|
49
|
-
AgentConfig,
|
|
50
|
-
ContinueAgentOptions,
|
|
51
|
-
MCPServerStdioConfig,
|
|
52
|
-
} from "./types.js";
|
|
53
|
-
import { AgentManagerError } from "./types.js";
|
|
54
|
-
import type { RoleRegistry, Capability } from "../roles/types.js";
|
|
55
|
-
import { AGENT_CAPABILITIES } from "../roles/capabilities.js";
|
|
56
|
-
import { DefaultRoleRegistry } from "../roles/registry.js";
|
|
57
|
-
import { generateSystemPrompt } from "./system-prompt.js";
|
|
58
|
-
import type { WorkspaceManager, Workspace } from "../workspace/types.js";
|
|
59
|
-
import {
|
|
60
|
-
terminateWithChangeConsolidation,
|
|
61
|
-
type WorkspaceProvider,
|
|
62
|
-
type CascadeAgentManager,
|
|
63
|
-
} from "../lifecycle/cascade.js";
|
|
64
|
-
import { AgentTokenManager } from "../auth/token.js";
|
|
65
|
-
import type { InboxAdapter } from "../adapters/types.js";
|
|
66
|
-
import type { TasksAdapter } from "../adapters/types.js";
|
|
67
|
-
import type { AgentManager, SpawnInterceptor } from "./agent-manager.js";
|
|
68
|
-
import { getPermissionOverlay } from "../dispatch/permission-overlay.js";
|
|
69
|
-
import { evaluatePermission } from "../dispatch/permission-evaluator.js";
|
|
70
|
-
|
|
71
|
-
// ─────────────────────────────────────────────────────────────────
|
|
72
|
-
// Helper
|
|
73
|
-
// ─────────────────────────────────────────────────────────────────
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Derive the canonical Claude Code tool name from a `permission_request`
|
|
77
|
-
* `toolCall` object.
|
|
78
|
-
*
|
|
79
|
-
* Why this is needed: claude-agent-acp's `toolInfoFromToolUse` mangles
|
|
80
|
-
* built-in tool names into display titles (e.g., `Read /tmp/x` instead of
|
|
81
|
-
* `Read`). MCP tools use their fully-qualified `mcp__server__tool` name as
|
|
82
|
-
* the title. The `kind` field (Claude SDK's tool category) is the
|
|
83
|
-
* cleanest signal for built-ins, with the input shape disambiguating
|
|
84
|
-
* within a category (e.g., `edit` covers Write/Edit/MultiEdit — we look
|
|
85
|
-
* at `old_string`/`edits` to pick which).
|
|
86
|
-
*
|
|
87
|
-
* Pure: no side effects; safe to call from the prompt iterator.
|
|
88
|
-
*/
|
|
89
|
-
function deriveToolName(toolCall: {
|
|
90
|
-
title?: string;
|
|
91
|
-
kind?: string;
|
|
92
|
-
rawInput?: unknown;
|
|
93
|
-
} | undefined): string {
|
|
94
|
-
const title = toolCall?.title ?? "";
|
|
95
|
-
const kind = toolCall?.kind ?? "";
|
|
96
|
-
// MCP tools — title is the canonical name.
|
|
97
|
-
if (title.startsWith("mcp__")) return title.split(/\s/)[0] ?? "";
|
|
98
|
-
const input =
|
|
99
|
-
toolCall?.rawInput && typeof toolCall.rawInput === "object"
|
|
100
|
-
? (toolCall.rawInput as Record<string, unknown>)
|
|
101
|
-
: {};
|
|
102
|
-
switch (kind) {
|
|
103
|
-
case "read":
|
|
104
|
-
return "Read";
|
|
105
|
-
case "execute":
|
|
106
|
-
return "Bash";
|
|
107
|
-
case "edit":
|
|
108
|
-
if ("edits" in input) return "MultiEdit";
|
|
109
|
-
if ("old_string" in input) return "Edit";
|
|
110
|
-
return "Write";
|
|
111
|
-
case "search":
|
|
112
|
-
return "path" in input ? "Grep" : "Glob";
|
|
113
|
-
case "think":
|
|
114
|
-
return "Task";
|
|
115
|
-
case "switch_mode":
|
|
116
|
-
return "ExitPlanMode";
|
|
117
|
-
}
|
|
118
|
-
// Fallback: first whitespace-delimited token of the title (Read/Write/
|
|
119
|
-
// Edit cases not caught above; built-ins newer than this matrix).
|
|
120
|
-
return title.split(/\s/)[0] ?? "";
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function getSpawnCapability(childRole: string): Capability {
|
|
124
|
-
const baseRole = childRole.split(".")[0];
|
|
125
|
-
switch (baseRole) {
|
|
126
|
-
case "worker":
|
|
127
|
-
return AGENT_CAPABILITIES.SPAWN_WORKER;
|
|
128
|
-
case "integrator":
|
|
129
|
-
return AGENT_CAPABILITIES.SPAWN_INTEGRATOR;
|
|
130
|
-
case "monitor":
|
|
131
|
-
return AGENT_CAPABILITIES.SPAWN_MONITOR;
|
|
132
|
-
case "coordinator":
|
|
133
|
-
return AGENT_CAPABILITIES.SPAWN_CUSTOM;
|
|
134
|
-
default:
|
|
135
|
-
return `agent.spawn.${childRole}` as Capability;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function generateName(): string {
|
|
140
|
-
return uniqueNamesGenerator({
|
|
141
|
-
dictionaries: [adjectives, animals],
|
|
142
|
-
separator: "-",
|
|
143
|
-
length: 2,
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// ─────────────────────────────────────────────────────────────────
|
|
148
|
-
// Configuration
|
|
149
|
-
// ─────────────────────────────────────────────────────────────────
|
|
150
|
-
|
|
151
|
-
/** Minimal health check interface (monitor module removed in V2) */
|
|
152
|
-
interface HealthCheckService {
|
|
153
|
-
startForCoordinator(agentId: string): void;
|
|
154
|
-
stopForCoordinator(agentId: string): void;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export interface AgentManagerV2Config {
|
|
158
|
-
defaultPermissionMode?: PermissionMode;
|
|
159
|
-
defaultAgentType?: string;
|
|
160
|
-
defaultCwd?: string;
|
|
161
|
-
workspaceManager?: WorkspaceManager;
|
|
162
|
-
roleRegistry?: RoleRegistry;
|
|
163
|
-
healthCheckService?: HealthCheckService;
|
|
164
|
-
agentTokenManager?: AgentTokenManager;
|
|
165
|
-
serverUrl?: string;
|
|
166
|
-
serverToken?: string;
|
|
167
|
-
/** Control socket path for MCP subprocess lifecycle RPC */
|
|
168
|
-
controlSocketPath?: string;
|
|
169
|
-
/**
|
|
170
|
-
* Default opentasks resource ID hosted on the OpenHive hub. When set,
|
|
171
|
-
* spawn paths build `taskRef = { resource_id: <this>, node_id: task_id }`
|
|
172
|
-
* automatically from `SpawnAgentOptions.task_id` (for any spawn where
|
|
173
|
-
* `resolveTaskRef` returned undefined AND the caller didn't supply an
|
|
174
|
-
* explicit `taskRef`).
|
|
175
|
-
*
|
|
176
|
-
* Operators set this once at swarm registration for the common
|
|
177
|
-
* single-graph case. Multi-graph deployments should use `resolveTaskRef`
|
|
178
|
-
* instead.
|
|
179
|
-
*/
|
|
180
|
-
taskResourceId?: string;
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Multi-graph resolver. Called at every spawn; return a `TaskRef` to set
|
|
184
|
-
* the binding or `undefined` to fall through to the `taskResourceId`
|
|
185
|
-
* default. Explicit `SpawnAgentOptions.taskRef` always wins over both.
|
|
186
|
-
*
|
|
187
|
-
* Keep cheap — runs per-spawn.
|
|
188
|
-
*/
|
|
189
|
-
resolveTaskRef?: (
|
|
190
|
-
spawnOptions: SpawnAgentOptions
|
|
191
|
-
) => import("git-cascade/events").TaskRef | undefined;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// ─────────────────────────────────────────────────────────────────
|
|
195
|
-
// Factory
|
|
196
|
-
// ─────────────────────────────────────────────────────────────────
|
|
197
|
-
|
|
198
|
-
export function createAgentManagerV2(
|
|
199
|
-
agentStore: AgentStore,
|
|
200
|
-
inboxAdapter: InboxAdapter,
|
|
201
|
-
tasksAdapter: TasksAdapter,
|
|
202
|
-
config: AgentManagerV2Config = {}
|
|
203
|
-
): AgentManager {
|
|
204
|
-
const {
|
|
205
|
-
defaultPermissionMode = "auto-approve",
|
|
206
|
-
defaultAgentType = "claude-code",
|
|
207
|
-
defaultCwd = process.cwd(),
|
|
208
|
-
workspaceManager,
|
|
209
|
-
roleRegistry = new DefaultRoleRegistry(),
|
|
210
|
-
healthCheckService,
|
|
211
|
-
serverUrl,
|
|
212
|
-
serverToken,
|
|
213
|
-
agentTokenManager,
|
|
214
|
-
controlSocketPath,
|
|
215
|
-
taskResourceId,
|
|
216
|
-
resolveTaskRef,
|
|
217
|
-
} = config;
|
|
218
|
-
|
|
219
|
-
// In-memory state
|
|
220
|
-
const activeSessions = new Map<AgentId, ActiveSession>();
|
|
221
|
-
const agentWorkspaces = new Map<AgentId, Workspace>();
|
|
222
|
-
const lifecycleListeners = new Set<AgentLifecycleCallback>();
|
|
223
|
-
// In-flight fire-and-forget terminate() promises (e.g. the promptUntilDone
|
|
224
|
-
// completion poller tears the agent down without awaiting it, to keep
|
|
225
|
-
// completion latency low). close() awaits these so shutdown cannot return
|
|
226
|
-
// while a group-kill is still racing — which would let process.exit orphan
|
|
227
|
-
// the subtree.
|
|
228
|
-
const pendingTerminations = new Set<Promise<unknown>>();
|
|
229
|
-
let spawnInterceptor: SpawnInterceptor | null = null;
|
|
230
|
-
let isShuttingDown = false;
|
|
231
|
-
let mapServerUrl: string | undefined;
|
|
232
|
-
|
|
233
|
-
// Swarmkit integration configs (set via late-binding setters from boot-v2)
|
|
234
|
-
let minimemConfig: { enabled: boolean; dir?: string; provider?: string; global?: boolean } | undefined;
|
|
235
|
-
let skilltreeConfig: { enabled: boolean; basePath?: string; defaultProfile?: string } | undefined;
|
|
236
|
-
let sessionlogConfig: { enabled: boolean; sync?: string } | undefined;
|
|
237
|
-
// Compiled skill loadouts per role (populated by team runtime)
|
|
238
|
-
const skillLoadouts = new Map<string, string>();
|
|
239
|
-
// MAP sidecar reference for trajectory reporting (set via setSidecar)
|
|
240
|
-
let sidecarRef: { connected: boolean; reportCheckpoint(cp: any): Promise<any> } | null = null;
|
|
241
|
-
|
|
242
|
-
// TopologyPolicy for workspace allocation (Phase 3+); set via setTopologyPolicy.
|
|
243
|
-
// When null, createWorkspaceForRole falls back to legacy role-name dispatch.
|
|
244
|
-
let topologyPolicy:
|
|
245
|
-
| import('../workspace/topology/types.js').TopologyPolicy
|
|
246
|
-
| null = null;
|
|
247
|
-
|
|
248
|
-
// ── Helpers ──────────────────────────────────────────────────
|
|
249
|
-
|
|
250
|
-
function notifyLifecycle(event: AgentLifecycleEvent): void {
|
|
251
|
-
for (const listener of lifecycleListeners) {
|
|
252
|
-
try {
|
|
253
|
-
listener(event);
|
|
254
|
-
} catch {
|
|
255
|
-
// Ignore listener errors
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
function agentRecordToAgent(record: AgentRecord): Agent {
|
|
261
|
-
const session = agentStore.getSession(record.id as AgentId);
|
|
262
|
-
return {
|
|
263
|
-
id: record.id,
|
|
264
|
-
name: record.name,
|
|
265
|
-
session_id: session?.session_id ?? "",
|
|
266
|
-
parent: record.parent_id,
|
|
267
|
-
lineage: record.lineage,
|
|
268
|
-
state: record.state,
|
|
269
|
-
stop_reason: record.stop_reason as Agent["stop_reason"],
|
|
270
|
-
task: record.task,
|
|
271
|
-
task_id: record.task_id,
|
|
272
|
-
role: record.role,
|
|
273
|
-
team_instance: record.team,
|
|
274
|
-
config: (record.config as Agent["config"]) ?? {},
|
|
275
|
-
cwd: record.cwd,
|
|
276
|
-
plan: [],
|
|
277
|
-
metadata: record.metadata,
|
|
278
|
-
created_at: record.created_at,
|
|
279
|
-
started_at: record.started_at,
|
|
280
|
-
stopped_at: record.stopped_at,
|
|
281
|
-
last_activity_at: record.last_activity_at,
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
function buildMcpServerConfig(opts: {
|
|
286
|
-
agentId: string;
|
|
287
|
-
parentId: string;
|
|
288
|
-
taskId: string;
|
|
289
|
-
cwd: string;
|
|
290
|
-
permissionMode: string;
|
|
291
|
-
lineage?: string[];
|
|
292
|
-
sessionId?: string;
|
|
293
|
-
streamId?: string;
|
|
294
|
-
}): MCPServerStdioConfig {
|
|
295
|
-
const env: Record<string, string> = {
|
|
296
|
-
MACRO_AGENT_ID: opts.agentId,
|
|
297
|
-
MACRO_PARENT_ID: opts.parentId,
|
|
298
|
-
MACRO_TASK_ID: opts.taskId,
|
|
299
|
-
MACRO_AGENT_CWD: opts.cwd,
|
|
300
|
-
MACRO_PERMISSION_MODE: opts.permissionMode,
|
|
301
|
-
MACRO_STREAM_ID: opts.streamId ?? "",
|
|
302
|
-
// Point to inbox socket for agent-inbox MCP tools
|
|
303
|
-
INBOX_SOCKET_PATH: inboxAdapter.socketPath,
|
|
304
|
-
// Control socket for lifecycle RPC (spawn, terminate, etc.)
|
|
305
|
-
MACRO_CONTROL_SOCKET_PATH: controlSocketPath ?? "",
|
|
306
|
-
// Base directory for AgentStore + inbox in MCP subprocess
|
|
307
|
-
MACRO_BASE_DIR: controlSocketPath
|
|
308
|
-
? controlSocketPath.replace(/\/control\.sock$/, "")
|
|
309
|
-
: "",
|
|
310
|
-
// opentasks client auto-discovers its socket
|
|
311
|
-
};
|
|
312
|
-
|
|
313
|
-
if (serverUrl) {
|
|
314
|
-
env.MACRO_SERVER_URL = serverUrl;
|
|
315
|
-
env.MACRO_AGENT_LINEAGE = JSON.stringify(opts.lineage ?? []);
|
|
316
|
-
env.MACRO_SESSION_ID = opts.sessionId ?? "";
|
|
317
|
-
if (serverToken) env.MACRO_SERVER_TOKEN = serverToken;
|
|
318
|
-
if (agentTokenManager) {
|
|
319
|
-
env.MACRO_AGENT_TOKEN = agentTokenManager.createToken(opts.agentId);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// Use node with local dist path to ensure we run V2 MCP server,
|
|
324
|
-
// not a globally installed V1 version.
|
|
325
|
-
const mcpEntryPoint = new URL("../../dist/cli/mcp.js", import.meta.url).pathname;
|
|
326
|
-
|
|
327
|
-
return {
|
|
328
|
-
name: "macro-agent",
|
|
329
|
-
command: "node",
|
|
330
|
-
args: [mcpEntryPoint],
|
|
331
|
-
env,
|
|
332
|
-
};
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// ── Workspace Helper ─────────────────────────────────────────
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* Execute a TopologyPolicy decision against the WorkspaceManager.
|
|
339
|
-
*
|
|
340
|
-
* Translates declarative `WorkspaceDecision` into concrete workspace
|
|
341
|
-
* allocations. Returns a `Workspace` compatible with the legacy shape
|
|
342
|
-
* so the rest of AgentManagerV2 doesn't need to change.
|
|
343
|
-
*/
|
|
344
|
-
async function executeWorkspaceDecision(
|
|
345
|
-
agentId: AgentId,
|
|
346
|
-
decision: import('../workspace/topology/types.js').WorkspaceDecision,
|
|
347
|
-
role?: string,
|
|
348
|
-
spawnOptions?: SpawnAgentOptions
|
|
349
|
-
): Promise<Workspace | undefined> {
|
|
350
|
-
if (!workspaceManager) return undefined;
|
|
351
|
-
|
|
352
|
-
switch (decision.kind) {
|
|
353
|
-
case 'none':
|
|
354
|
-
case 'share-parent-cwd':
|
|
355
|
-
return undefined;
|
|
356
|
-
|
|
357
|
-
case 'share-with-agent': {
|
|
358
|
-
const worktree = workspaceManager.allocateWorktree({
|
|
359
|
-
agentId,
|
|
360
|
-
sharedWithAgent: decision.agentId,
|
|
361
|
-
});
|
|
362
|
-
return {
|
|
363
|
-
agentId,
|
|
364
|
-
path: worktree.path,
|
|
365
|
-
branch: worktree.currentStream
|
|
366
|
-
? `stream/${worktree.currentStream}`
|
|
367
|
-
: 'unknown',
|
|
368
|
-
streamId: worktree.currentStream ?? '',
|
|
369
|
-
role: 'v3', // V3 path — bypass legacy worker task/merge-queue flows
|
|
370
|
-
createdAt: worktree.createdAt,
|
|
371
|
-
};
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
case 'attach-to-stream': {
|
|
375
|
-
// Record even if no worktree — the topology needs the stream↔role
|
|
376
|
-
// mapping for event-driven features like on_parent_advanced.
|
|
377
|
-
const attachPolicy = topologyPolicy as unknown as {
|
|
378
|
-
recordAgentStream?: (a: string, s: string, role?: string) => void;
|
|
379
|
-
};
|
|
380
|
-
attachPolicy.recordAgentStream?.(agentId, decision.streamId, role);
|
|
381
|
-
|
|
382
|
-
if (!decision.allocateWorktree) {
|
|
383
|
-
return undefined;
|
|
384
|
-
}
|
|
385
|
-
const worktree = workspaceManager.allocateWorktree({
|
|
386
|
-
agentId,
|
|
387
|
-
streamId: decision.streamId,
|
|
388
|
-
});
|
|
389
|
-
return {
|
|
390
|
-
agentId,
|
|
391
|
-
path: worktree.path,
|
|
392
|
-
branch: `stream/${decision.streamId}`,
|
|
393
|
-
streamId: decision.streamId,
|
|
394
|
-
role: 'v3', // V3 path — attach-to-team-root
|
|
395
|
-
createdAt: worktree.createdAt,
|
|
396
|
-
};
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
case 'new-stream': {
|
|
400
|
-
// If the spawning agent has a taskRef and the streamSpec doesn't
|
|
401
|
-
// already carry one, weave it into metadata so the resulting stream
|
|
402
|
-
// binds to the OpenTasks node. Explicit streamSpec.metadata.task_ref
|
|
403
|
-
// wins.
|
|
404
|
-
const taskRef = spawnOptions?.taskRef;
|
|
405
|
-
const existingMeta = decision.streamSpec.metadata as
|
|
406
|
-
| Record<string, unknown>
|
|
407
|
-
| undefined;
|
|
408
|
-
const streamSpec = taskRef && !existingMeta?.task_ref
|
|
409
|
-
? {
|
|
410
|
-
...decision.streamSpec,
|
|
411
|
-
metadata: { ...(existingMeta ?? {}), task_ref: taskRef },
|
|
412
|
-
}
|
|
413
|
-
: decision.streamSpec;
|
|
414
|
-
const streamId = workspaceManager.createStreamV3(streamSpec);
|
|
415
|
-
// Record the mapping in the topology if it supports it (for share-with lookup).
|
|
416
|
-
const policy = topologyPolicy as unknown as {
|
|
417
|
-
recordAgentStream?: (a: string, s: string, role?: string) => void;
|
|
418
|
-
};
|
|
419
|
-
policy.recordAgentStream?.(agentId, streamId, role);
|
|
420
|
-
|
|
421
|
-
if (!decision.allocateWorktree) {
|
|
422
|
-
return undefined;
|
|
423
|
-
}
|
|
424
|
-
const worktree = workspaceManager.allocateWorktree({
|
|
425
|
-
agentId,
|
|
426
|
-
streamId,
|
|
427
|
-
});
|
|
428
|
-
return {
|
|
429
|
-
agentId,
|
|
430
|
-
path: worktree.path,
|
|
431
|
-
branch: `stream/${streamId}`,
|
|
432
|
-
streamId,
|
|
433
|
-
role: 'v3', // V3 path — new-stream
|
|
434
|
-
createdAt: worktree.createdAt,
|
|
435
|
-
};
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
async function createWorkspaceForRole(
|
|
441
|
-
agentId: AgentId,
|
|
442
|
-
role: string,
|
|
443
|
-
options: SpawnAgentOptions
|
|
444
|
-
): Promise<Workspace | undefined> {
|
|
445
|
-
if (!workspaceManager) return undefined;
|
|
446
|
-
|
|
447
|
-
// V3 path — TopologyPolicy-driven. Set by boot-v2 when team YAML has
|
|
448
|
-
// `macro_agent.workspace`. When set, this takes precedence over the legacy
|
|
449
|
-
// capability/role-name dispatch below.
|
|
450
|
-
if (topologyPolicy) {
|
|
451
|
-
const decision = await topologyPolicy.onAgentSpawn({
|
|
452
|
-
agentId,
|
|
453
|
-
role,
|
|
454
|
-
parentAgentId: options.parent ?? undefined,
|
|
455
|
-
parentStreamId: options.streamId,
|
|
456
|
-
teamStreamId: (() => {
|
|
457
|
-
const stream = (
|
|
458
|
-
topologyPolicy as { getAgentStream?: (a: AgentId) => string | null }
|
|
459
|
-
).getAgentStream?.(agentId);
|
|
460
|
-
return stream ?? undefined;
|
|
461
|
-
})(),
|
|
462
|
-
workspaceManager,
|
|
463
|
-
getAgentByRole: (r: string) => {
|
|
464
|
-
for (const [aid, ws] of agentWorkspaces) {
|
|
465
|
-
const rec = agentStore.getAgent(aid);
|
|
466
|
-
if (rec?.role === r) return aid;
|
|
467
|
-
}
|
|
468
|
-
return null;
|
|
469
|
-
},
|
|
470
|
-
});
|
|
471
|
-
return executeWorkspaceDecision(agentId, decision, role, options);
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
// Capability-based dispatch for programmatic callers that don't use
|
|
475
|
-
// team YAML. This is the supported path for libraries that construct
|
|
476
|
-
// WorkspaceManager + GitCascadeAdapter directly and spawn agents with
|
|
477
|
-
// explicit `capabilities` + `streamId` arguments. It coexists with the
|
|
478
|
-
// V3 topology path above.
|
|
479
|
-
return capabilityBasedDispatch(agentId, options, workspaceManager);
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
/**
|
|
483
|
-
* Capability-based workspace allocation for programmatic callers.
|
|
484
|
-
*
|
|
485
|
-
* Matches on `workspace.stream` / `workspace.integrate` / `workspace.worktree`
|
|
486
|
-
* capabilities + corresponding streamId/streamConfig args. Delegates to the
|
|
487
|
-
* role-shaped WorkspaceManager methods (createWorkerWorkspace,
|
|
488
|
-
* createIntegratorWorkspace, createCoordinatorWorkspace).
|
|
489
|
-
*
|
|
490
|
-
* Not used by team-YAML-driven teams — those go through TopologyPolicy above.
|
|
491
|
-
*/
|
|
492
|
-
async function capabilityBasedDispatch(
|
|
493
|
-
agentId: AgentId,
|
|
494
|
-
options: SpawnAgentOptions,
|
|
495
|
-
ws: WorkspaceManager
|
|
496
|
-
): Promise<Workspace | undefined> {
|
|
497
|
-
const capabilities = options.capabilities ?? [];
|
|
498
|
-
const streamId = options.streamId;
|
|
499
|
-
// Merge taskRef (if set at spawn time) into streamConfig.metadata so that
|
|
500
|
-
// adapter.createStream → x-cascade/stream.opened carries the binding to
|
|
501
|
-
// OpenTasks. Explicit streamConfig.metadata.task_ref wins if already set.
|
|
502
|
-
const streamConfig = options.streamConfig
|
|
503
|
-
? options.taskRef &&
|
|
504
|
-
!(options.streamConfig.metadata &&
|
|
505
|
-
(options.streamConfig.metadata as Record<string, unknown>).task_ref)
|
|
506
|
-
? {
|
|
507
|
-
...options.streamConfig,
|
|
508
|
-
metadata: {
|
|
509
|
-
...(options.streamConfig.metadata ?? {}),
|
|
510
|
-
task_ref: options.taskRef,
|
|
511
|
-
},
|
|
512
|
-
}
|
|
513
|
-
: options.streamConfig
|
|
514
|
-
: undefined;
|
|
515
|
-
const gitCascadeTaskId = options.gitCascadeTaskId;
|
|
516
|
-
|
|
517
|
-
if (capabilities.includes("workspace.stream") && streamConfig) {
|
|
518
|
-
const newStreamId = ws.createIntegrationStream(agentId, streamConfig);
|
|
519
|
-
return ws.createCoordinatorWorkspace(agentId, newStreamId);
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
if (capabilities.includes("workspace.integrate") && streamId) {
|
|
523
|
-
return ws.createIntegratorWorkspace(agentId, streamId);
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
if (capabilities.includes("workspace.worktree") && streamId) {
|
|
527
|
-
const taskId = gitCascadeTaskId ?? agentId;
|
|
528
|
-
return ws.createWorkerWorkspace(agentId, taskId, streamId);
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
// No matching capability — agent inherits parent cwd (no workspace)
|
|
532
|
-
return undefined;
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
// ── Core Lifecycle ───────────────────────────────────────────
|
|
536
|
-
|
|
537
|
-
async function spawn(rawOptions: SpawnAgentOptions): Promise<SpawnedAgent> {
|
|
538
|
-
if (isShuttingDown) {
|
|
539
|
-
throw new AgentManagerError(
|
|
540
|
-
"Cannot spawn agent during shutdown",
|
|
541
|
-
"SHUTDOWN_IN_PROGRESS"
|
|
542
|
-
);
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
// Apply spawn interceptor (set by TeamRuntime)
|
|
546
|
-
const interceptedOptions = spawnInterceptor
|
|
547
|
-
? await spawnInterceptor(rawOptions)
|
|
548
|
-
: rawOptions;
|
|
549
|
-
|
|
550
|
-
// Resolve taskRef with three-level precedence:
|
|
551
|
-
// 1. Explicit `options.taskRef` (caller knows exactly what graph).
|
|
552
|
-
// 2. `resolveTaskRef(opts)` (multi-graph deployments decide per spawn).
|
|
553
|
-
// 3. `taskResourceId` + `options.task_id` (single-graph default).
|
|
554
|
-
// If none resolves, spawn proceeds with no taskRef — cascade events
|
|
555
|
-
// land without a task binding (hub back-fills from first commit that
|
|
556
|
-
// carries one, if any).
|
|
557
|
-
let resolvedTaskRef = interceptedOptions.taskRef;
|
|
558
|
-
if (!resolvedTaskRef && resolveTaskRef) {
|
|
559
|
-
try {
|
|
560
|
-
resolvedTaskRef = resolveTaskRef(interceptedOptions);
|
|
561
|
-
} catch (err) {
|
|
562
|
-
// Resolver failures must not block spawn. Log + fall through.
|
|
563
|
-
// eslint-disable-next-line no-console
|
|
564
|
-
console.warn(
|
|
565
|
-
"[agent-manager-v2] resolveTaskRef threw; falling back to taskResourceId default:",
|
|
566
|
-
err instanceof Error ? err.message : err
|
|
567
|
-
);
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
if (!resolvedTaskRef && taskResourceId && interceptedOptions.task_id) {
|
|
571
|
-
resolvedTaskRef = {
|
|
572
|
-
resource_id: taskResourceId,
|
|
573
|
-
node_id: String(interceptedOptions.task_id),
|
|
574
|
-
};
|
|
575
|
-
}
|
|
576
|
-
const options = resolvedTaskRef === interceptedOptions.taskRef
|
|
577
|
-
? interceptedOptions
|
|
578
|
-
: { ...interceptedOptions, taskRef: resolvedTaskRef };
|
|
579
|
-
|
|
580
|
-
const {
|
|
581
|
-
task,
|
|
582
|
-
task_id,
|
|
583
|
-
parent,
|
|
584
|
-
cwd = defaultCwd,
|
|
585
|
-
permissionMode = defaultPermissionMode,
|
|
586
|
-
askForAllTools = false,
|
|
587
|
-
subscribeParent = true,
|
|
588
|
-
topics = [],
|
|
589
|
-
config: agentConfig,
|
|
590
|
-
agentType = defaultAgentType,
|
|
591
|
-
customPrompt,
|
|
592
|
-
interactionPatterns,
|
|
593
|
-
role,
|
|
594
|
-
team_instance,
|
|
595
|
-
capabilities,
|
|
596
|
-
} = options;
|
|
597
|
-
|
|
598
|
-
// Generate IDs
|
|
599
|
-
const agentId = `agent_${nanoid(12)}` as AgentId;
|
|
600
|
-
const taskId = (task_id ?? `task_${nanoid(12)}`) as TaskId;
|
|
601
|
-
const sessionId = `session_${nanoid(12)}`;
|
|
602
|
-
const name = generateName();
|
|
603
|
-
|
|
604
|
-
// Validate parent exists
|
|
605
|
-
if (parent) {
|
|
606
|
-
const parentRecord = agentStore.getAgent(parent);
|
|
607
|
-
if (!parentRecord) {
|
|
608
|
-
throw new AgentManagerError(
|
|
609
|
-
`Parent agent ${parent} not found`,
|
|
610
|
-
"AGENT_NOT_FOUND",
|
|
611
|
-
parent
|
|
612
|
-
);
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
// Check spawn capability
|
|
616
|
-
if (role) {
|
|
617
|
-
const requiredCap = getSpawnCapability(role);
|
|
618
|
-
const parentRole = parentRecord.role;
|
|
619
|
-
if (
|
|
620
|
-
!roleRegistry.hasCapability(parentRole, requiredCap) &&
|
|
621
|
-
!roleRegistry.hasCapability(parentRole, AGENT_CAPABILITIES.SPAWN_CUSTOM)
|
|
622
|
-
) {
|
|
623
|
-
throw new AgentManagerError(
|
|
624
|
-
`Parent ${parent} (role: ${parentRole}) lacks capability ${requiredCap}`,
|
|
625
|
-
"CAPABILITY_DENIED",
|
|
626
|
-
parent
|
|
627
|
-
);
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
// Compute lineage
|
|
633
|
-
const lineage: AgentId[] = [];
|
|
634
|
-
if (parent) {
|
|
635
|
-
const parentRecord = agentStore.getAgent(parent);
|
|
636
|
-
if (parentRecord) {
|
|
637
|
-
lineage.push(...parentRecord.lineage, parent);
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
// Generate system prompt
|
|
642
|
-
const resolvedRole = role
|
|
643
|
-
? roleRegistry.resolveRole(role)
|
|
644
|
-
: undefined;
|
|
645
|
-
const systemPromptContext: SystemPromptContext = {
|
|
646
|
-
agentId,
|
|
647
|
-
task,
|
|
648
|
-
taskId,
|
|
649
|
-
parentId: parent ?? null,
|
|
650
|
-
isHeadManager: !parent,
|
|
651
|
-
lineage,
|
|
652
|
-
role,
|
|
653
|
-
};
|
|
654
|
-
let systemPrompt = generateSystemPrompt(systemPromptContext);
|
|
655
|
-
if (customPrompt) {
|
|
656
|
-
systemPrompt += `\n\n## Role Instructions\n\n${customPrompt}`;
|
|
657
|
-
} else if (resolvedRole?.systemPrompt) {
|
|
658
|
-
systemPrompt += `\n\n## Role Instructions\n\n${resolvedRole.systemPrompt}`;
|
|
659
|
-
}
|
|
660
|
-
if (interactionPatterns?.length) {
|
|
661
|
-
systemPrompt += `\n\n${interactionPatterns.join("\n\n")}`;
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
// Persist agent in store. Stash taskRef in metadata so done()'s
|
|
665
|
-
// lifecycle context can read it without separate plumbing — this is the
|
|
666
|
-
// path that makes per-commit task_ref binding work end-to-end.
|
|
667
|
-
const now = Date.now() as Timestamp;
|
|
668
|
-
const agentRecord: AgentRecord = {
|
|
669
|
-
id: agentId,
|
|
670
|
-
name,
|
|
671
|
-
role: role ?? (parent ? "worker" : "coordinator"),
|
|
672
|
-
state: "running",
|
|
673
|
-
parent_id: parent ?? null,
|
|
674
|
-
lineage,
|
|
675
|
-
team: team_instance,
|
|
676
|
-
scope: team_instance ?? "default",
|
|
677
|
-
task: task ?? "",
|
|
678
|
-
task_id: taskId,
|
|
679
|
-
cwd,
|
|
680
|
-
capabilities: capabilities ?? resolvedRole?.capabilities ?? [],
|
|
681
|
-
created_at: now,
|
|
682
|
-
started_at: now,
|
|
683
|
-
config: agentConfig as Record<string, unknown>,
|
|
684
|
-
metadata: {
|
|
685
|
-
...(options.taskRef ? { task_ref: options.taskRef } : {}),
|
|
686
|
-
// Persist isolatedSettings so resume() applies the same agentMeta
|
|
687
|
-
// policy without needing the original SpawnAgentOptions.
|
|
688
|
-
...(options.isolatedSettings ? { isolatedSettings: true } : {}),
|
|
689
|
-
},
|
|
690
|
-
};
|
|
691
|
-
agentStore.putAgent(agentRecord);
|
|
692
|
-
|
|
693
|
-
let handle: AgentHandle | undefined;
|
|
694
|
-
let workspace: Workspace | undefined;
|
|
695
|
-
|
|
696
|
-
try {
|
|
697
|
-
// Spawn process via acp-factory
|
|
698
|
-
const env: Record<string, string> = {
|
|
699
|
-
...agentConfig?.env,
|
|
700
|
-
};
|
|
701
|
-
|
|
702
|
-
// Configure cc-swarm to connect to macro-agent's local MAP server.
|
|
703
|
-
// These env vars are read by cc-swarm hooks in the Claude Code process
|
|
704
|
-
// (not the MCP subprocess), so they must be in the agent process env.
|
|
705
|
-
if (mapServerUrl) {
|
|
706
|
-
env.SWARM_MAP_SERVER = mapServerUrl;
|
|
707
|
-
env.SWARM_MAP_ENABLED = "true";
|
|
708
|
-
env.SWARM_MAP_SCOPE = `swarm:${agentId}`;
|
|
709
|
-
env.SWARM_SESSIONLOG_ENABLED = "true";
|
|
710
|
-
env.SWARM_SESSIONLOG_SYNC = "metrics";
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
handle = await AgentFactory.spawn(agentType, {
|
|
714
|
-
permissionMode,
|
|
715
|
-
env,
|
|
716
|
-
});
|
|
717
|
-
|
|
718
|
-
// Create workspace if applicable
|
|
719
|
-
workspace = await createWorkspaceForRole(agentId, role ?? "", options);
|
|
720
|
-
if (workspace) {
|
|
721
|
-
agentWorkspaces.set(agentId, workspace);
|
|
722
|
-
|
|
723
|
-
// Create and claim git-cascade task for workers
|
|
724
|
-
if (
|
|
725
|
-
workspace.role === "worker" &&
|
|
726
|
-
workspace.streamId &&
|
|
727
|
-
workspaceManager
|
|
728
|
-
) {
|
|
729
|
-
const dpTaskId = options.gitCascadeTaskId ?? agentId;
|
|
730
|
-
workspaceManager.createTask(workspace.streamId, {
|
|
731
|
-
title: task ?? `Task for ${agentId}`,
|
|
732
|
-
});
|
|
733
|
-
workspaceManager.claimTask(dpTaskId, agentId, workspace.path);
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
agentStore.updateAgent(agentId, {
|
|
737
|
-
cwd: workspace.path,
|
|
738
|
-
workspace_path: workspace.path,
|
|
739
|
-
workspace_stream_id: workspace.streamId,
|
|
740
|
-
});
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
const effectiveCwd = workspace?.path ?? cwd;
|
|
744
|
-
|
|
745
|
-
// Build MCP server config
|
|
746
|
-
const macroAgentMcp = buildMcpServerConfig({
|
|
747
|
-
agentId,
|
|
748
|
-
parentId: parent ?? "",
|
|
749
|
-
taskId,
|
|
750
|
-
cwd: effectiveCwd,
|
|
751
|
-
permissionMode,
|
|
752
|
-
lineage,
|
|
753
|
-
sessionId,
|
|
754
|
-
streamId: workspace?.streamId,
|
|
755
|
-
});
|
|
756
|
-
|
|
757
|
-
// Convert to acp-factory format
|
|
758
|
-
const mcpServers = [
|
|
759
|
-
{
|
|
760
|
-
name: macroAgentMcp.name,
|
|
761
|
-
command: macroAgentMcp.command,
|
|
762
|
-
args: macroAgentMcp.args ?? [],
|
|
763
|
-
env: Object.entries(macroAgentMcp.env ?? {}).map(([k, v]) => ({
|
|
764
|
-
name: k,
|
|
765
|
-
value: v,
|
|
766
|
-
})),
|
|
767
|
-
},
|
|
768
|
-
...(agentConfig?.mcpServers?.map((s) => {
|
|
769
|
-
if ("command" in s) {
|
|
770
|
-
return {
|
|
771
|
-
name: s.name,
|
|
772
|
-
command: s.command,
|
|
773
|
-
args: s.args ?? [],
|
|
774
|
-
env: Object.entries(s.env ?? {}).map(([k, v]) => ({
|
|
775
|
-
name: k,
|
|
776
|
-
value: v,
|
|
777
|
-
})),
|
|
778
|
-
};
|
|
779
|
-
}
|
|
780
|
-
return {
|
|
781
|
-
name: s.name,
|
|
782
|
-
type: s.type,
|
|
783
|
-
url: s.url,
|
|
784
|
-
headers: Object.entries(s.headers ?? {}).map(([k, v]) => ({
|
|
785
|
-
name: k,
|
|
786
|
-
value: v,
|
|
787
|
-
})),
|
|
788
|
-
};
|
|
789
|
-
}) ?? []),
|
|
790
|
-
];
|
|
791
|
-
|
|
792
|
-
// Always-on subsystem MCP servers (the "trinity"). The macro-agent
|
|
793
|
-
// architecture docs describe agent-inbox + opentasks as separate MCP
|
|
794
|
-
// servers available to spawned workers, but until this entry block
|
|
795
|
-
// existed they were only reachable when host-level Claude plugins
|
|
796
|
-
// happened to have wired them. That left mail-inbound workers
|
|
797
|
-
// (`parent: null` + `isolatedSettings: true`) without inbox/tasks
|
|
798
|
-
// tools — see openhive-2 docs/LOADOUTS_DESIGN.md "Loadout-provided
|
|
799
|
-
// MCP servers" live finding 2026-05-03.
|
|
800
|
-
//
|
|
801
|
-
// Registering them here makes them per-spawn defaults independent
|
|
802
|
-
// of host configuration. Caller-supplied `agentConfig.mcpServers`
|
|
803
|
-
// remains additive (Option C / "hybrid"): the trinity is always
|
|
804
|
-
// there, callers can layer more on top.
|
|
805
|
-
|
|
806
|
-
// agent-inbox — exposes send_message, check_inbox, read_thread,
|
|
807
|
-
// list_agents via the InboxMcpProxy stdio bridge.
|
|
808
|
-
if (inboxAdapter.socketPath) {
|
|
809
|
-
const inboxProxyEntry = new URL(
|
|
810
|
-
"../../dist/cli/inbox-mcp-proxy.js",
|
|
811
|
-
import.meta.url,
|
|
812
|
-
).pathname;
|
|
813
|
-
mcpServers.push({
|
|
814
|
-
name: "agent-inbox",
|
|
815
|
-
command: "node",
|
|
816
|
-
args: [inboxProxyEntry],
|
|
817
|
-
env: [
|
|
818
|
-
{ name: "INBOX_SOCKET_PATH", value: inboxAdapter.socketPath },
|
|
819
|
-
{ name: "MACRO_AGENT_ID", value: agentId },
|
|
820
|
-
],
|
|
821
|
-
} as any);
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
// opentasks — exposes task, link, annotate, query via the
|
|
825
|
-
// `opentasks mcp` CLI subcommand. The package's dist/mcp/stdio.js
|
|
826
|
-
// is an exports-only module (no auto-start); the CLI's `mcp`
|
|
827
|
-
// subcommand is what actually wires StdioServerTransport. Conditional
|
|
828
|
-
// on tasksAdapter.connected — when the daemon isn't running, skip
|
|
829
|
-
// rather than mount a server that would fail at every tool call.
|
|
830
|
-
if (tasksAdapter.connected) {
|
|
831
|
-
const opentasksCliEntry = new URL(
|
|
832
|
-
"opentasks/dist/cli.js",
|
|
833
|
-
import.meta.url,
|
|
834
|
-
).pathname;
|
|
835
|
-
mcpServers.push({
|
|
836
|
-
name: "opentasks",
|
|
837
|
-
command: "node",
|
|
838
|
-
args: [opentasksCliEntry, "mcp"],
|
|
839
|
-
env: [],
|
|
840
|
-
} as any);
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
// Register minimem MCP server (agent-type independent — works for any MCP-capable agent)
|
|
844
|
-
if (minimemConfig?.enabled) {
|
|
845
|
-
mcpServers.push({
|
|
846
|
-
name: "minimem",
|
|
847
|
-
command: "minimem",
|
|
848
|
-
args: [
|
|
849
|
-
"mcp",
|
|
850
|
-
"--dir", minimemConfig.dir ?? ".swarm/minimem/",
|
|
851
|
-
"--provider", minimemConfig.provider ?? "auto",
|
|
852
|
-
...(minimemConfig.global ? ["--global"] : []),
|
|
853
|
-
],
|
|
854
|
-
env: [],
|
|
855
|
-
} as any);
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
// Build agentMeta. Two layers:
|
|
859
|
-
//
|
|
860
|
-
// 1. `settingSources: []` — when the caller requests isolated
|
|
861
|
-
// settings (mail-inbound dispatch workers via
|
|
862
|
-
// SpawnAgentOptions.isolatedSettings), strip user/project/local
|
|
863
|
-
// setting sources so the worker doesn't load the host's
|
|
864
|
-
// claude-code-swarm / oh-my-claudecode / etc plugin MCP servers
|
|
865
|
-
// — those plugins assume host-shaped environment (sockets,
|
|
866
|
-
// daemons) and hang at session/new MCP-init when missing.
|
|
867
|
-
// Interactive `multiagent` callers leave this false so their
|
|
868
|
-
// installed plugins load normally.
|
|
869
|
-
//
|
|
870
|
-
// 2. `settings.permissions` — when the caller passes
|
|
871
|
-
// SpawnAgentOptions.permissions (e.g., from a materialized
|
|
872
|
-
// loadout), wire the rules inline via the Claude Agent SDK's
|
|
873
|
-
// session-level settings pass-through. Verified live: `deny`
|
|
874
|
-
// wins even over `permissionMode: "auto-approve"`. Inline
|
|
875
|
-
// wiring avoids file collisions when concurrent workers share
|
|
876
|
-
// a CWD (no `.claude/settings.json` written to disk).
|
|
877
|
-
//
|
|
878
|
-
// SDK contract pinned by the boundary test in
|
|
879
|
-
// `src/agent/__tests__/agent-manager-v2.permissions.test.ts` —
|
|
880
|
-
// it captures the literal `agentMeta` argument passed to
|
|
881
|
-
// `handle.createSession` and asserts both `settingSources: []`
|
|
882
|
-
// AND `settings.permissions` are present together (the
|
|
883
|
-
// interaction between filesystem-stripping and inline
|
|
884
|
-
// reconciliation that the SDK's docs don't fully spell out).
|
|
885
|
-
// If a future SDK version changes how `settings` reconciles
|
|
886
|
-
// with empty `settingSources`, that test will catch it.
|
|
887
|
-
//
|
|
888
|
-
// `ask` rules collapse based on `fullAutonomous`:
|
|
889
|
-
// - fullAutonomous: true → ask → allow (autonomous worker
|
|
890
|
-
// opts to proceed when there's no human to answer)
|
|
891
|
-
// - fullAutonomous: false → ask → deny (safe default;
|
|
892
|
-
// autonomous workers shouldn't make judgment calls)
|
|
893
|
-
const claudeCodeOptions: Record<string, any> = {};
|
|
894
|
-
if (options.isolatedSettings || permissionMode === "interactive") {
|
|
895
|
-
claudeCodeOptions.settingSources = [];
|
|
896
|
-
}
|
|
897
|
-
if (options.permissions) {
|
|
898
|
-
const { allow = [], deny = [], ask = [] } = options.permissions;
|
|
899
|
-
const finalAllow = options.fullAutonomous ? [...allow, ...ask] : [...allow];
|
|
900
|
-
const finalDeny = options.fullAutonomous ? [...deny] : [...deny, ...ask];
|
|
901
|
-
claudeCodeOptions.settings = {
|
|
902
|
-
permissions: {
|
|
903
|
-
...(finalAllow.length ? { allow: finalAllow } : {}),
|
|
904
|
-
...(finalDeny.length ? { deny: finalDeny } : {}),
|
|
905
|
-
},
|
|
906
|
-
};
|
|
907
|
-
}
|
|
908
|
-
// P3 spike: when an agent should funnel every tool call through the host
|
|
909
|
-
// (so the prompt-iterator handler can apply runtime overlays), set
|
|
910
|
-
// `ask: ['*']` on settings.permissions. The SDK then consults canUseTool
|
|
911
|
-
// for every tool, which emits `permission_request` session updates.
|
|
912
|
-
// Used for dispatch-target agents (mail+reuse, ACP+reuse) that need
|
|
913
|
-
// dynamic enforcement; chat agents and parented children stay on
|
|
914
|
-
// their session's static rules.
|
|
915
|
-
if (options.askForAllTools) {
|
|
916
|
-
const existingPerms =
|
|
917
|
-
claudeCodeOptions.settings?.permissions ?? {};
|
|
918
|
-
claudeCodeOptions.settings = {
|
|
919
|
-
...claudeCodeOptions.settings,
|
|
920
|
-
permissions: {
|
|
921
|
-
...existingPerms,
|
|
922
|
-
ask: ["*"],
|
|
923
|
-
},
|
|
924
|
-
};
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
// 3. Runtime permission overlay enforcement lives in the prompt
|
|
928
|
-
// iterator (see `prompt()` below), NOT here at spawn time.
|
|
929
|
-
//
|
|
930
|
-
// Background: an earlier design installed a Claude SDK PreToolUse
|
|
931
|
-
// hook here that closed over the per-process permission-overlay
|
|
932
|
-
// registry. That mechanism was verified broken: function callbacks
|
|
933
|
-
// inside arrays don't survive JSON.stringify across the
|
|
934
|
-
// macro-agent → claude-agent-acp stdio JSON-RPC boundary, so the
|
|
935
|
-
// hook arrived as `null` at the SDK and silently no-op'd.
|
|
936
|
-
//
|
|
937
|
-
// The current design uses ACP's `permission_request` session
|
|
938
|
-
// update path instead. When an agent is spawned with
|
|
939
|
-
// `askForAllTools: true` + `permissionMode: 'interactive'`, the
|
|
940
|
-
// SDK consults `canUseTool` on every tool call, claude-agent-acp
|
|
941
|
-
// converts that into a `client.requestPermission` call, and
|
|
942
|
-
// acp-factory emits it as a `permission_request` session update.
|
|
943
|
-
// The prompt iterator below intercepts those updates, evaluates
|
|
944
|
-
// against the overlay, and responds via `respondPermission`.
|
|
945
|
-
// See `docs/PERMISSION_OVERLAY_ACP_DESIGN.md` for the full design.
|
|
946
|
-
|
|
947
|
-
const agentMeta = Object.keys(claudeCodeOptions).length > 0
|
|
948
|
-
? { claudeCode: { options: claudeCodeOptions } }
|
|
949
|
-
: undefined;
|
|
950
|
-
|
|
951
|
-
// Build capabilities context + skill-tree loadout for system prompt
|
|
952
|
-
// Matches cc-swarm's context injection pattern (role-aware, tool-specific)
|
|
953
|
-
let contextSuffix = "";
|
|
954
|
-
try {
|
|
955
|
-
const { buildCapabilitiesContext } = await import("../integrations/context-builder.js");
|
|
956
|
-
contextSuffix = buildCapabilitiesContext({
|
|
957
|
-
role: parent ? (role ?? "worker") : null, // null = orchestrator, string = spawned agent
|
|
958
|
-
teamName: team_instance ?? undefined,
|
|
959
|
-
minimem: minimemConfig
|
|
960
|
-
? { enabled: minimemConfig.enabled, status: "ready" }
|
|
961
|
-
: undefined,
|
|
962
|
-
skilltree: skilltreeConfig
|
|
963
|
-
? { enabled: skilltreeConfig.enabled, status: "ready", profile: skilltreeConfig.defaultProfile }
|
|
964
|
-
: undefined,
|
|
965
|
-
sessionlog: sessionlogConfig
|
|
966
|
-
? { enabled: sessionlogConfig.enabled, sync: sessionlogConfig.sync }
|
|
967
|
-
: undefined,
|
|
968
|
-
mesh: mapServerUrl ? { enabled: false } : undefined, // mesh state from config
|
|
969
|
-
map: mapServerUrl
|
|
970
|
-
? { enabled: true, scope: `swarm:${agentId}`, status: "connected" }
|
|
971
|
-
: undefined,
|
|
972
|
-
opentasks: tasksAdapter.connected
|
|
973
|
-
? { enabled: true, status: "connected" }
|
|
974
|
-
: undefined,
|
|
975
|
-
inbox: { enabled: true },
|
|
976
|
-
});
|
|
977
|
-
} catch { /* context builder not available */ }
|
|
978
|
-
|
|
979
|
-
// Inject skill-tree loadout if available for this role
|
|
980
|
-
const roleLoadout = role ? skillLoadouts.get(role) : undefined;
|
|
981
|
-
if (roleLoadout) {
|
|
982
|
-
contextSuffix += `\n\n## Skills\n\n${roleLoadout}`;
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
// Merge context into system prompt
|
|
986
|
-
const enrichedPrompt = contextSuffix
|
|
987
|
-
? `${systemPrompt ?? ""}\n\n${contextSuffix}`.trim()
|
|
988
|
-
: systemPrompt;
|
|
989
|
-
|
|
990
|
-
// Create session
|
|
991
|
-
const session = await handle.createSession(effectiveCwd, {
|
|
992
|
-
mcpServers,
|
|
993
|
-
systemPrompt: enrichedPrompt ?? systemPrompt,
|
|
994
|
-
...(agentMeta && { agentMeta }),
|
|
995
|
-
} as any);
|
|
996
|
-
|
|
997
|
-
// Store session record
|
|
998
|
-
agentStore.putSession({
|
|
999
|
-
agent_id: agentId,
|
|
1000
|
-
session_id: sessionId,
|
|
1001
|
-
provider_session_id: session.id,
|
|
1002
|
-
created_at: now,
|
|
1003
|
-
});
|
|
1004
|
-
|
|
1005
|
-
// Update agent with provider session ID. Merge with existing metadata
|
|
1006
|
-
// so fields set at spawn time (e.g. task_ref) aren't clobbered.
|
|
1007
|
-
const existingMeta = agentStore.getAgent(agentId)?.metadata ?? {};
|
|
1008
|
-
agentStore.updateAgent(agentId, {
|
|
1009
|
-
metadata: { ...existingMeta, provider_session_id: session.id },
|
|
1010
|
-
});
|
|
1011
|
-
|
|
1012
|
-
// Register agent in inbox
|
|
1013
|
-
await inboxAdapter.registerAgent(agentId, {
|
|
1014
|
-
name,
|
|
1015
|
-
role: role ?? "worker",
|
|
1016
|
-
scope: team_instance ?? "default",
|
|
1017
|
-
});
|
|
1018
|
-
|
|
1019
|
-
// Track active session
|
|
1020
|
-
const activeSession: ActiveSession = {
|
|
1021
|
-
agentId,
|
|
1022
|
-
handle,
|
|
1023
|
-
session,
|
|
1024
|
-
createdAt: now,
|
|
1025
|
-
isPrompting: false,
|
|
1026
|
-
};
|
|
1027
|
-
activeSessions.set(agentId, activeSession);
|
|
1028
|
-
|
|
1029
|
-
// Notify lifecycle
|
|
1030
|
-
const agent = agentRecordToAgent(agentStore.getAgent(agentId)!);
|
|
1031
|
-
notifyLifecycle({ type: "spawned", agent });
|
|
1032
|
-
notifyLifecycle({ type: "started", agent });
|
|
1033
|
-
|
|
1034
|
-
// Start health monitoring for coordinators
|
|
1035
|
-
if (healthCheckService && role === "coordinator") {
|
|
1036
|
-
healthCheckService.startForCoordinator(agentId);
|
|
1037
|
-
}
|
|
1038
|
-
|
|
1039
|
-
return {
|
|
1040
|
-
id: agentId,
|
|
1041
|
-
session_id: sessionId,
|
|
1042
|
-
agent,
|
|
1043
|
-
session,
|
|
1044
|
-
workspace,
|
|
1045
|
-
streamId: workspace?.streamId,
|
|
1046
|
-
};
|
|
1047
|
-
} catch (err) {
|
|
1048
|
-
// Cleanup on failure
|
|
1049
|
-
if (handle) {
|
|
1050
|
-
try {
|
|
1051
|
-
await handle.close();
|
|
1052
|
-
} catch {
|
|
1053
|
-
/* ignore */
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
agentStore.updateAgent(agentId, {
|
|
1057
|
-
state: "failed",
|
|
1058
|
-
stop_reason: "failed",
|
|
1059
|
-
stopped_at: Date.now(),
|
|
1060
|
-
});
|
|
1061
|
-
throw err;
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1064
|
-
|
|
1065
|
-
async function terminate(
|
|
1066
|
-
agentId: AgentId,
|
|
1067
|
-
reason: AgentStopReason
|
|
1068
|
-
): Promise<void> {
|
|
1069
|
-
const record = agentStore.getAgent(agentId);
|
|
1070
|
-
if (!record) {
|
|
1071
|
-
throw new AgentManagerError(
|
|
1072
|
-
`Agent ${agentId} not found`,
|
|
1073
|
-
"AGENT_NOT_FOUND",
|
|
1074
|
-
agentId
|
|
1075
|
-
);
|
|
1076
|
-
}
|
|
1077
|
-
|
|
1078
|
-
// Close active session
|
|
1079
|
-
const activeSession = activeSessions.get(agentId);
|
|
1080
|
-
if (activeSession) {
|
|
1081
|
-
try {
|
|
1082
|
-
await activeSession.handle.close();
|
|
1083
|
-
} catch {
|
|
1084
|
-
/* ignore close errors */
|
|
1085
|
-
}
|
|
1086
|
-
activeSessions.delete(agentId);
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
// Stop health monitoring
|
|
1090
|
-
if (healthCheckService && record.role === "coordinator") {
|
|
1091
|
-
healthCheckService.stopForCoordinator(agentId);
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
// Land the worker's work if completed with a workspace.
|
|
1095
|
-
//
|
|
1096
|
-
// V3 path (preferred): look up the role's YAML landing strategy via
|
|
1097
|
-
// TopologyPolicy.getRoleConfig and dispatch through
|
|
1098
|
-
// WorkspaceManager.land(). This fires cascade events (stream.merged or
|
|
1099
|
-
// queue.added) so the hub sees the work. Landing = 'none' short-circuits.
|
|
1100
|
-
//
|
|
1101
|
-
// Legacy fallback: if no TopologyPolicy is wired or it can't resolve a
|
|
1102
|
-
// landing for this role, submit to the legacy MergeQueue as before.
|
|
1103
|
-
// Keeps pre-V3 programmatic callers + tests that bypass YAML working.
|
|
1104
|
-
if (
|
|
1105
|
-
workspaceManager &&
|
|
1106
|
-
agentWorkspaces.has(agentId) &&
|
|
1107
|
-
reason === "completed"
|
|
1108
|
-
) {
|
|
1109
|
-
const ws = agentWorkspaces.get(agentId)!;
|
|
1110
|
-
if (ws.role === "worker" && ws.streamId) {
|
|
1111
|
-
const roleConfig = topologyPolicy?.getRoleConfig?.(record.role);
|
|
1112
|
-
const yamlLandingName = roleConfig?.landing;
|
|
1113
|
-
const usingV3Landing =
|
|
1114
|
-
typeof yamlLandingName === "string" && yamlLandingName.length > 0;
|
|
1115
|
-
|
|
1116
|
-
if (usingV3Landing) {
|
|
1117
|
-
try {
|
|
1118
|
-
const taskRef = (record.metadata as Record<string, unknown> | undefined)
|
|
1119
|
-
?.task_ref as { resource_id: string; node_id: string } | undefined;
|
|
1120
|
-
await workspaceManager.land({
|
|
1121
|
-
agentId,
|
|
1122
|
-
streamId: ws.streamId,
|
|
1123
|
-
sourceWorktree: ws.path,
|
|
1124
|
-
strategyName: yamlLandingName,
|
|
1125
|
-
strategyConfig: roleConfig?.landing_config,
|
|
1126
|
-
taskRef,
|
|
1127
|
-
// Dispatcher overwrites this with `this`; placeholder keeps the
|
|
1128
|
-
// type satisfied without a cast.
|
|
1129
|
-
workspaceManager,
|
|
1130
|
-
});
|
|
1131
|
-
} catch {
|
|
1132
|
-
// Non-fatal landing failure — agent still terminates; conflicts
|
|
1133
|
-
// and strategy errors surface via WorkspaceEvent emission and
|
|
1134
|
-
// the strategy's own logs.
|
|
1135
|
-
}
|
|
1136
|
-
} else {
|
|
1137
|
-
try {
|
|
1138
|
-
const mergeQueue = workspaceManager.getMergeQueue();
|
|
1139
|
-
if (mergeQueue) {
|
|
1140
|
-
mergeQueue.submit({
|
|
1141
|
-
streamId: ws.streamId,
|
|
1142
|
-
workerBranch: ws.branch,
|
|
1143
|
-
taskId: record.task_id ?? agentId,
|
|
1144
|
-
workerAgentId: agentId,
|
|
1145
|
-
});
|
|
1146
|
-
}
|
|
1147
|
-
} catch {
|
|
1148
|
-
// Non-fatal merge queue submission failure
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
}
|
|
1152
|
-
}
|
|
1153
|
-
|
|
1154
|
-
// Deallocate workspace
|
|
1155
|
-
if (workspaceManager && agentWorkspaces.has(agentId)) {
|
|
1156
|
-
try {
|
|
1157
|
-
workspaceManager.deallocateWorkspace(agentId);
|
|
1158
|
-
} catch {
|
|
1159
|
-
/* ignore */
|
|
1160
|
-
}
|
|
1161
|
-
agentWorkspaces.delete(agentId);
|
|
1162
|
-
}
|
|
1163
|
-
|
|
1164
|
-
// Revoke auth token
|
|
1165
|
-
if (agentTokenManager) {
|
|
1166
|
-
agentTokenManager.revokeToken(agentId);
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
|
-
// Notify parent via inbox
|
|
1170
|
-
if (record.parent_id) {
|
|
1171
|
-
try {
|
|
1172
|
-
await inboxAdapter.send(
|
|
1173
|
-
agentId,
|
|
1174
|
-
record.parent_id,
|
|
1175
|
-
{
|
|
1176
|
-
type: "event",
|
|
1177
|
-
event: "agent_stopped",
|
|
1178
|
-
data: {
|
|
1179
|
-
agentId,
|
|
1180
|
-
reason,
|
|
1181
|
-
taskId: record.task_id,
|
|
1182
|
-
role: record.role,
|
|
1183
|
-
},
|
|
1184
|
-
},
|
|
1185
|
-
{ importance: "high", threadTag: `lifecycle:${agentId}` }
|
|
1186
|
-
);
|
|
1187
|
-
} catch {
|
|
1188
|
-
// Non-fatal inbox notification failure
|
|
1189
|
-
}
|
|
1190
|
-
}
|
|
1191
|
-
|
|
1192
|
-
// Deregister from inbox
|
|
1193
|
-
await inboxAdapter.deregisterAgent(agentId);
|
|
1194
|
-
|
|
1195
|
-
// Update agent state
|
|
1196
|
-
agentStore.updateAgent(agentId, {
|
|
1197
|
-
state: "stopped" as AgentState,
|
|
1198
|
-
stop_reason: reason as any,
|
|
1199
|
-
stopped_at: Date.now(),
|
|
1200
|
-
});
|
|
1201
|
-
|
|
1202
|
-
// Emit final trajectory checkpoint with phase: "ended"
|
|
1203
|
-
if (sidecarRef?.connected) {
|
|
1204
|
-
try {
|
|
1205
|
-
const session = agentStore.getSession(agentId);
|
|
1206
|
-
sidecarRef.reportCheckpoint({
|
|
1207
|
-
id: `${session?.session_id ?? agentId}-ended`,
|
|
1208
|
-
session_id: session?.session_id ?? agentId,
|
|
1209
|
-
agent: record.name ?? agentId,
|
|
1210
|
-
branch: null,
|
|
1211
|
-
files_touched: [],
|
|
1212
|
-
checkpoints_count: 0,
|
|
1213
|
-
metadata: { phase: "ended", reason },
|
|
1214
|
-
}).catch(() => {});
|
|
1215
|
-
} catch {
|
|
1216
|
-
// best effort
|
|
1217
|
-
}
|
|
1218
|
-
}
|
|
1219
|
-
|
|
1220
|
-
// Notify lifecycle
|
|
1221
|
-
const updatedAgent = agentRecordToAgent(agentStore.getAgent(agentId)!);
|
|
1222
|
-
notifyLifecycle({ type: "stopped", agent: updatedAgent, reason });
|
|
1223
|
-
|
|
1224
|
-
// Cascade termination to children
|
|
1225
|
-
const children = agentStore.getChildren(agentId);
|
|
1226
|
-
for (const child of children) {
|
|
1227
|
-
if (child.state === "running" || child.state === "spawning") {
|
|
1228
|
-
const wsProvider: WorkspaceProvider = {
|
|
1229
|
-
getWorkspace: (id: AgentId) => agentWorkspaces.get(id) ?? null,
|
|
1230
|
-
};
|
|
1231
|
-
const cascadeAdapter: CascadeAgentManager = {
|
|
1232
|
-
getChildren: (id: AgentId) =>
|
|
1233
|
-
agentStore
|
|
1234
|
-
.getChildren(id)
|
|
1235
|
-
.map((r) => agentRecordToAgent(r)),
|
|
1236
|
-
terminate: (id: AgentId, r: AgentStopReason) => terminate(id, r),
|
|
1237
|
-
};
|
|
1238
|
-
const parentTaskRef = (record.metadata as Record<string, unknown> | undefined)
|
|
1239
|
-
?.task_ref as { resource_id: string; node_id: string } | undefined;
|
|
1240
|
-
await terminateWithChangeConsolidation(
|
|
1241
|
-
child.id as AgentId,
|
|
1242
|
-
agentId,
|
|
1243
|
-
cascadeAdapter,
|
|
1244
|
-
wsProvider,
|
|
1245
|
-
undefined,
|
|
1246
|
-
workspaceManager ?? undefined,
|
|
1247
|
-
parentTaskRef
|
|
1248
|
-
);
|
|
1249
|
-
}
|
|
1250
|
-
}
|
|
1251
|
-
}
|
|
1252
|
-
|
|
1253
|
-
async function resume(
|
|
1254
|
-
agentId: AgentId,
|
|
1255
|
-
overridePermissionMode?: PermissionMode
|
|
1256
|
-
): Promise<SpawnedAgent> {
|
|
1257
|
-
if (isShuttingDown) {
|
|
1258
|
-
throw new AgentManagerError(
|
|
1259
|
-
"Cannot resume agent during shutdown",
|
|
1260
|
-
"SHUTDOWN_IN_PROGRESS"
|
|
1261
|
-
);
|
|
1262
|
-
}
|
|
1263
|
-
|
|
1264
|
-
const record = agentStore.getAgent(agentId);
|
|
1265
|
-
if (!record) {
|
|
1266
|
-
throw new AgentManagerError(
|
|
1267
|
-
`Agent ${agentId} not found`,
|
|
1268
|
-
"AGENT_NOT_FOUND",
|
|
1269
|
-
agentId
|
|
1270
|
-
);
|
|
1271
|
-
}
|
|
1272
|
-
|
|
1273
|
-
if (activeSessions.has(agentId)) {
|
|
1274
|
-
throw new AgentManagerError(
|
|
1275
|
-
`Agent ${agentId} already has active session`,
|
|
1276
|
-
"ALREADY_RUNNING",
|
|
1277
|
-
agentId
|
|
1278
|
-
);
|
|
1279
|
-
}
|
|
1280
|
-
|
|
1281
|
-
const permMode = overridePermissionMode ?? defaultPermissionMode;
|
|
1282
|
-
const agentCwd = record.cwd || defaultCwd;
|
|
1283
|
-
|
|
1284
|
-
const handle = await AgentFactory.spawn(defaultAgentType, {
|
|
1285
|
-
permissionMode: permMode,
|
|
1286
|
-
});
|
|
1287
|
-
|
|
1288
|
-
const macroAgentMcp = buildMcpServerConfig({
|
|
1289
|
-
agentId,
|
|
1290
|
-
parentId: record.parent_id ?? "",
|
|
1291
|
-
taskId: record.task_id ?? "",
|
|
1292
|
-
cwd: agentCwd,
|
|
1293
|
-
permissionMode: permMode,
|
|
1294
|
-
lineage: record.lineage,
|
|
1295
|
-
});
|
|
1296
|
-
|
|
1297
|
-
const mcpServers = [
|
|
1298
|
-
{
|
|
1299
|
-
name: macroAgentMcp.name,
|
|
1300
|
-
command: macroAgentMcp.command,
|
|
1301
|
-
args: macroAgentMcp.args ?? [],
|
|
1302
|
-
env: Object.entries(macroAgentMcp.env ?? {}).map(([k, v]) => ({
|
|
1303
|
-
name: k,
|
|
1304
|
-
value: v,
|
|
1305
|
-
})),
|
|
1306
|
-
},
|
|
1307
|
-
];
|
|
1308
|
-
|
|
1309
|
-
// Strip user/project/local setting sources for isolated workers (the
|
|
1310
|
-
// metadata flag is set at spawn time when SpawnAgentOptions.isolatedSettings
|
|
1311
|
-
// was true) or interactive mode. See spawn() for the rationale.
|
|
1312
|
-
const isIsolated = (record.metadata as Record<string, unknown> | undefined)?.isolatedSettings === true;
|
|
1313
|
-
const agentMeta =
|
|
1314
|
-
isIsolated || permMode === "interactive"
|
|
1315
|
-
? { claudeCode: { options: { settingSources: [] } } }
|
|
1316
|
-
: undefined;
|
|
1317
|
-
|
|
1318
|
-
// Try to load existing session or create new
|
|
1319
|
-
const sessionRecord = agentStore.getSession(agentId);
|
|
1320
|
-
let session: Session;
|
|
1321
|
-
|
|
1322
|
-
if (sessionRecord?.provider_session_id) {
|
|
1323
|
-
session = await handle.loadSession(
|
|
1324
|
-
sessionRecord.provider_session_id,
|
|
1325
|
-
agentCwd,
|
|
1326
|
-
mcpServers as any,
|
|
1327
|
-
agentMeta ? { agentMeta } : undefined
|
|
1328
|
-
);
|
|
1329
|
-
} else {
|
|
1330
|
-
session = await handle.createSession(agentCwd, {
|
|
1331
|
-
mcpServers,
|
|
1332
|
-
...(agentMeta && { agentMeta }),
|
|
1333
|
-
});
|
|
1334
|
-
}
|
|
1335
|
-
|
|
1336
|
-
const now = Date.now() as Timestamp;
|
|
1337
|
-
activeSessions.set(agentId, {
|
|
1338
|
-
agentId,
|
|
1339
|
-
handle,
|
|
1340
|
-
session,
|
|
1341
|
-
createdAt: now,
|
|
1342
|
-
isPrompting: false,
|
|
1343
|
-
});
|
|
1344
|
-
|
|
1345
|
-
agentStore.updateAgent(agentId, {
|
|
1346
|
-
state: "running",
|
|
1347
|
-
started_at: now,
|
|
1348
|
-
});
|
|
1349
|
-
agentStore.putSession({
|
|
1350
|
-
agent_id: agentId,
|
|
1351
|
-
session_id: sessionRecord?.session_id ?? `session_${nanoid(12)}`,
|
|
1352
|
-
provider_session_id: session.id,
|
|
1353
|
-
created_at: now,
|
|
1354
|
-
});
|
|
1355
|
-
|
|
1356
|
-
const agent = agentRecordToAgent(agentStore.getAgent(agentId)!);
|
|
1357
|
-
|
|
1358
|
-
// Re-publish the agent to subscribers (local MAP server, hub lifecycle
|
|
1359
|
-
// bridge, team auto-join listeners) so a resumed agent is a first-class
|
|
1360
|
-
// registered agent — not just an in-memory handle. Without this, the hub
|
|
1361
|
-
// never re-registers the agent after cold-start; ACP routing works but
|
|
1362
|
-
// the hub's "Registered Agents" view stays empty and capabilities never
|
|
1363
|
-
// propagate back through `map/agents/register`.
|
|
1364
|
-
//
|
|
1365
|
-
// Spawn semantics are correct here: the process is new, the session is
|
|
1366
|
-
// (re)loaded, and subscribers treat it as a fresh registration. Paired
|
|
1367
|
-
// with the `stopped` event that fired on the prior termination, this
|
|
1368
|
-
// keeps the bridge's `registered` map consistent.
|
|
1369
|
-
notifyLifecycle({ type: "spawned", agent });
|
|
1370
|
-
notifyLifecycle({ type: "started", agent });
|
|
1371
|
-
|
|
1372
|
-
return {
|
|
1373
|
-
id: agentId,
|
|
1374
|
-
session_id: sessionRecord?.session_id ?? "",
|
|
1375
|
-
agent,
|
|
1376
|
-
session,
|
|
1377
|
-
};
|
|
1378
|
-
}
|
|
1379
|
-
|
|
1380
|
-
async function continueAgent(
|
|
1381
|
-
agentId: AgentId,
|
|
1382
|
-
options?: ContinueAgentOptions
|
|
1383
|
-
): Promise<SpawnedAgent> {
|
|
1384
|
-
const record = agentStore.getAgent(agentId);
|
|
1385
|
-
if (!record) {
|
|
1386
|
-
throw new AgentManagerError(
|
|
1387
|
-
`Agent ${agentId} not found`,
|
|
1388
|
-
"AGENT_NOT_FOUND",
|
|
1389
|
-
agentId
|
|
1390
|
-
);
|
|
1391
|
-
}
|
|
1392
|
-
|
|
1393
|
-
const contextLines: string[] = [];
|
|
1394
|
-
if (options?.additionalContext) {
|
|
1395
|
-
contextLines.push(options.additionalContext);
|
|
1396
|
-
}
|
|
1397
|
-
contextLines.push(`## Prior Session Context`);
|
|
1398
|
-
contextLines.push(`Continuing from agent ${agentId}.`);
|
|
1399
|
-
|
|
1400
|
-
const resumeContext = contextLines.join("\n");
|
|
1401
|
-
const taskDescription =
|
|
1402
|
-
options?.task ?? record.task ?? `Continue work from ${agentId}`;
|
|
1403
|
-
|
|
1404
|
-
return spawn({
|
|
1405
|
-
task: taskDescription,
|
|
1406
|
-
role: record.role,
|
|
1407
|
-
parent: record.parent_id ?? undefined,
|
|
1408
|
-
cwd: record.cwd || defaultCwd,
|
|
1409
|
-
customPrompt: resumeContext,
|
|
1410
|
-
});
|
|
1411
|
-
}
|
|
1412
|
-
|
|
1413
|
-
async function forkAgent(
|
|
1414
|
-
sourceAgentId: AgentId,
|
|
1415
|
-
options?: { name?: string; prompt?: string; cwd?: string }
|
|
1416
|
-
): Promise<SpawnedAgent> {
|
|
1417
|
-
if (isShuttingDown) {
|
|
1418
|
-
throw new AgentManagerError(
|
|
1419
|
-
"Cannot fork during shutdown",
|
|
1420
|
-
"SHUTDOWN_IN_PROGRESS"
|
|
1421
|
-
);
|
|
1422
|
-
}
|
|
1423
|
-
|
|
1424
|
-
const record = agentStore.getAgent(sourceAgentId);
|
|
1425
|
-
if (!record) {
|
|
1426
|
-
throw new AgentManagerError(
|
|
1427
|
-
`Agent ${sourceAgentId} not found`,
|
|
1428
|
-
"AGENT_NOT_FOUND",
|
|
1429
|
-
sourceAgentId
|
|
1430
|
-
);
|
|
1431
|
-
}
|
|
1432
|
-
|
|
1433
|
-
const activeSession = activeSessions.get(sourceAgentId);
|
|
1434
|
-
const sessionRecord = agentStore.getSession(sourceAgentId);
|
|
1435
|
-
if (!activeSession && !sessionRecord?.provider_session_id) {
|
|
1436
|
-
throw new AgentManagerError(
|
|
1437
|
-
`Agent ${sourceAgentId} has no session to fork`,
|
|
1438
|
-
"FORK_NOT_SUPPORTED",
|
|
1439
|
-
sourceAgentId
|
|
1440
|
-
);
|
|
1441
|
-
}
|
|
1442
|
-
|
|
1443
|
-
const forkCwd = options?.cwd ?? record.cwd ?? defaultCwd;
|
|
1444
|
-
|
|
1445
|
-
// Get forked session ID
|
|
1446
|
-
let forkedProviderSessionId: string;
|
|
1447
|
-
if (activeSession) {
|
|
1448
|
-
const forkedSession = await activeSession.session.forkWithFlush();
|
|
1449
|
-
forkedProviderSessionId = forkedSession.id;
|
|
1450
|
-
} else {
|
|
1451
|
-
forkedProviderSessionId = sessionRecord!.provider_session_id!;
|
|
1452
|
-
}
|
|
1453
|
-
|
|
1454
|
-
// Spawn new process
|
|
1455
|
-
const handle = await AgentFactory.spawn(defaultAgentType, {
|
|
1456
|
-
permissionMode: defaultPermissionMode,
|
|
1457
|
-
});
|
|
1458
|
-
|
|
1459
|
-
const agentId = `agent_${nanoid(12)}` as AgentId;
|
|
1460
|
-
const taskId = `task_${nanoid(12)}` as TaskId;
|
|
1461
|
-
const sessionId = `session_${nanoid(12)}`;
|
|
1462
|
-
const name = options?.name ?? generateName();
|
|
1463
|
-
const now = Date.now() as Timestamp;
|
|
1464
|
-
|
|
1465
|
-
// Persist forked agent
|
|
1466
|
-
agentStore.putAgent({
|
|
1467
|
-
id: agentId,
|
|
1468
|
-
name,
|
|
1469
|
-
role: record.role,
|
|
1470
|
-
state: "running",
|
|
1471
|
-
parent_id: record.parent_id,
|
|
1472
|
-
lineage: record.lineage,
|
|
1473
|
-
team: record.team,
|
|
1474
|
-
scope: record.scope,
|
|
1475
|
-
task: options?.prompt ?? `[Fork of ${sourceAgentId}]`,
|
|
1476
|
-
task_id: taskId,
|
|
1477
|
-
cwd: forkCwd,
|
|
1478
|
-
capabilities: record.capabilities,
|
|
1479
|
-
created_at: now,
|
|
1480
|
-
started_at: now,
|
|
1481
|
-
metadata: { fork_of: sourceAgentId },
|
|
1482
|
-
});
|
|
1483
|
-
|
|
1484
|
-
const macroAgentMcp = buildMcpServerConfig({
|
|
1485
|
-
agentId,
|
|
1486
|
-
parentId: record.parent_id ?? "",
|
|
1487
|
-
taskId,
|
|
1488
|
-
cwd: forkCwd,
|
|
1489
|
-
permissionMode: defaultPermissionMode,
|
|
1490
|
-
lineage: record.lineage,
|
|
1491
|
-
sessionId,
|
|
1492
|
-
});
|
|
1493
|
-
|
|
1494
|
-
const session = await handle.loadSession(
|
|
1495
|
-
forkedProviderSessionId,
|
|
1496
|
-
forkCwd,
|
|
1497
|
-
[
|
|
1498
|
-
{
|
|
1499
|
-
name: macroAgentMcp.name,
|
|
1500
|
-
command: macroAgentMcp.command,
|
|
1501
|
-
args: macroAgentMcp.args ?? [],
|
|
1502
|
-
env: Object.entries(macroAgentMcp.env ?? {}).map(([k, v]) => ({
|
|
1503
|
-
name: k,
|
|
1504
|
-
value: v,
|
|
1505
|
-
})),
|
|
1506
|
-
},
|
|
1507
|
-
] as any
|
|
1508
|
-
);
|
|
1509
|
-
|
|
1510
|
-
agentStore.putSession({
|
|
1511
|
-
agent_id: agentId,
|
|
1512
|
-
session_id: sessionId,
|
|
1513
|
-
provider_session_id: session.id,
|
|
1514
|
-
created_at: now,
|
|
1515
|
-
});
|
|
1516
|
-
|
|
1517
|
-
await inboxAdapter.registerAgent(agentId, {
|
|
1518
|
-
name,
|
|
1519
|
-
role: record.role,
|
|
1520
|
-
scope: record.scope,
|
|
1521
|
-
});
|
|
1522
|
-
|
|
1523
|
-
activeSessions.set(agentId, {
|
|
1524
|
-
agentId,
|
|
1525
|
-
handle,
|
|
1526
|
-
session,
|
|
1527
|
-
createdAt: now,
|
|
1528
|
-
isPrompting: false,
|
|
1529
|
-
});
|
|
1530
|
-
|
|
1531
|
-
const agent = agentRecordToAgent(agentStore.getAgent(agentId)!);
|
|
1532
|
-
notifyLifecycle({ type: "spawned", agent });
|
|
1533
|
-
notifyLifecycle({ type: "started", agent });
|
|
1534
|
-
|
|
1535
|
-
return { id: agentId, session_id: sessionId, agent, session };
|
|
1536
|
-
}
|
|
1537
|
-
|
|
1538
|
-
// ── Query Methods ──────────────────────────────────────────
|
|
1539
|
-
|
|
1540
|
-
function get(agentId: AgentId): Agent | null {
|
|
1541
|
-
const record = agentStore.getAgent(agentId);
|
|
1542
|
-
return record ? agentRecordToAgent(record) : null;
|
|
1543
|
-
}
|
|
1544
|
-
|
|
1545
|
-
function list(filter?: AgentFilter): Agent[] {
|
|
1546
|
-
const records = agentStore.listAgents({
|
|
1547
|
-
state: filter?.state,
|
|
1548
|
-
parent_id: filter?.parent,
|
|
1549
|
-
});
|
|
1550
|
-
|
|
1551
|
-
let result = records.map(agentRecordToAgent);
|
|
1552
|
-
|
|
1553
|
-
if (filter?.task_id) {
|
|
1554
|
-
result = result.filter((a) => a.task_id === filter.task_id);
|
|
1555
|
-
}
|
|
1556
|
-
if (filter?.headManagersOnly) {
|
|
1557
|
-
result = result.filter((a) => !a.parent);
|
|
1558
|
-
}
|
|
1559
|
-
|
|
1560
|
-
return result;
|
|
1561
|
-
}
|
|
1562
|
-
|
|
1563
|
-
function getChildren(agentId: AgentId): Agent[] {
|
|
1564
|
-
return agentStore.getChildren(agentId).map(agentRecordToAgent);
|
|
1565
|
-
}
|
|
1566
|
-
|
|
1567
|
-
function getHierarchy(
|
|
1568
|
-
agentId: AgentId,
|
|
1569
|
-
options?: HierarchyOptions
|
|
1570
|
-
): AgentHierarchy | null {
|
|
1571
|
-
const record = agentStore.getAgent(agentId);
|
|
1572
|
-
if (!record) return null;
|
|
1573
|
-
|
|
1574
|
-
function buildTree(
|
|
1575
|
-
id: AgentId,
|
|
1576
|
-
depth: number
|
|
1577
|
-
): AgentHierarchyNode {
|
|
1578
|
-
const agent = agentRecordToAgent(agentStore.getAgent(id)!);
|
|
1579
|
-
const maxDepth = options?.depth;
|
|
1580
|
-
const children =
|
|
1581
|
-
maxDepth !== undefined && depth >= maxDepth
|
|
1582
|
-
? []
|
|
1583
|
-
: agentStore
|
|
1584
|
-
.getChildren(id)
|
|
1585
|
-
.map((c) => buildTree(c.id as AgentId, depth + 1));
|
|
1586
|
-
|
|
1587
|
-
return { agent, children };
|
|
1588
|
-
}
|
|
1589
|
-
|
|
1590
|
-
const root = buildTree(agentId, 0);
|
|
1591
|
-
|
|
1592
|
-
function countNodes(node: AgentHierarchyNode): number {
|
|
1593
|
-
return 1 + node.children.reduce((sum, c) => sum + countNodes(c), 0);
|
|
1594
|
-
}
|
|
1595
|
-
|
|
1596
|
-
function maxDepth(node: AgentHierarchyNode, d: number): number {
|
|
1597
|
-
if (node.children.length === 0) return d;
|
|
1598
|
-
return Math.max(...node.children.map((c) => maxDepth(c, d + 1)));
|
|
1599
|
-
}
|
|
1600
|
-
|
|
1601
|
-
return {
|
|
1602
|
-
root,
|
|
1603
|
-
depth: maxDepth(root, 0),
|
|
1604
|
-
totalAgents: countNodes(root),
|
|
1605
|
-
};
|
|
1606
|
-
}
|
|
1607
|
-
|
|
1608
|
-
// ── Head Manager ─────────────────────────────────────────────
|
|
1609
|
-
|
|
1610
|
-
async function getOrCreateHeadManager(
|
|
1611
|
-
options: HeadManagerOptions
|
|
1612
|
-
): Promise<SpawnedAgent> {
|
|
1613
|
-
// Check for an existing head manager matching this cwd that ALSO has a
|
|
1614
|
-
// live session in this process. The activeSessions check has to be inside
|
|
1615
|
-
// the predicate (not after .find) — the agentStore is persistent across
|
|
1616
|
-
// process restarts, so without this filter we'd match stale "running"
|
|
1617
|
-
// records from previous processes whose sessions are gone, then fall
|
|
1618
|
-
// through to spawn() and create a duplicate coordinator.
|
|
1619
|
-
const existing = agentStore
|
|
1620
|
-
.listAgents({ parent_id: null, state: "running" })
|
|
1621
|
-
.find(
|
|
1622
|
-
(a) =>
|
|
1623
|
-
a.cwd === options.cwd &&
|
|
1624
|
-
activeSessions.has(a.id as AgentId),
|
|
1625
|
-
);
|
|
1626
|
-
|
|
1627
|
-
if (existing) {
|
|
1628
|
-
const sessionEntry = activeSessions.get(existing.id as AgentId)!;
|
|
1629
|
-
const storedSession = agentStore.getSession(existing.id as AgentId);
|
|
1630
|
-
return {
|
|
1631
|
-
id: existing.id as AgentId,
|
|
1632
|
-
session_id: storedSession?.session_id ?? sessionEntry.session.id ?? "",
|
|
1633
|
-
agent: agentRecordToAgent(existing),
|
|
1634
|
-
session: sessionEntry.session,
|
|
1635
|
-
};
|
|
1636
|
-
}
|
|
1637
|
-
|
|
1638
|
-
return spawn({
|
|
1639
|
-
task: "Head manager",
|
|
1640
|
-
parent: null,
|
|
1641
|
-
cwd: options.cwd,
|
|
1642
|
-
permissionMode: options.permissionMode,
|
|
1643
|
-
customPrompt: options.systemPrompt,
|
|
1644
|
-
role: "coordinator",
|
|
1645
|
-
topics: options.topics,
|
|
1646
|
-
});
|
|
1647
|
-
}
|
|
1648
|
-
|
|
1649
|
-
function listHeadManagers(): Agent[] {
|
|
1650
|
-
return agentStore
|
|
1651
|
-
.listAgents({ parent_id: null })
|
|
1652
|
-
.map(agentRecordToAgent);
|
|
1653
|
-
}
|
|
1654
|
-
|
|
1655
|
-
/**
|
|
1656
|
-
* Look up the spawned-agent shape for any agent that's still alive in this
|
|
1657
|
-
* process (any role, not just coordinators). Returns null if the agent
|
|
1658
|
-
* doesn't exist, isn't running, or has no live session in `activeSessions`.
|
|
1659
|
-
*
|
|
1660
|
-
* Used by the ACP layer to bind a session to a specific agent when the MAP
|
|
1661
|
-
* stream targets one explicitly — preserving the routing intent that
|
|
1662
|
-
* cwd-based head-manager lookup would otherwise lose in multi-coordinator
|
|
1663
|
-
* scenarios.
|
|
1664
|
-
*/
|
|
1665
|
-
function getActiveAgentSession(agentId: AgentId): SpawnedAgent | null {
|
|
1666
|
-
if (!activeSessions.has(agentId)) return null;
|
|
1667
|
-
const record = agentStore.getAgent(agentId);
|
|
1668
|
-
if (!record || record.state !== "running") return null;
|
|
1669
|
-
const sessionEntry = activeSessions.get(agentId)!;
|
|
1670
|
-
const storedSession = agentStore.getSession(agentId);
|
|
1671
|
-
return {
|
|
1672
|
-
id: agentId,
|
|
1673
|
-
session_id: storedSession?.session_id ?? sessionEntry.session.id ?? "",
|
|
1674
|
-
agent: agentRecordToAgent(record),
|
|
1675
|
-
session: sessionEntry.session,
|
|
1676
|
-
};
|
|
1677
|
-
}
|
|
1678
|
-
|
|
1679
|
-
// ── Session Interaction ──────────────────────────────────────
|
|
1680
|
-
|
|
1681
|
-
async function* prompt(
|
|
1682
|
-
agentId: AgentId,
|
|
1683
|
-
message: string
|
|
1684
|
-
): AsyncIterable<ExtendedSessionUpdate> {
|
|
1685
|
-
const activeSession = activeSessions.get(agentId);
|
|
1686
|
-
if (!activeSession) {
|
|
1687
|
-
throw new AgentManagerError(
|
|
1688
|
-
`No active session for agent ${agentId}`,
|
|
1689
|
-
"SESSION_NOT_FOUND",
|
|
1690
|
-
agentId
|
|
1691
|
-
);
|
|
1692
|
-
}
|
|
1693
|
-
|
|
1694
|
-
activeSession.isPrompting = true;
|
|
1695
|
-
try {
|
|
1696
|
-
// Permission overlay enforcement — for agents in dispatch context
|
|
1697
|
-
// (mail-inbound + ACP reuse targets), the dispatch consumer sets a
|
|
1698
|
-
// per-agent overlay before driving prompt(). When set, `permission_request`
|
|
1699
|
-
// session updates are intercepted here, evaluated against the overlay,
|
|
1700
|
-
// and answered via `respondToPermission`. The update is NOT yielded
|
|
1701
|
-
// to the consumer in that case — dispatch enforcement is internal.
|
|
1702
|
-
//
|
|
1703
|
-
// When no overlay is set (the common case — chat agents, sub-agents
|
|
1704
|
-
// spawned by parents, etc.), permission_request updates are yielded
|
|
1705
|
-
// through unchanged so chat surfaces' UI permission dialogs (the
|
|
1706
|
-
// swarmcraft PermissionDialog rendered via the openhive-acp-service
|
|
1707
|
-
// WS subscription) keep working.
|
|
1708
|
-
//
|
|
1709
|
-
// See `docs/PERMISSION_OVERLAY_ACP_DESIGN.md` for the rationale and
|
|
1710
|
-
// a diagram of the four-process flow.
|
|
1711
|
-
for await (const update of activeSession.session.prompt(message)) {
|
|
1712
|
-
const u = update as {
|
|
1713
|
-
sessionUpdate?: string;
|
|
1714
|
-
requestId?: string;
|
|
1715
|
-
toolCall?: { title?: string; kind?: string; rawInput?: unknown };
|
|
1716
|
-
options?: Array<{ kind?: string; optionId?: string }>;
|
|
1717
|
-
};
|
|
1718
|
-
if (u?.sessionUpdate === "permission_request") {
|
|
1719
|
-
const overlay = getPermissionOverlay(agentId);
|
|
1720
|
-
// No overlay → pass through to the consumer (chat UI, etc.).
|
|
1721
|
-
if (!overlay) {
|
|
1722
|
-
yield update;
|
|
1723
|
-
continue;
|
|
1724
|
-
}
|
|
1725
|
-
let optionId: string | undefined;
|
|
1726
|
-
try {
|
|
1727
|
-
const toolName = deriveToolName(u.toolCall);
|
|
1728
|
-
const toolInput = u.toolCall?.rawInput ?? {};
|
|
1729
|
-
const decision = evaluatePermission(toolName, toolInput, overlay)
|
|
1730
|
-
.decision;
|
|
1731
|
-
const wantedKind =
|
|
1732
|
-
decision === "deny" ? "reject_once" : "allow_once";
|
|
1733
|
-
const opt =
|
|
1734
|
-
u.options?.find((o) => o.kind === wantedKind) ??
|
|
1735
|
-
u.options?.find(
|
|
1736
|
-
(o) =>
|
|
1737
|
-
o.kind === (decision === "deny" ? "reject_always" : "allow_always"),
|
|
1738
|
-
);
|
|
1739
|
-
optionId = opt?.optionId;
|
|
1740
|
-
} catch {
|
|
1741
|
-
// Fail closed: on registry/evaluator error, deny.
|
|
1742
|
-
optionId = u.options?.find((o) => o.kind === "reject_once")?.optionId;
|
|
1743
|
-
}
|
|
1744
|
-
if (u.requestId && optionId) {
|
|
1745
|
-
try {
|
|
1746
|
-
(activeSession.session as any).respondToPermission?.(
|
|
1747
|
-
u.requestId,
|
|
1748
|
-
optionId,
|
|
1749
|
-
);
|
|
1750
|
-
} catch (err) {
|
|
1751
|
-
console.warn(
|
|
1752
|
-
`[perm-overlay] respondToPermission failed agent=${agentId} req=${u.requestId}: ${(err as Error).message}`,
|
|
1753
|
-
);
|
|
1754
|
-
}
|
|
1755
|
-
}
|
|
1756
|
-
// Don't yield permission_request to the consumer — dispatch-internal.
|
|
1757
|
-
continue;
|
|
1758
|
-
}
|
|
1759
|
-
yield update;
|
|
1760
|
-
}
|
|
1761
|
-
} finally {
|
|
1762
|
-
activeSession.isPrompting = false;
|
|
1763
|
-
agentStore.updateAgent(agentId, {
|
|
1764
|
-
last_activity_at: Date.now(),
|
|
1765
|
-
});
|
|
1766
|
-
}
|
|
1767
|
-
}
|
|
1768
|
-
|
|
1769
|
-
async function promptUntilDone(
|
|
1770
|
-
agentId: AgentId,
|
|
1771
|
-
message: string,
|
|
1772
|
-
options?: {
|
|
1773
|
-
maxFollowUps?: number;
|
|
1774
|
-
onUpdate?: (update: ExtendedSessionUpdate) => void;
|
|
1775
|
-
/**
|
|
1776
|
-
* Optional caller-supplied completion predicate. When it returns true the
|
|
1777
|
-
* loop stops consuming, terminates the agent as "completed", and returns
|
|
1778
|
-
* `completedExternally: true` — WITHOUT requiring the macro `done()` tool.
|
|
1779
|
-
*
|
|
1780
|
-
* Checked three ways (all fire terminate + return completedExternally):
|
|
1781
|
-
* (a) reactively after each streamed update (fast path),
|
|
1782
|
-
* (b) reactively after each attempt's stream ends (fast path),
|
|
1783
|
-
* (c) on a CONCURRENT ~2s timer while consuming the stream. (c) is the
|
|
1784
|
-
* critical path for the live τ rollout: the agent runs the whole
|
|
1785
|
-
* episode in ONE prompt() call, calls τ's finish, the env writes its
|
|
1786
|
-
* reward sink (done:true), and then the prompt() stream goes SILENT
|
|
1787
|
-
* and does NOT close — the async iterator just blocks. With only the
|
|
1788
|
-
* reactive checks (a)/(b) the predicate is never re-evaluated and
|
|
1789
|
-
* solve() hangs until the rollout backstop. The concurrent poller
|
|
1790
|
-
* observes done:true independent of update arrival, flips
|
|
1791
|
-
* completedExternally, and terminate()s the agent — which ends the
|
|
1792
|
-
* in-flight prompt() generator (via return or throw, both handled).
|
|
1793
|
-
*
|
|
1794
|
-
* Generic by design: this module has NO knowledge of what the predicate
|
|
1795
|
-
* reads (e.g. an external reward sink). Back-compat: when absent, NO poller
|
|
1796
|
-
* is started and behavior is unchanged — only `done()` detection drives
|
|
1797
|
-
* completion.
|
|
1798
|
-
*
|
|
1799
|
-
* @param pollIntervalMs Override the concurrent poll cadence (default
|
|
1800
|
-
* 2000ms). Primarily a test seam.
|
|
1801
|
-
*/
|
|
1802
|
-
isComplete?: () => boolean | Promise<boolean>;
|
|
1803
|
-
pollIntervalMs?: number;
|
|
1804
|
-
}
|
|
1805
|
-
): Promise<{
|
|
1806
|
-
doneCalled: boolean;
|
|
1807
|
-
doneStatus?: string;
|
|
1808
|
-
completedExternally: boolean;
|
|
1809
|
-
updates: ExtendedSessionUpdate[];
|
|
1810
|
-
}> {
|
|
1811
|
-
const maxFollowUps = options?.maxFollowUps ?? 2;
|
|
1812
|
-
const pollIntervalMs = options?.pollIntervalMs ?? 2000;
|
|
1813
|
-
const allUpdates: ExtendedSessionUpdate[] = [];
|
|
1814
|
-
let doneCalled = false;
|
|
1815
|
-
let doneStatus: string | undefined;
|
|
1816
|
-
let completedExternally = false;
|
|
1817
|
-
|
|
1818
|
-
let currentMessage = message;
|
|
1819
|
-
|
|
1820
|
-
for (let attempt = 0; attempt <= maxFollowUps; attempt++) {
|
|
1821
|
-
// ── Concurrent completion poller ─────────────────────────────────────
|
|
1822
|
-
// Only armed when the caller supplies a predicate (back-compat: no
|
|
1823
|
-
// predicate → no poller, identical behavior). The live failure mode is a
|
|
1824
|
-
// prompt() stream that has gone SILENT but not CLOSED after the episode
|
|
1825
|
-
// externally completed; the reactive per-update check never fires again
|
|
1826
|
-
// because no updates arrive. This timer re-evaluates isComplete()
|
|
1827
|
-
// independent of update arrival. On true it flips completedExternally and
|
|
1828
|
-
// terminate()s the agent, which ends the in-flight prompt() generator
|
|
1829
|
-
// (return or throw) so the for-await below unblocks. The interval is
|
|
1830
|
-
// always cleared in `finally` so no timer leaks across attempts.
|
|
1831
|
-
let pollTimer: ReturnType<typeof setInterval> | undefined;
|
|
1832
|
-
let polling = false;
|
|
1833
|
-
|
|
1834
|
-
// Resolves the instant completion is detected (external predicate OR the
|
|
1835
|
-
// macro done() tool). The attempt is RACED against this signal so it
|
|
1836
|
-
// returns WITHOUT waiting for the prompt() generator to drain or for
|
|
1837
|
-
// terminate() to finish. The prior design relied on terminate() ending
|
|
1838
|
-
// the silent in-flight stream to unblock the for-await; when that teardown
|
|
1839
|
-
// stalled (e.g. a long, busy real episode whose subprocess is slow to die),
|
|
1840
|
-
// promptUntilDone never returned and the macro session stayed "running"
|
|
1841
|
-
// until the multi-hour hard-timer — the refinement "finalization hang".
|
|
1842
|
-
let signalDone: () => void = () => {};
|
|
1843
|
-
const doneSignal = new Promise<void>((resolve) => {
|
|
1844
|
-
signalDone = resolve;
|
|
1845
|
-
});
|
|
1846
|
-
|
|
1847
|
-
const startPoller = () => {
|
|
1848
|
-
if (!options?.isComplete) return;
|
|
1849
|
-
pollTimer = setInterval(() => {
|
|
1850
|
-
if (polling || completedExternally) return;
|
|
1851
|
-
polling = true;
|
|
1852
|
-
void (async () => {
|
|
1853
|
-
try {
|
|
1854
|
-
if (!completedExternally && options.isComplete && (await options.isComplete())) {
|
|
1855
|
-
completedExternally = true;
|
|
1856
|
-
signalDone();
|
|
1857
|
-
// Tear down the agent/session in the BACKGROUND — never await it
|
|
1858
|
-
// here, so a slow teardown cannot gate the return. Tracked in
|
|
1859
|
-
// pendingTerminations so close() awaits it before shutdown
|
|
1860
|
-
// returns (else a still-racing group-kill is abandoned by a
|
|
1861
|
-
// subsequent process.exit, orphaning the subtree). Best effort.
|
|
1862
|
-
{
|
|
1863
|
-
const t = terminate(agentId, "completed" as any).catch(() => {});
|
|
1864
|
-
pendingTerminations.add(t);
|
|
1865
|
-
void t.finally(() => pendingTerminations.delete(t));
|
|
1866
|
-
}
|
|
1867
|
-
}
|
|
1868
|
-
} catch {
|
|
1869
|
-
// Predicate errored — leave completion to the reactive checks.
|
|
1870
|
-
} finally {
|
|
1871
|
-
polling = false;
|
|
1872
|
-
}
|
|
1873
|
-
})();
|
|
1874
|
-
}, pollIntervalMs);
|
|
1875
|
-
};
|
|
1876
|
-
|
|
1877
|
-
// Consume the stream as a background task so a silent-but-open generator
|
|
1878
|
-
// can be raced against the completion signal below.
|
|
1879
|
-
const consume = (async () => {
|
|
1880
|
-
for await (const update of prompt(agentId, currentMessage)) {
|
|
1881
|
-
allUpdates.push(update);
|
|
1882
|
-
options?.onUpdate?.(update);
|
|
1883
|
-
|
|
1884
|
-
// External completion predicate (reactive fast path).
|
|
1885
|
-
if (options?.isComplete && (await options.isComplete())) {
|
|
1886
|
-
completedExternally = true;
|
|
1887
|
-
signalDone();
|
|
1888
|
-
break;
|
|
1889
|
-
}
|
|
1890
|
-
if (completedExternally) break;
|
|
1891
|
-
|
|
1892
|
-
// Detect done() tool call from session updates.
|
|
1893
|
-
// acp-factory uses { sessionUpdate: "tool_call", title: "mcp__macro-agent__done" }
|
|
1894
|
-
const uAny = update as any;
|
|
1895
|
-
|
|
1896
|
-
// Check title field (primary detection)
|
|
1897
|
-
if (
|
|
1898
|
-
(uAny.sessionUpdate === "tool_call" || uAny.sessionUpdate === "tool_call_update") &&
|
|
1899
|
-
typeof uAny.title === "string" &&
|
|
1900
|
-
uAny.title.endsWith("__done")
|
|
1901
|
-
) {
|
|
1902
|
-
doneCalled = true;
|
|
1903
|
-
// Extract status from rawInput (may arrive across multiple updates —
|
|
1904
|
-
// first update has rawInput={}, subsequent has full input)
|
|
1905
|
-
try {
|
|
1906
|
-
const raw = uAny.rawInput;
|
|
1907
|
-
const input =
|
|
1908
|
-
typeof raw === "string" ? JSON.parse(raw) :
|
|
1909
|
-
typeof raw === "object" ? raw :
|
|
1910
|
-
uAny.input;
|
|
1911
|
-
if (input?.status) {
|
|
1912
|
-
doneStatus = input.status;
|
|
1913
|
-
}
|
|
1914
|
-
} catch {
|
|
1915
|
-
// Best effort — rawInput may not be parseable yet
|
|
1916
|
-
}
|
|
1917
|
-
signalDone();
|
|
1918
|
-
}
|
|
1919
|
-
|
|
1920
|
-
// Fallback: check older format
|
|
1921
|
-
if (
|
|
1922
|
-
uAny.type === "result" &&
|
|
1923
|
-
uAny.subtype === "tool_result" &&
|
|
1924
|
-
uAny.toolName === "done"
|
|
1925
|
-
) {
|
|
1926
|
-
doneCalled = true;
|
|
1927
|
-
doneStatus = uAny.result?.status;
|
|
1928
|
-
signalDone();
|
|
1929
|
-
}
|
|
1930
|
-
}
|
|
1931
|
-
})();
|
|
1932
|
-
// Prevent an unhandled rejection if we return via doneSignal before the
|
|
1933
|
-
// now-orphaned stream throws from a background teardown.
|
|
1934
|
-
consume.catch(() => {});
|
|
1935
|
-
|
|
1936
|
-
try {
|
|
1937
|
-
startPoller();
|
|
1938
|
-
// Return as soon as EITHER the stream drains naturally OR completion is
|
|
1939
|
-
// signalled — whichever comes first. This is the key change: completion
|
|
1940
|
-
// no longer depends on the generator unblocking.
|
|
1941
|
-
await Promise.race([consume, doneSignal]);
|
|
1942
|
-
} catch (err) {
|
|
1943
|
-
// A genuine stream error (not a post-completion teardown) re-throws.
|
|
1944
|
-
if (!completedExternally && !doneCalled) throw err;
|
|
1945
|
-
} finally {
|
|
1946
|
-
// Always clear the concurrent poller so no timer leaks across attempts.
|
|
1947
|
-
if (pollTimer) clearInterval(pollTimer);
|
|
1948
|
-
}
|
|
1949
|
-
|
|
1950
|
-
// Re-check after the stream ends in case completion landed exactly as the
|
|
1951
|
-
// attempt's stream drained (the inner break / poller already set the flag
|
|
1952
|
-
// when it fired mid-stream).
|
|
1953
|
-
if (!completedExternally && options?.isComplete && (await options.isComplete())) {
|
|
1954
|
-
completedExternally = true;
|
|
1955
|
-
}
|
|
1956
|
-
|
|
1957
|
-
if (doneCalled || completedExternally) break;
|
|
1958
|
-
|
|
1959
|
-
if (attempt < maxFollowUps) {
|
|
1960
|
-
currentMessage =
|
|
1961
|
-
"Please call the done() tool to signal that you have completed your work.";
|
|
1962
|
-
}
|
|
1963
|
-
}
|
|
1964
|
-
|
|
1965
|
-
// Auto-terminate when the agent completed — either via the macro done() tool
|
|
1966
|
-
// or via the caller's external completion predicate. This closes the
|
|
1967
|
-
// lifecycle gap: without this, agents stay in "running" state because
|
|
1968
|
-
// nothing triggers terminate(). The external path mirrors the done() path
|
|
1969
|
-
// (terminate as "completed").
|
|
1970
|
-
if (doneCalled) {
|
|
1971
|
-
const reason = doneStatus === "completed" ? "completed" : (doneStatus ?? "failed");
|
|
1972
|
-
try {
|
|
1973
|
-
await terminate(agentId, reason as any);
|
|
1974
|
-
} catch {
|
|
1975
|
-
// Best effort — agent may already be stopping
|
|
1976
|
-
}
|
|
1977
|
-
} else if (completedExternally) {
|
|
1978
|
-
try {
|
|
1979
|
-
await terminate(agentId, "completed" as any);
|
|
1980
|
-
} catch {
|
|
1981
|
-
// Best effort — agent may already be stopping
|
|
1982
|
-
}
|
|
1983
|
-
}
|
|
1984
|
-
|
|
1985
|
-
return { doneCalled, doneStatus, completedExternally, updates: allUpdates };
|
|
1986
|
-
}
|
|
1987
|
-
|
|
1988
|
-
function getSession(agentId: AgentId): Session | null {
|
|
1989
|
-
return activeSessions.get(agentId)?.session ?? null;
|
|
1990
|
-
}
|
|
1991
|
-
|
|
1992
|
-
function hasActiveSession(agentId: AgentId): boolean {
|
|
1993
|
-
return activeSessions.has(agentId);
|
|
1994
|
-
}
|
|
1995
|
-
|
|
1996
|
-
function isPrompting(agentId: AgentId): boolean {
|
|
1997
|
-
return activeSessions.get(agentId)?.isPrompting ?? false;
|
|
1998
|
-
}
|
|
1999
|
-
|
|
2000
|
-
async function supportsInjection(agentId: AgentId): Promise<boolean> {
|
|
2001
|
-
const session = activeSessions.get(agentId)?.session;
|
|
2002
|
-
if (!session) return false;
|
|
2003
|
-
return typeof (session as any).supportsInject === "function"
|
|
2004
|
-
? (session as any).supportsInject()
|
|
2005
|
-
: false;
|
|
2006
|
-
}
|
|
2007
|
-
|
|
2008
|
-
function isProcessRunning(agentId: AgentId): boolean {
|
|
2009
|
-
const session = activeSessions.get(agentId);
|
|
2010
|
-
if (!session) return false;
|
|
2011
|
-
return typeof (session as any).handle?.isRunning === "function"
|
|
2012
|
-
? (session as any).handle.isRunning()
|
|
2013
|
-
: true;
|
|
2014
|
-
}
|
|
2015
|
-
|
|
2016
|
-
function respondToPermission(
|
|
2017
|
-
agentId: AgentId,
|
|
2018
|
-
requestId: string,
|
|
2019
|
-
optionId: string
|
|
2020
|
-
): boolean {
|
|
2021
|
-
const session = activeSessions.get(agentId)?.session;
|
|
2022
|
-
if (!session) return false;
|
|
2023
|
-
return (session as any).respondToPermission?.(requestId, optionId) ?? false;
|
|
2024
|
-
}
|
|
2025
|
-
|
|
2026
|
-
function cancelPermission(agentId: AgentId, requestId: string): boolean {
|
|
2027
|
-
const session = activeSessions.get(agentId)?.session;
|
|
2028
|
-
if (!session) return false;
|
|
2029
|
-
return (session as any).cancelPermission?.(requestId) ?? false;
|
|
2030
|
-
}
|
|
2031
|
-
|
|
2032
|
-
function setPermissionMode(
|
|
2033
|
-
agentId: AgentId,
|
|
2034
|
-
mode: PermissionMode
|
|
2035
|
-
): boolean {
|
|
2036
|
-
const session = activeSessions.get(agentId);
|
|
2037
|
-
if (!session) return false;
|
|
2038
|
-
if (typeof (session.handle as any).setPermissionMode === "function") {
|
|
2039
|
-
(session.handle as any).setPermissionMode(mode);
|
|
2040
|
-
return true;
|
|
2041
|
-
}
|
|
2042
|
-
return false;
|
|
2043
|
-
}
|
|
2044
|
-
|
|
2045
|
-
function getPermissionMode(agentId: AgentId): PermissionMode | null {
|
|
2046
|
-
const session = activeSessions.get(agentId);
|
|
2047
|
-
if (!session) return null;
|
|
2048
|
-
return typeof (session.handle as any).getPermissionMode === "function"
|
|
2049
|
-
? (session.handle as any).getPermissionMode()
|
|
2050
|
-
: null;
|
|
2051
|
-
}
|
|
2052
|
-
|
|
2053
|
-
// ── Lifecycle Callbacks ──────────────────────────────────────
|
|
2054
|
-
|
|
2055
|
-
function onLifecycleEvent(
|
|
2056
|
-
callback: AgentLifecycleCallback
|
|
2057
|
-
): () => void {
|
|
2058
|
-
lifecycleListeners.add(callback);
|
|
2059
|
-
return () => lifecycleListeners.delete(callback);
|
|
2060
|
-
}
|
|
2061
|
-
|
|
2062
|
-
function setSpawnInterceptorFn(
|
|
2063
|
-
interceptor: SpawnInterceptor | null
|
|
2064
|
-
): void {
|
|
2065
|
-
spawnInterceptor = interceptor;
|
|
2066
|
-
}
|
|
2067
|
-
|
|
2068
|
-
function getRoleRegistry(): RoleRegistry {
|
|
2069
|
-
return roleRegistry;
|
|
2070
|
-
}
|
|
2071
|
-
|
|
2072
|
-
// ── Legacy stubs ─────────────────────────────────────────────
|
|
2073
|
-
// These methods exist for backward compatibility but are no-ops
|
|
2074
|
-
// since messaging and task management are now in subsystems.
|
|
2075
|
-
|
|
2076
|
-
function setOpenTasksSocketPath(_socketPath: string): void {
|
|
2077
|
-
// No-op: opentasks client auto-discovers socket
|
|
2078
|
-
}
|
|
2079
|
-
|
|
2080
|
-
function setMapServerUrl(url: string): void {
|
|
2081
|
-
mapServerUrl = url;
|
|
2082
|
-
}
|
|
2083
|
-
|
|
2084
|
-
function setIntegrationConfigs(configs: {
|
|
2085
|
-
minimem?: typeof minimemConfig;
|
|
2086
|
-
skilltree?: typeof skilltreeConfig;
|
|
2087
|
-
sessionlog?: typeof sessionlogConfig;
|
|
2088
|
-
}): void {
|
|
2089
|
-
if (configs.minimem) minimemConfig = configs.minimem;
|
|
2090
|
-
if (configs.skilltree) skilltreeConfig = configs.skilltree;
|
|
2091
|
-
if (configs.sessionlog) sessionlogConfig = configs.sessionlog;
|
|
2092
|
-
}
|
|
2093
|
-
|
|
2094
|
-
function setSkillLoadout(role: string, content: string): void {
|
|
2095
|
-
skillLoadouts.set(role, content);
|
|
2096
|
-
}
|
|
2097
|
-
|
|
2098
|
-
function setSidecar(sidecar: { connected: boolean; reportCheckpoint(cp: any): Promise<any> } | null): void {
|
|
2099
|
-
sidecarRef = sidecar;
|
|
2100
|
-
}
|
|
2101
|
-
|
|
2102
|
-
function setTopologyPolicyFn(
|
|
2103
|
-
policy: import('../workspace/topology/types.js').TopologyPolicy | null
|
|
2104
|
-
): void {
|
|
2105
|
-
topologyPolicy = policy;
|
|
2106
|
-
}
|
|
2107
|
-
|
|
2108
|
-
function setMailServices(): void {
|
|
2109
|
-
// No-op: agent-inbox handles conversation tracking
|
|
2110
|
-
}
|
|
2111
|
-
|
|
2112
|
-
// ── Cleanup ──────────────────────────────────────────────────
|
|
2113
|
-
|
|
2114
|
-
async function close(): Promise<void> {
|
|
2115
|
-
isShuttingDown = true;
|
|
2116
|
-
|
|
2117
|
-
for (const [agentId, session] of activeSessions) {
|
|
2118
|
-
try {
|
|
2119
|
-
await session.handle.close();
|
|
2120
|
-
} catch {
|
|
2121
|
-
/* ignore */
|
|
2122
|
-
}
|
|
2123
|
-
agentStore.updateAgent(agentId, {
|
|
2124
|
-
state: "stopped",
|
|
2125
|
-
stop_reason: "cancelled",
|
|
2126
|
-
stopped_at: Date.now(),
|
|
2127
|
-
});
|
|
2128
|
-
}
|
|
2129
|
-
activeSessions.clear();
|
|
2130
|
-
|
|
2131
|
-
// Await any in-flight fire-and-forget terminations (e.g. the completion
|
|
2132
|
-
// poller's background teardown) so shutdown does not return while a
|
|
2133
|
-
// group-kill is still racing — otherwise a follow-on process.exit abandons
|
|
2134
|
-
// it and orphans the acp/claude/mcp_env subtree. Bounded by a backstop so a
|
|
2135
|
-
// genuinely stuck close() cannot wedge shutdown.
|
|
2136
|
-
if (pendingTerminations.size > 0) {
|
|
2137
|
-
await Promise.race([
|
|
2138
|
-
Promise.allSettled([...pendingTerminations]),
|
|
2139
|
-
new Promise((resolve) => setTimeout(resolve, 6000)),
|
|
2140
|
-
]);
|
|
2141
|
-
}
|
|
2142
|
-
|
|
2143
|
-
agentWorkspaces.clear();
|
|
2144
|
-
lifecycleListeners.clear();
|
|
2145
|
-
|
|
2146
|
-
// Note: isShuttingDown stays true after close() to prevent further spawns
|
|
2147
|
-
}
|
|
2148
|
-
|
|
2149
|
-
// ── Return AgentManager interface ────────────────────────────
|
|
2150
|
-
|
|
2151
|
-
return {
|
|
2152
|
-
spawn,
|
|
2153
|
-
terminate,
|
|
2154
|
-
resume,
|
|
2155
|
-
continueAgent,
|
|
2156
|
-
forkAgent,
|
|
2157
|
-
get,
|
|
2158
|
-
list,
|
|
2159
|
-
getChildren,
|
|
2160
|
-
getHierarchy,
|
|
2161
|
-
getOrCreateHeadManager,
|
|
2162
|
-
listHeadManagers,
|
|
2163
|
-
getActiveAgentSession,
|
|
2164
|
-
prompt,
|
|
2165
|
-
promptUntilDone,
|
|
2166
|
-
getSession,
|
|
2167
|
-
hasActiveSession,
|
|
2168
|
-
isPrompting,
|
|
2169
|
-
supportsInjection,
|
|
2170
|
-
isProcessRunning,
|
|
2171
|
-
respondToPermission,
|
|
2172
|
-
cancelPermission,
|
|
2173
|
-
setPermissionMode,
|
|
2174
|
-
getPermissionMode,
|
|
2175
|
-
onLifecycleEvent,
|
|
2176
|
-
setSpawnInterceptor: setSpawnInterceptorFn,
|
|
2177
|
-
getRoleRegistry,
|
|
2178
|
-
setOpenTasksSocketPath,
|
|
2179
|
-
setMapServerUrl,
|
|
2180
|
-
setIntegrationConfigs,
|
|
2181
|
-
setSkillLoadout,
|
|
2182
|
-
setSidecar,
|
|
2183
|
-
setTopologyPolicy: setTopologyPolicyFn,
|
|
2184
|
-
setMailServices,
|
|
2185
|
-
close,
|
|
2186
|
-
} as AgentManager;
|
|
2187
|
-
}
|