macro-agent 0.0.10 → 0.0.12
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/.macro-agent/teams/self-driving/prompts/grinder.md +27 -0
- package/.macro-agent/teams/self-driving/prompts/judge.md +27 -0
- package/.macro-agent/teams/self-driving/prompts/planner.md +33 -0
- package/.macro-agent/teams/self-driving/roles/grinder.yaml +17 -0
- package/.macro-agent/teams/self-driving/roles/judge.yaml +24 -0
- package/.macro-agent/teams/self-driving/roles/planner.yaml +18 -0
- package/.macro-agent/teams/self-driving/team.yaml +103 -0
- package/.macro-agent/teams/structured/prompts/developer.md +26 -0
- package/.macro-agent/teams/structured/prompts/lead.md +25 -0
- package/.macro-agent/teams/structured/prompts/reviewer.md +24 -0
- package/.macro-agent/teams/structured/roles/developer.yaml +12 -0
- package/.macro-agent/teams/structured/roles/lead.yaml +11 -0
- package/.macro-agent/teams/structured/roles/reviewer.yaml +19 -0
- package/.macro-agent/teams/structured/team.yaml +89 -0
- package/.sudocode/issues.jsonl +56 -51
- package/.sudocode/specs.jsonl +8 -1
- package/CLAUDE.md +121 -30
- package/README.md +60 -3
- package/dist/acp/macro-agent.d.ts +4 -0
- package/dist/acp/macro-agent.d.ts.map +1 -1
- package/dist/acp/macro-agent.js +50 -4
- package/dist/acp/macro-agent.js.map +1 -1
- package/dist/acp/session-mapper.d.ts +20 -1
- package/dist/acp/session-mapper.d.ts.map +1 -1
- package/dist/acp/session-mapper.js +90 -1
- package/dist/acp/session-mapper.js.map +1 -1
- package/dist/acp/types.d.ts +24 -1
- package/dist/acp/types.d.ts.map +1 -1
- package/dist/acp/types.js.map +1 -1
- package/dist/agent/agent-manager.d.ts +40 -1
- package/dist/agent/agent-manager.d.ts.map +1 -1
- package/dist/agent/agent-manager.js +172 -8
- package/dist/agent/agent-manager.js.map +1 -1
- package/dist/agent/types.d.ts +22 -0
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/agent/wake.d.ts +15 -0
- package/dist/agent/wake.d.ts.map +1 -1
- package/dist/agent/wake.js +15 -0
- package/dist/agent/wake.js.map +1 -1
- package/dist/agent-detection/command-builder.d.ts +30 -0
- package/dist/agent-detection/command-builder.d.ts.map +1 -0
- package/dist/agent-detection/command-builder.js +71 -0
- package/dist/agent-detection/command-builder.js.map +1 -0
- package/dist/agent-detection/detector.d.ts +84 -0
- package/dist/agent-detection/detector.d.ts.map +1 -0
- package/dist/agent-detection/detector.js +240 -0
- package/dist/agent-detection/detector.js.map +1 -0
- package/dist/agent-detection/index.d.ts +12 -0
- package/dist/agent-detection/index.d.ts.map +1 -0
- package/dist/agent-detection/index.js +14 -0
- package/dist/agent-detection/index.js.map +1 -0
- package/dist/agent-detection/registry.d.ts +53 -0
- package/dist/agent-detection/registry.d.ts.map +1 -0
- package/dist/agent-detection/registry.js +177 -0
- package/dist/agent-detection/registry.js.map +1 -0
- package/dist/agent-detection/types.d.ts +121 -0
- package/dist/agent-detection/types.d.ts.map +1 -0
- package/dist/agent-detection/types.js +20 -0
- package/dist/agent-detection/types.js.map +1 -0
- package/dist/api/server.d.ts +5 -1
- package/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +362 -0
- package/dist/api/server.js.map +1 -1
- package/dist/api/types.d.ts +50 -1
- package/dist/api/types.d.ts.map +1 -1
- package/dist/cli/acp.d.ts +2 -0
- package/dist/cli/acp.d.ts.map +1 -1
- package/dist/cli/acp.js +8 -1
- package/dist/cli/acp.js.map +1 -1
- package/dist/cli/index.js +29 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp.js +38 -0
- package/dist/cli/mcp.js.map +1 -1
- package/dist/config/index.d.ts +2 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +2 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/project-config.d.ts +46 -0
- package/dist/config/project-config.d.ts.map +1 -0
- package/dist/config/project-config.js +68 -0
- package/dist/config/project-config.js.map +1 -0
- package/dist/lifecycle/cascade.d.ts +1 -1
- package/dist/lifecycle/cascade.d.ts.map +1 -1
- package/dist/lifecycle/handlers/index.d.ts +4 -0
- package/dist/lifecycle/handlers/index.d.ts.map +1 -1
- package/dist/lifecycle/handlers/index.js +2 -0
- package/dist/lifecycle/handlers/index.js.map +1 -1
- package/dist/lifecycle/handlers/worker.d.ts +4 -0
- package/dist/lifecycle/handlers/worker.d.ts.map +1 -1
- package/dist/lifecycle/handlers/worker.js +35 -3
- package/dist/lifecycle/handlers/worker.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.map +1 -1
- package/dist/map/adapter/acp-over-map.js +32 -2
- package/dist/map/adapter/acp-over-map.js.map +1 -1
- package/dist/map/adapter/event-translator.d.ts.map +1 -1
- package/dist/map/adapter/event-translator.js +4 -0
- package/dist/map/adapter/event-translator.js.map +1 -1
- 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/index.d.ts +10 -1
- package/dist/map/adapter/extensions/index.d.ts.map +1 -1
- package/dist/map/adapter/extensions/index.js +39 -0
- package/dist/map/adapter/extensions/index.js.map +1 -1
- 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/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/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 +34 -10
- package/dist/map/adapter/map-adapter.d.ts.map +1 -1
- package/dist/map/adapter/map-adapter.js +110 -14
- package/dist/map/adapter/map-adapter.js.map +1 -1
- package/dist/map/adapter/rpc-handler.d.ts +4 -1
- package/dist/map/adapter/rpc-handler.d.ts.map +1 -1
- package/dist/map/adapter/rpc-handler.js +6 -0
- package/dist/map/adapter/rpc-handler.js.map +1 -1
- package/dist/map/index.d.ts +1 -0
- package/dist/map/index.d.ts.map +1 -1
- package/dist/map/index.js +2 -0
- package/dist/map/index.js.map +1 -1
- package/dist/map/types.d.ts +3 -1
- package/dist/map/types.d.ts.map +1 -1
- package/dist/map/types.js.map +1 -1
- package/dist/mcp/mcp-server.d.ts +6 -0
- package/dist/mcp/mcp-server.d.ts.map +1 -1
- package/dist/mcp/mcp-server.js +45 -0
- package/dist/mcp/mcp-server.js.map +1 -1
- 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.d.ts +15 -2
- package/dist/mcp/tools/done.d.ts.map +1 -1
- package/dist/mcp/tools/done.js +45 -10
- package/dist/mcp/tools/done.js.map +1 -1
- 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/metrics/index.d.ts +2 -0
- package/dist/metrics/index.d.ts.map +1 -0
- package/dist/metrics/index.js +2 -0
- package/dist/metrics/index.js.map +1 -0
- package/dist/metrics/metrics.d.ts +79 -0
- package/dist/metrics/metrics.d.ts.map +1 -0
- package/dist/metrics/metrics.js +166 -0
- package/dist/metrics/metrics.js.map +1 -0
- package/dist/roles/capabilities.d.ts +1 -0
- package/dist/roles/capabilities.d.ts.map +1 -1
- package/dist/roles/capabilities.js +3 -0
- package/dist/roles/capabilities.js.map +1 -1
- package/dist/roles/types.d.ts +1 -1
- package/dist/roles/types.d.ts.map +1 -1
- package/dist/router/channels.d.ts +2 -4
- package/dist/router/channels.d.ts.map +1 -1
- package/dist/router/channels.js.map +1 -1
- package/dist/router/message-router.d.ts +85 -9
- package/dist/router/message-router.d.ts.map +1 -1
- package/dist/router/message-router.js +203 -14
- package/dist/router/message-router.js.map +1 -1
- package/dist/router/role-resolver.d.ts +10 -1
- package/dist/router/role-resolver.d.ts.map +1 -1
- package/dist/router/role-resolver.js +15 -1
- package/dist/router/role-resolver.js.map +1 -1
- package/dist/router/types.d.ts +30 -1
- package/dist/router/types.d.ts.map +1 -1
- package/dist/router/types.js.map +1 -1
- package/dist/server/combined-server.d.ts +6 -0
- package/dist/server/combined-server.d.ts.map +1 -1
- package/dist/server/combined-server.js +24 -2
- package/dist/server/combined-server.js.map +1 -1
- package/dist/store/event-store.d.ts +14 -1
- package/dist/store/event-store.d.ts.map +1 -1
- package/dist/store/event-store.js +456 -4
- package/dist/store/event-store.js.map +1 -1
- package/dist/store/types/agents.d.ts +1 -1
- package/dist/store/types/agents.d.ts.map +1 -1
- 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/events.d.ts +1 -1
- package/dist/store/types/events.d.ts.map +1 -1
- package/dist/store/types/events.js.map +1 -1
- package/dist/store/types/index.d.ts +2 -0
- package/dist/store/types/index.d.ts.map +1 -1
- package/dist/store/types/index.js +2 -0
- package/dist/store/types/index.js.map +1 -1
- 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/tasks.d.ts +2 -0
- package/dist/store/types/tasks.d.ts.map +1 -1
- package/dist/task/backend/memory.d.ts +4 -1
- package/dist/task/backend/memory.d.ts.map +1 -1
- package/dist/task/backend/memory.js +81 -0
- package/dist/task/backend/memory.js.map +1 -1
- package/dist/task/backend/types.d.ts +30 -0
- package/dist/task/backend/types.d.ts.map +1 -1
- package/dist/task/backend/types.js.map +1 -1
- package/dist/teams/index.d.ts +4 -0
- package/dist/teams/index.d.ts.map +1 -0
- package/dist/teams/index.js +4 -0
- package/dist/teams/index.js.map +1 -0
- package/dist/teams/team-loader.d.ts +20 -0
- package/dist/teams/team-loader.d.ts.map +1 -0
- package/dist/teams/team-loader.js +293 -0
- package/dist/teams/team-loader.js.map +1 -0
- package/dist/teams/team-runtime.d.ts +139 -0
- package/dist/teams/team-runtime.d.ts.map +1 -0
- package/dist/teams/team-runtime.js +613 -0
- package/dist/teams/team-runtime.js.map +1 -0
- package/dist/teams/types.d.ts +266 -0
- package/dist/teams/types.d.ts.map +1 -0
- package/dist/teams/types.js +20 -0
- package/dist/teams/types.js.map +1 -0
- package/dist/trigger/router/trigger-router.d.ts +30 -3
- package/dist/trigger/router/trigger-router.d.ts.map +1 -1
- package/dist/trigger/router/trigger-router.js +30 -3
- package/dist/trigger/router/trigger-router.js.map +1 -1
- package/dist/trigger/wake/types.d.ts +31 -5
- package/dist/trigger/wake/types.d.ts.map +1 -1
- package/dist/trigger/wake/types.js +19 -0
- package/dist/trigger/wake/types.js.map +1 -1
- package/dist/workspace/dataplane-adapter.d.ts +1 -1
- package/dist/workspace/dataplane-adapter.d.ts.map +1 -1
- package/dist/workspace/dataplane-adapter.js +1 -1
- package/dist/workspace/dataplane-adapter.js.map +1 -1
- package/dist/workspace/index.d.ts +1 -1
- package/dist/workspace/index.d.ts.map +1 -1
- package/dist/workspace/strategies/index.d.ts +6 -0
- package/dist/workspace/strategies/index.d.ts.map +1 -0
- package/dist/workspace/strategies/index.js +5 -0
- package/dist/workspace/strategies/index.js.map +1 -0
- package/dist/workspace/strategies/optimistic.d.ts +26 -0
- package/dist/workspace/strategies/optimistic.d.ts.map +1 -0
- package/dist/workspace/strategies/optimistic.js +121 -0
- package/dist/workspace/strategies/optimistic.js.map +1 -0
- package/dist/workspace/strategies/queue.d.ts +26 -0
- package/dist/workspace/strategies/queue.d.ts.map +1 -0
- package/dist/workspace/strategies/queue.js +67 -0
- package/dist/workspace/strategies/queue.js.map +1 -0
- package/dist/workspace/strategies/registry.d.ts +37 -0
- package/dist/workspace/strategies/registry.d.ts.map +1 -0
- package/dist/workspace/strategies/registry.js +63 -0
- package/dist/workspace/strategies/registry.js.map +1 -0
- package/dist/workspace/strategies/trunk.d.ts +20 -0
- package/dist/workspace/strategies/trunk.d.ts.map +1 -0
- package/dist/workspace/strategies/trunk.js +108 -0
- package/dist/workspace/strategies/trunk.js.map +1 -0
- package/dist/workspace/strategies/types.d.ts +104 -0
- package/dist/workspace/strategies/types.d.ts.map +1 -0
- package/dist/workspace/strategies/types.js +11 -0
- package/dist/workspace/strategies/types.js.map +1 -0
- package/dist/workspace/types.d.ts +1 -1
- package/dist/workspace/types.d.ts.map +1 -1
- package/dist/workspace/workspace-manager.d.ts +1 -1
- package/dist/workspace/workspace-manager.d.ts.map +1 -1
- package/docs/implementation-details.md +1127 -0
- package/docs/implementation-summary.md +448 -0
- package/docs/mail-integration.md +608 -0
- package/docs/plan-self-driving-support.md +433 -0
- package/docs/spec-self-driving-support.md +462 -0
- package/docs/team-templates.md +860 -0
- package/docs/teams.md +233 -0
- package/package.json +5 -3
- package/src/acp/__tests__/integration.test.ts +161 -1
- package/src/acp/__tests__/macro-agent.test.ts +95 -0
- package/src/acp/__tests__/session-persistence.test.ts +276 -0
- package/src/acp/macro-agent.ts +79 -7
- package/src/acp/session-mapper.ts +108 -1
- package/src/acp/types.ts +33 -1
- package/src/agent/agent-manager.ts +278 -6
- package/src/agent/types.ts +27 -0
- package/src/agent/wake.ts +15 -0
- package/src/agent-detection/__tests__/command-builder.test.ts +336 -0
- package/src/agent-detection/__tests__/detector.test.ts +768 -0
- package/src/agent-detection/__tests__/registry.test.ts +254 -0
- package/src/agent-detection/command-builder.ts +90 -0
- package/src/agent-detection/detector.ts +307 -0
- package/src/agent-detection/index.ts +36 -0
- package/src/agent-detection/registry.ts +200 -0
- package/src/agent-detection/types.ts +184 -0
- package/src/api/__tests__/conversation-api.test.ts +468 -0
- package/src/api/server.ts +425 -1
- package/src/api/types.ts +64 -1
- package/src/cli/acp.ts +9 -1
- package/src/cli/index.ts +44 -0
- package/src/cli/mcp.ts +47 -0
- package/src/config/index.ts +9 -0
- package/src/config/project-config.ts +107 -0
- package/src/lifecycle/cascade.ts +1 -1
- package/src/lifecycle/handlers/index.ts +8 -0
- package/src/lifecycle/handlers/worker.ts +48 -3
- package/src/mail/__tests__/conversation-lifecycle.test.ts +409 -0
- package/src/mail/__tests__/eventstore-stores.test.ts +1073 -0
- package/src/mail/__tests__/mail-full-agent.e2e.test.ts +575 -0
- package/src/mail/__tests__/mail-integration.test.ts +759 -0
- package/src/mail/__tests__/mail-map-protocol.e2e.test.ts +1068 -0
- package/src/mail/__tests__/mail-service.test.ts +506 -0
- package/src/mail/__tests__/turn-recorder.test.ts +328 -0
- package/src/mail/conversation-map.ts +107 -0
- package/src/mail/index.ts +25 -0
- package/src/mail/mail-service.ts +257 -0
- package/src/mail/stores/eventstore-conversation-store.ts +146 -0
- package/src/mail/stores/eventstore-participant-store.ts +172 -0
- package/src/mail/stores/eventstore-thread-store.ts +129 -0
- package/src/mail/stores/eventstore-turn-store.ts +173 -0
- package/src/mail/stores/index.ts +12 -0
- package/src/mail/stores/types.ts +160 -0
- package/src/mail/turn-recorder.ts +124 -0
- package/src/map/README.md +79 -0
- package/src/map/adapter/__tests__/extensions.test.ts +359 -0
- package/src/map/adapter/__tests__/map-adapter.test.ts +90 -0
- package/src/map/adapter/__tests__/workspace-files.test.ts +673 -0
- package/src/map/adapter/acp-over-map.ts +45 -2
- package/src/map/adapter/event-translator.ts +4 -0
- package/src/map/adapter/extensions/agent-detection.ts +201 -0
- package/src/map/adapter/extensions/index.ts +63 -0
- package/src/map/adapter/extensions/resume.ts +114 -0
- package/src/map/adapter/extensions/workspace-files.ts +449 -0
- package/src/map/adapter/mail-handler-adapter.ts +429 -0
- package/src/map/adapter/map-adapter.ts +173 -27
- package/src/map/adapter/rpc-handler.ts +8 -1
- package/src/map/index.ts +3 -0
- package/src/map/types.ts +3 -1
- package/src/mcp/mcp-server.ts +67 -0
- package/src/mcp/tools/claim_task.ts +86 -0
- package/src/mcp/tools/done.ts +59 -10
- package/src/mcp/tools/list_claimable_tasks.ts +93 -0
- package/src/mcp/tools/unclaim_task.ts +71 -0
- package/src/metrics/index.ts +9 -0
- package/src/metrics/metrics.ts +280 -0
- package/src/roles/capabilities.ts +3 -0
- package/src/roles/types.ts +2 -1
- package/src/router/README.md +120 -0
- package/src/router/__tests__/message-router.test.ts +561 -0
- package/src/router/channels.ts +3 -4
- package/src/router/message-router.ts +308 -22
- package/src/router/role-resolver.ts +22 -1
- package/src/router/types.ts +36 -1
- package/src/server/combined-server.ts +36 -2
- package/src/store/README.md +134 -0
- package/src/store/event-store.ts +546 -3
- package/src/store/types/agents.ts +1 -1
- package/src/store/types/conversations.ts +129 -0
- package/src/store/types/events.ts +5 -1
- package/src/store/types/index.ts +2 -0
- package/src/store/types/sessions.ts +53 -0
- package/src/store/types/tasks.ts +3 -0
- package/src/task/backend/memory.ts +116 -0
- package/src/task/backend/types.ts +43 -0
- package/src/teams/__tests__/cross-subsystem.integration.test.ts +983 -0
- package/src/teams/__tests__/e2e/team-runtime.e2e.test.ts +553 -0
- package/src/teams/__tests__/team-system.test.ts +1280 -0
- package/src/teams/index.ts +13 -0
- package/src/teams/team-loader.ts +434 -0
- package/src/teams/team-runtime.ts +727 -0
- package/src/teams/types.ts +377 -0
- package/src/trigger/router/trigger-router.ts +30 -3
- package/src/trigger/wake/types.ts +32 -5
- package/src/trigger/wake/wake-manager.ts +2 -2
- package/src/workspace/dataplane-adapter.ts +1 -1
- package/src/workspace/index.ts +1 -1
- package/src/workspace/strategies/index.ts +18 -0
- package/src/workspace/strategies/optimistic.ts +136 -0
- package/src/workspace/strategies/queue.ts +81 -0
- package/src/workspace/strategies/registry.ts +89 -0
- package/src/workspace/strategies/trunk.ts +123 -0
- package/src/workspace/strategies/types.ts +145 -0
- package/src/workspace/types.ts +1 -1
- package/src/workspace/workspace-manager.ts +1 -1
- package/.claude/settings.local.json +0 -59
- package/dist/map/utils/address-translation.d.ts +0 -99
- package/dist/map/utils/address-translation.d.ts.map +0 -1
- package/dist/map/utils/address-translation.js +0 -285
- package/dist/map/utils/address-translation.js.map +0 -1
- package/dist/map/utils/index.d.ts +0 -7
- package/dist/map/utils/index.d.ts.map +0 -1
- package/dist/map/utils/index.js +0 -7
- package/dist/map/utils/index.js.map +0 -1
- package/openspec/AGENTS.md +0 -456
- package/openspec/changes/archive/2025-12-21-add-mvp-foundation/design.md +0 -128
- package/openspec/changes/archive/2025-12-21-add-mvp-foundation/proposal.md +0 -49
- package/openspec/changes/archive/2025-12-21-add-mvp-foundation/specs/agent-manager/spec.md +0 -150
- package/openspec/changes/archive/2025-12-21-add-mvp-foundation/specs/cli-api/spec.md +0 -258
- package/openspec/changes/archive/2025-12-21-add-mvp-foundation/specs/event-store/spec.md +0 -160
- package/openspec/changes/archive/2025-12-21-add-mvp-foundation/specs/mcp-tools/spec.md +0 -224
- package/openspec/changes/archive/2025-12-21-add-mvp-foundation/specs/message-router/spec.md +0 -153
- package/openspec/changes/archive/2025-12-21-add-mvp-foundation/specs/task-manager/spec.md +0 -136
- package/openspec/changes/archive/2025-12-21-add-mvp-foundation/tasks.md +0 -147
- package/openspec/project.md +0 -31
- package/openspec/specs/agent-manager/spec.md +0 -154
- package/openspec/specs/cli-api/spec.md +0 -262
- package/openspec/specs/event-store/spec.md +0 -164
- package/openspec/specs/mcp-tools/spec.md +0 -228
- package/openspec/specs/message-router/spec.md +0 -157
- package/openspec/specs/task-manager/spec.md +0 -140
- package/references/acp-factory-ref/CHANGELOG.md +0 -33
- package/references/acp-factory-ref/LICENSE +0 -21
- package/references/acp-factory-ref/README.md +0 -341
- package/references/acp-factory-ref/package-lock.json +0 -3102
- package/references/acp-factory-ref/package.json +0 -96
- package/references/acp-factory-ref/python/CHANGELOG.md +0 -33
- package/references/acp-factory-ref/python/LICENSE +0 -21
- package/references/acp-factory-ref/python/Makefile +0 -57
- package/references/acp-factory-ref/python/README.md +0 -253
- package/references/acp-factory-ref/python/pyproject.toml +0 -73
- package/references/acp-factory-ref/python/tests/__init__.py +0 -0
- package/references/acp-factory-ref/python/tests/e2e/__init__.py +0 -1
- package/references/acp-factory-ref/python/tests/e2e/test_codex_e2e.py +0 -349
- package/references/acp-factory-ref/python/tests/e2e/test_gemini_e2e.py +0 -165
- package/references/acp-factory-ref/python/tests/e2e/test_opencode_e2e.py +0 -296
- package/references/acp-factory-ref/python/tests/test_client_handler.py +0 -543
- package/references/acp-factory-ref/python/tests/test_pushable.py +0 -199
- package/references/claude-code-acp/.github/workflows/ci.yml +0 -45
- package/references/claude-code-acp/.github/workflows/publish.yml +0 -34
- package/references/claude-code-acp/.prettierrc.json +0 -4
- package/references/claude-code-acp/CHANGELOG.md +0 -249
- package/references/claude-code-acp/LICENSE +0 -222
- package/references/claude-code-acp/README.md +0 -53
- package/references/claude-code-acp/docs/RELEASES.md +0 -24
- package/references/claude-code-acp/eslint.config.js +0 -48
- package/references/claude-code-acp/package-lock.json +0 -4570
- package/references/claude-code-acp/package.json +0 -88
- package/references/claude-code-acp/scripts/release.sh +0 -119
- package/references/claude-code-acp/src/acp-agent.ts +0 -2065
- package/references/claude-code-acp/src/index.ts +0 -26
- package/references/claude-code-acp/src/lib.ts +0 -38
- package/references/claude-code-acp/src/mcp-server.ts +0 -911
- package/references/claude-code-acp/src/settings.ts +0 -522
- package/references/claude-code-acp/src/tests/.claude/commands/quick-math.md +0 -5
- package/references/claude-code-acp/src/tests/.claude/commands/say-hello.md +0 -6
- package/references/claude-code-acp/src/tests/acp-agent-fork.test.ts +0 -479
- package/references/claude-code-acp/src/tests/acp-agent.test.ts +0 -1502
- package/references/claude-code-acp/src/tests/extract-lines.test.ts +0 -103
- package/references/claude-code-acp/src/tests/fork-session.test.ts +0 -335
- package/references/claude-code-acp/src/tests/replace-and-calculate-location.test.ts +0 -334
- package/references/claude-code-acp/src/tests/settings.test.ts +0 -617
- package/references/claude-code-acp/src/tests/skills-options.test.ts +0 -187
- package/references/claude-code-acp/src/tests/tools.test.ts +0 -318
- package/references/claude-code-acp/src/tests/typescript-declarations.test.ts +0 -558
- package/references/claude-code-acp/src/tools.ts +0 -819
- package/references/claude-code-acp/src/utils.ts +0 -171
- package/references/claude-code-acp/tsconfig.json +0 -18
- package/references/claude-code-acp/vitest.config.ts +0 -19
- package/references/multi-agent-protocol/.sudocode/issues.jsonl +0 -82
- package/references/multi-agent-protocol/.sudocode/specs.jsonl +0 -9
- package/references/multi-agent-protocol/LICENSE +0 -21
- package/references/multi-agent-protocol/README.md +0 -113
- package/references/multi-agent-protocol/docs/00-design-specification.md +0 -460
- package/references/multi-agent-protocol/docs/01-open-questions.md +0 -1050
- package/references/multi-agent-protocol/docs/02-wire-protocol.md +0 -296
- package/references/multi-agent-protocol/docs/03-streaming-semantics.md +0 -252
- package/references/multi-agent-protocol/docs/04-error-handling.md +0 -231
- package/references/multi-agent-protocol/docs/05-connection-model.md +0 -244
- package/references/multi-agent-protocol/docs/06-visibility-permissions.md +0 -243
- package/references/multi-agent-protocol/docs/07-federation.md +0 -259
- package/references/multi-agent-protocol/docs/08-macro-agent-migration.md +0 -253
- package/references/multi-agent-protocol/package-lock.json +0 -3239
- package/references/multi-agent-protocol/package.json +0 -56
- package/references/multi-agent-protocol/schema/meta.json +0 -337
- package/references/multi-agent-protocol/schema/schema.json +0 -1828
|
@@ -0,0 +1,1127 @@
|
|
|
1
|
+
# Implementation Details: Self-Driving Support
|
|
2
|
+
|
|
3
|
+
Detailed implementation plan for each phase, with resolved ambiguities, concrete interface designs, and precise code integration points.
|
|
4
|
+
|
|
5
|
+
Companion to [plan-self-driving-support.md](plan-self-driving-support.md) (high-level plan) and [team-templates.md](team-templates.md) (team template design).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Ambiguities and Design Decisions
|
|
10
|
+
|
|
11
|
+
These are questions that arose when mapping the design documents to the actual codebase. Each is resolved here with rationale.
|
|
12
|
+
|
|
13
|
+
### A1: Where does TeamRuntime sit in the dependency graph?
|
|
14
|
+
|
|
15
|
+
**Problem**: TeamRuntime needs access to RoleRegistry, AgentManager, MessageRouter, TaskBackend, and IntegrationStrategy. But these services are created independently in the CLI (`src/cli/index.ts`) and passed around via dependency injection. TeamRuntime isn't a service — it's a configuration layer that modifies how existing services behave.
|
|
16
|
+
|
|
17
|
+
**Resolution**: TeamRuntime is initialized *after* core services are created but *before* any agents are spawned. It receives service references and configures them:
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
// In CLI start/chat commands (or equivalent bootstrap path):
|
|
21
|
+
const eventStore = await createEventStore({ inMemory: false });
|
|
22
|
+
const messageRouter = createMessageRouter(eventStore);
|
|
23
|
+
const agentManager = createAgentManager(eventStore, messageRouter);
|
|
24
|
+
const taskBackend = createInMemoryTaskBackend(eventStore);
|
|
25
|
+
|
|
26
|
+
// NEW: Load and apply team configuration
|
|
27
|
+
let teamRuntime: TeamRuntime | null = null;
|
|
28
|
+
if (teamName) {
|
|
29
|
+
const manifest = await TeamLoader.load(teamName);
|
|
30
|
+
teamRuntime = new TeamRuntime(manifest, {
|
|
31
|
+
roleRegistry: agentManager.getRoleRegistry(), // exposed getter
|
|
32
|
+
messageRouter,
|
|
33
|
+
taskBackend,
|
|
34
|
+
eventStore,
|
|
35
|
+
});
|
|
36
|
+
await teamRuntime.initialize();
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
TeamRuntime does three things at `initialize()`:
|
|
41
|
+
1. Registers team roles into the RoleRegistry (team layer, highest priority)
|
|
42
|
+
2. Sets up an IntegrationStrategy from the registry
|
|
43
|
+
3. Stores team state (manifest, active strategy, task mode) for later use during spawns
|
|
44
|
+
|
|
45
|
+
TeamRuntime does **not** wrap or replace AgentManager. Instead, it hooks into the spawn flow via a **spawn interceptor** pattern (see A2).
|
|
46
|
+
|
|
47
|
+
### A2: How does TeamRuntime intercept agent spawns?
|
|
48
|
+
|
|
49
|
+
**Problem**: When an agent is spawned within a team, the TeamRuntime needs to:
|
|
50
|
+
- Add topic subscriptions from the communication topology
|
|
51
|
+
- Set up peer routes
|
|
52
|
+
- Inject the role's static prompt
|
|
53
|
+
- Add MCP servers from the team config
|
|
54
|
+
- Set team environment variables
|
|
55
|
+
|
|
56
|
+
Currently, `AgentManager.spawn()` handles all of this. We need team context without a deep refactor.
|
|
57
|
+
|
|
58
|
+
**Resolution**: Add a `spawnInterceptor` hook to AgentManager. The interceptor receives the `SpawnAgentOptions` and returns modified options before the spawn proceeds.
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
// In agent-manager.ts
|
|
62
|
+
type SpawnInterceptor = (options: SpawnAgentOptions) => SpawnAgentOptions | Promise<SpawnAgentOptions>;
|
|
63
|
+
|
|
64
|
+
interface AgentManagerConfig {
|
|
65
|
+
// ... existing fields
|
|
66
|
+
spawnInterceptor?: SpawnInterceptor;
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
TeamRuntime registers itself as the interceptor:
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
// In TeamRuntime.initialize()
|
|
74
|
+
agentManager.setSpawnInterceptor((options) => {
|
|
75
|
+
const roleManifest = this.manifest.roles.find(r => r.name === options.role);
|
|
76
|
+
if (!roleManifest) return options; // Unknown role, pass through
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
...options,
|
|
80
|
+
// Inject team topic subscriptions
|
|
81
|
+
topics: [
|
|
82
|
+
...(options.topics ?? []),
|
|
83
|
+
...this.getTopicsForRole(options.role),
|
|
84
|
+
],
|
|
85
|
+
// Add team MCP servers
|
|
86
|
+
config: {
|
|
87
|
+
...options.config,
|
|
88
|
+
mcpServers: [
|
|
89
|
+
...(options.config?.mcpServers ?? []),
|
|
90
|
+
...this.getMCPServersForRole(options.role),
|
|
91
|
+
],
|
|
92
|
+
env: {
|
|
93
|
+
...options.config?.env,
|
|
94
|
+
MACRO_TEAM_NAME: this.manifest.name,
|
|
95
|
+
MACRO_INTEGRATION_STRATEGY: this.manifest.macro_agent.integration.strategy,
|
|
96
|
+
MACRO_TASK_MODE: this.manifest.macro_agent.task_assignment.mode,
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
// Team's custom prompt replaces the default
|
|
100
|
+
customPrompt: roleManifest.prompt
|
|
101
|
+
? this.loadedPrompts.get(roleManifest.prompt)
|
|
102
|
+
: options.customPrompt,
|
|
103
|
+
};
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The interceptor pattern is minimal — one new optional field on AgentManagerConfig, one check in spawn(). No existing code changes beyond the hook point.
|
|
108
|
+
|
|
109
|
+
**Where the hook goes in spawn()**: After options destructuring (line ~346), before capability checks (line ~379):
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
async function spawn(rawOptions: SpawnAgentOptions): Promise<SpawnedAgent> {
|
|
113
|
+
// Apply spawn interceptor if present
|
|
114
|
+
const options = spawnInterceptor
|
|
115
|
+
? await spawnInterceptor(rawOptions)
|
|
116
|
+
: rawOptions;
|
|
117
|
+
|
|
118
|
+
const { task, task_id, parent, ... } = options;
|
|
119
|
+
// ... rest of spawn continues unchanged
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### A3: How do companion agents work without a parent-child relationship?
|
|
124
|
+
|
|
125
|
+
**Problem**: The self-driving team has a judge companion — a peer of the planner, not its child. But `AgentManager.spawn()` assumes every agent has a parent (except the head manager). Companions need to be discoverable via role addressing (`{ role: "judge" }`) without being in anyone's subtree.
|
|
126
|
+
|
|
127
|
+
**Resolution**: Companions are spawned by the TeamRuntime bootstrap, not by any agent. They use `parent: null` (no parent, like a head manager) but are tagged with the team name for scoping.
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
// In TeamRuntime.bootstrap()
|
|
131
|
+
async bootstrap(): Promise<void> {
|
|
132
|
+
// 1. Spawn root agent
|
|
133
|
+
const root = await this.agentManager.spawn({
|
|
134
|
+
task: `Team root: ${this.manifest.name}`,
|
|
135
|
+
parent: null, // head-like agent
|
|
136
|
+
role: this.manifest.topology.root.role,
|
|
137
|
+
config: this.manifest.topology.root.config,
|
|
138
|
+
});
|
|
139
|
+
this.rootAgentId = root.id;
|
|
140
|
+
|
|
141
|
+
// 2. Spawn companions
|
|
142
|
+
for (const companion of this.manifest.topology.companions ?? []) {
|
|
143
|
+
const agent = await this.agentManager.spawn({
|
|
144
|
+
task: `Companion: ${companion.role}`,
|
|
145
|
+
parent: null, // peer, not child
|
|
146
|
+
role: companion.role,
|
|
147
|
+
config: companion.config,
|
|
148
|
+
});
|
|
149
|
+
this.companionAgentIds.push(agent.id);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// 3. Set up peer subscriptions between root and companions
|
|
153
|
+
// (neither is in the other's subtree, so explicit subscriptions needed)
|
|
154
|
+
for (const peerId of this.companionAgentIds) {
|
|
155
|
+
this.setupPeerSubscriptions(this.rootAgentId, peerId);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Peer discovery**: Companions are discoverable via `{ role: "judge" }` MAP addressing because `setupDefaultSubscriptions` already subscribes agents to their role channel when `role` is provided (line 1078-1081 in message-router.ts). No changes needed for role-based discovery.
|
|
161
|
+
|
|
162
|
+
**Subtree visibility**: Since the judge is not in the planner's subtree, it won't automatically receive worker status updates. The TeamRuntime's spawn interceptor adds explicit topic subscriptions (from the communication topology) to solve this — the judge subscribes to `work_coordination` and `health` topics.
|
|
163
|
+
|
|
164
|
+
### A4: How does IntegrationStrategy inject into the done() handler?
|
|
165
|
+
|
|
166
|
+
**Problem**: The worker done() handler (in `src/lifecycle/handlers/worker.ts`) currently receives `WorkerHandlerDeps` which includes `mergeQueue?`. We need to replace (or augment) this with `integrationStrategy`. But `WorkerHandlerDeps` is used in `AllHandlerDeps` (in `handlers/index.ts`), which is constructed in `createDoneHandler` (in `mcp/tools/done.ts`) from `DoneToolDeps`.
|
|
167
|
+
|
|
168
|
+
The dependency chain is:
|
|
169
|
+
```
|
|
170
|
+
DoneToolDeps → AllHandlerDeps → WorkerHandlerDeps → mergeQueue?
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**Resolution**: Add `integrationStrategy?` to `AllHandlerDeps` (and thus `WorkerHandlerDeps`). The worker handler checks for it first, falls back to mergeQueue:
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
// In handlers/index.ts
|
|
177
|
+
export interface AllHandlerDeps {
|
|
178
|
+
messageRouter: MessageRouter;
|
|
179
|
+
agentManager: AgentManager;
|
|
180
|
+
mergeQueue?: MergeQueueInterface; // existing
|
|
181
|
+
getWorkspacePath?: (agentId: string) => string | undefined;
|
|
182
|
+
integrationStrategy?: IntegrationStrategy; // NEW
|
|
183
|
+
taskMode?: 'push' | 'pull'; // NEW
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
// In handlers/worker.ts, Step 4 (merge/integration)
|
|
189
|
+
if (args.status === 'completed' && context.workspacePath) {
|
|
190
|
+
if (deps.integrationStrategy) {
|
|
191
|
+
// NEW: use pluggable strategy
|
|
192
|
+
const result = await deps.integrationStrategy.land({
|
|
193
|
+
streamId: context.streamId ?? 'default',
|
|
194
|
+
workerBranch: sourceBranch,
|
|
195
|
+
integrationBranch: context.integrationBranch ?? 'main',
|
|
196
|
+
workerAgentId: context.agentId,
|
|
197
|
+
taskId: context.taskId ?? '',
|
|
198
|
+
workspacePath: context.workspacePath,
|
|
199
|
+
});
|
|
200
|
+
// Handle result...
|
|
201
|
+
} else if (deps.mergeQueue) {
|
|
202
|
+
// EXISTING: fall back to merge queue (backward compatible)
|
|
203
|
+
// ... current merge queue logic unchanged ...
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
The `DoneToolDeps` gets a new optional field:
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
export interface DoneToolDeps {
|
|
212
|
+
// ... existing fields
|
|
213
|
+
integrationStrategy?: IntegrationStrategy;
|
|
214
|
+
taskMode?: 'push' | 'pull';
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
And in `createDoneHandler`, it passes through to `AllHandlerDeps`:
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
const handlerDeps: AllHandlerDeps = {
|
|
222
|
+
messageRouter,
|
|
223
|
+
agentManager,
|
|
224
|
+
mergeQueue: workspaceManager?.getMergeQueue?.(),
|
|
225
|
+
getWorkspacePath: ...,
|
|
226
|
+
integrationStrategy: deps.integrationStrategy, // NEW
|
|
227
|
+
taskMode: deps.taskMode, // NEW
|
|
228
|
+
};
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Where does the strategy come from at MCP server creation time?** The `MCPServices` type gets extended with optional team context:
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
// In mcp-server.ts
|
|
235
|
+
interface MCPServices {
|
|
236
|
+
// ... existing fields
|
|
237
|
+
integrationStrategy?: IntegrationStrategy;
|
|
238
|
+
taskMode?: 'push' | 'pull';
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
TeamRuntime sets these on the MCPServices when it's active. When no team is loaded, these are undefined and the existing mergeQueue path is used.
|
|
243
|
+
|
|
244
|
+
### A5: How does the pull-mode done() handler keep agents alive?
|
|
245
|
+
|
|
246
|
+
**Problem**: Currently, `handleWorkerDone` returns `{ shouldTerminate: true }` for completed/failed status. In pull mode, a completed worker should NOT terminate — it should continue its claim loop.
|
|
247
|
+
|
|
248
|
+
**Resolution**: The worker handler checks `deps.taskMode`:
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
// In handlers/worker.ts, final return
|
|
252
|
+
if (deps.taskMode === 'pull' && args.status === 'completed') {
|
|
253
|
+
return {
|
|
254
|
+
shouldTerminate: false, // Worker continues to claim next task
|
|
255
|
+
warnings: handlerWarnings,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return {
|
|
260
|
+
shouldTerminate: args.status === 'completed' || args.status === 'failed',
|
|
261
|
+
warnings: handlerWarnings,
|
|
262
|
+
};
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
The worker agent's prompt (from the team template) instructs it to call `claim_task()` after `done()`. The `done()` tool returning `shouldTerminate: false` means the MCP server stays alive and the agent can continue.
|
|
266
|
+
|
|
267
|
+
**Idle timeout**: The agent is responsible for tracking idle time. If `claim_task()` returns empty repeatedly beyond the configured `idle_timeout_s`, the agent calls `done({ status: "completed", summary: "idle exit" })`. This is conveyed in the injected interaction pattern section of the system prompt (see team-templates.md "Interaction Pattern Injection").
|
|
268
|
+
|
|
269
|
+
### A6: How does `claim_task` work with optimistic locking in InMemoryTaskBackend?
|
|
270
|
+
|
|
271
|
+
**Problem**: `InMemoryTaskBackend` wraps the EventStore. Tasks are materialized views from events. There's no explicit version field for CAS operations. Under concurrency (multiple MCP server processes calling claim), we need atomicity.
|
|
272
|
+
|
|
273
|
+
**Resolution**: The EventStore's SQLite backend serializes writes. Since all task state comes from events, a claim operation is:
|
|
274
|
+
|
|
275
|
+
1. Read task state (from materialized view)
|
|
276
|
+
2. Check status is `pending` or `ready` (claimable)
|
|
277
|
+
3. Emit an `assign` event
|
|
278
|
+
4. The materialized view updates the task to `assigned`
|
|
279
|
+
|
|
280
|
+
Since the EventStore is SQLite-backed and writes are serialized, two concurrent claims for the same task will be ordered — the second one will see the task is already assigned and fail.
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
// In memory.ts
|
|
284
|
+
async claim(agentId: string, filters?: ClaimFilters): Promise<ExtendedTask | null> {
|
|
285
|
+
// 1. Find claimable tasks matching filters
|
|
286
|
+
const candidates = this.eventStore.listTasks({
|
|
287
|
+
status: 'pending',
|
|
288
|
+
...filters,
|
|
289
|
+
}).filter(t => !t.assigned_agent);
|
|
290
|
+
|
|
291
|
+
if (candidates.length === 0) return null;
|
|
292
|
+
|
|
293
|
+
// 2. Pick one (first match, or random for load distribution)
|
|
294
|
+
const target = candidates[0];
|
|
295
|
+
|
|
296
|
+
// 3. Try to assign — this emits an event through SQLite
|
|
297
|
+
// If another process claimed it first, the task status won't be 'pending'
|
|
298
|
+
// and assign() will throw (status transition validation)
|
|
299
|
+
try {
|
|
300
|
+
await this.assign(target.id, agentId);
|
|
301
|
+
await this.start(target.id);
|
|
302
|
+
return this.get(target.id);
|
|
303
|
+
} catch {
|
|
304
|
+
// Another agent claimed it — return null to retry
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
This is not true CAS, but it works because:
|
|
311
|
+
- SQLite serializes writes across processes
|
|
312
|
+
- `assign()` validates status transitions (only `pending` → `assigned` is valid)
|
|
313
|
+
- The window between read and write is small (same process, synchronous event emit)
|
|
314
|
+
- Under contention, the failure mode is "try again" not "corrupt state"
|
|
315
|
+
|
|
316
|
+
For the InMemory backend this is sufficient. If a higher-concurrency backend is needed later (e.g., Sudocode), it can implement true CAS.
|
|
317
|
+
|
|
318
|
+
### A7: How does the system prompt change for team-loaded roles?
|
|
319
|
+
|
|
320
|
+
**Problem**: Currently, `generateSystemPrompt()` in `system-prompt.ts` generates a fixed structure. Team templates provide static prompt files and interaction pattern injections. How do these compose?
|
|
321
|
+
|
|
322
|
+
**Resolution**: Extend `SystemPromptContext` with optional team fields:
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
interface SystemPromptContext {
|
|
326
|
+
// ... existing fields
|
|
327
|
+
teamPrompt?: string; // Static prompt from team template prompts/<role>.md
|
|
328
|
+
interactionPatterns?: string[]; // Auto-injected sections (pull mode, trunk integration, etc.)
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
The prompt assembly order in `generateSystemPrompt()` becomes:
|
|
333
|
+
|
|
334
|
+
```
|
|
335
|
+
1. Identity section ← always (agent ID, task, lineage)
|
|
336
|
+
2. Role section ← existing role guidance OR team prompt (mutually exclusive)
|
|
337
|
+
3. Interaction patterns ← NEW: auto-injected operational sections
|
|
338
|
+
4. MCP tools listing ← always (capability-filtered)
|
|
339
|
+
5. Communication guidelines ← always
|
|
340
|
+
6. Guidelines section ← always (execution, error handling)
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
In `AgentManager.spawn()`, the team prompt replaces the default role section:
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
// Current code (line ~421-424):
|
|
347
|
+
const resolvedRole = roleRegistry.resolveRole(role ?? "worker");
|
|
348
|
+
if (resolvedRole.systemPrompt) {
|
|
349
|
+
systemPrompt += `\n\n# Role-Specific Instructions\n\n${resolvedRole.systemPrompt}`;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// With team support:
|
|
353
|
+
const resolvedRole = roleRegistry.resolveRole(role ?? "worker");
|
|
354
|
+
if (teamPrompt) {
|
|
355
|
+
// Team prompt replaces role.systemPrompt
|
|
356
|
+
systemPrompt += `\n\n# Role Instructions\n\n${teamPrompt}`;
|
|
357
|
+
} else if (resolvedRole.systemPrompt) {
|
|
358
|
+
systemPrompt += `\n\n# Role-Specific Instructions\n\n${resolvedRole.systemPrompt}`;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Append interaction pattern sections
|
|
362
|
+
for (const pattern of interactionPatterns ?? []) {
|
|
363
|
+
systemPrompt += `\n\n${pattern}`;
|
|
364
|
+
}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
The spawn interceptor (A2) provides `teamPrompt` and `interactionPatterns` via the spawn options. This requires adding these fields to `SpawnAgentOptions` and `SystemPromptContext`.
|
|
368
|
+
|
|
369
|
+
### A8: What happens to existing behavior when no team is loaded?
|
|
370
|
+
|
|
371
|
+
**Problem**: We need to ensure that without `--team`, everything works exactly as before.
|
|
372
|
+
|
|
373
|
+
**Resolution**: All new code paths are guarded by optional checks:
|
|
374
|
+
|
|
375
|
+
- No `spawnInterceptor`? Options pass through unchanged.
|
|
376
|
+
- No `integrationStrategy`? Worker handler falls back to `mergeQueue` (existing path).
|
|
377
|
+
- No `taskMode`? Done handler returns `shouldTerminate: true` (existing behavior).
|
|
378
|
+
- No `teamPrompt`? System prompt uses `resolvedRole.systemPrompt` (existing behavior).
|
|
379
|
+
- No team roles registered? RoleRegistry resolves to built-in roles (existing behavior).
|
|
380
|
+
|
|
381
|
+
The `queue` integration strategy wraps the existing merge queue with no behavioral change.
|
|
382
|
+
|
|
383
|
+
### A9: How do signal channels map to topics at runtime?
|
|
384
|
+
|
|
385
|
+
**Problem**: The team manifest defines named channels (`task_updates`, `work_coordination`, `health`) with signal lists. The router has `topic` channels. How do we bridge these?
|
|
386
|
+
|
|
387
|
+
**Resolution**: Each named channel becomes a topic. When the TeamRuntime's spawn interceptor adds topics for a role, it uses the channel names:
|
|
388
|
+
|
|
389
|
+
```yaml
|
|
390
|
+
# In team.yaml
|
|
391
|
+
communication:
|
|
392
|
+
channels:
|
|
393
|
+
task_updates:
|
|
394
|
+
signals: [TASK_CREATED, TASK_COMPLETED, TASK_FAILED]
|
|
395
|
+
subscriptions:
|
|
396
|
+
planner:
|
|
397
|
+
- channel: task_updates
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
Maps to:
|
|
401
|
+
|
|
402
|
+
```typescript
|
|
403
|
+
// In TeamRuntime.getTopicsForRole("planner")
|
|
404
|
+
return ["task_updates"]; // topic name = channel name
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
When an agent emits a signal (e.g., `TASK_CREATED`), it publishes to the channel's topic:
|
|
408
|
+
|
|
409
|
+
```typescript
|
|
410
|
+
// In a future emit_signal MCP tool (or via emit_status with signal details)
|
|
411
|
+
messageRouter.sendToAddress({
|
|
412
|
+
from: agentId,
|
|
413
|
+
to: { scope: "task_updates" }, // ScopeAddress → routes to topic subscribers
|
|
414
|
+
content: JSON.stringify({ signal: "TASK_CREATED", taskId, ... }),
|
|
415
|
+
});
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
**Signal filtering**: For subscriptions with specific signals (e.g., `judge subscribes to task_updates but only TASK_FAILED`), filtering happens at message receive time. The agent receives all messages on the `task_updates` topic, but the MCP tool (or a thin filtering wrapper) only surfaces messages matching the subscribed signals. This avoids adding filtering complexity to the router.
|
|
419
|
+
|
|
420
|
+
Implementation options for filtering:
|
|
421
|
+
1. **Filter in `check_messages`**: When the MCP tool retrieves messages, apply the signal filter from the team manifest. This is simplest.
|
|
422
|
+
2. **Separate topics per signal**: `task_updates.TASK_CREATED`, `task_updates.TASK_FAILED`. More topics but no filtering needed. Verbose.
|
|
423
|
+
3. **Router-level filter**: Add filter predicates to subscriptions. Most powerful but changes the router.
|
|
424
|
+
|
|
425
|
+
**Recommendation**: Option 1 (filter in check_messages) for Phase 1. It's the least invasive. The team manifest's signal filters are stored in TeamRuntime and applied when the agent reads messages. Can upgrade to option 3 later if performance requires it.
|
|
426
|
+
|
|
427
|
+
### A10: Enforcement of emissions — where does it happen?
|
|
428
|
+
|
|
429
|
+
**Problem**: The team manifest declares what signals a role can emit. The current router has no emission restrictions.
|
|
430
|
+
|
|
431
|
+
**Resolution**: Enforcement happens at the MCP tool level, not the router level. When an agent calls `emit_status` (or a future `emit_signal` tool), the tool handler checks the team manifest's `emissions` declaration for the agent's role.
|
|
432
|
+
|
|
433
|
+
```typescript
|
|
434
|
+
// In the emit_status tool handler (or a wrapper)
|
|
435
|
+
if (teamRuntime && enforcement === 'strict') {
|
|
436
|
+
const allowed = teamRuntime.getAllowedEmissions(context.role);
|
|
437
|
+
if (allowed && !allowed.includes(signal)) {
|
|
438
|
+
return { error: `Role ${context.role} cannot emit signal ${signal}` };
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
For `permissive` mode, the check logs a warning but allows the emission. For `audit` mode, it records the emission in the EventStore for later analysis.
|
|
444
|
+
|
|
445
|
+
This keeps the router clean (it doesn't need to know about team-level enforcement) and is easy to implement as a thin layer in the MCP tool.
|
|
446
|
+
|
|
447
|
+
---
|
|
448
|
+
|
|
449
|
+
## Phase 1: Team Template System — Implementation Details
|
|
450
|
+
|
|
451
|
+
### 1.1 TeamManifest Types (`src/teams/types.ts`)
|
|
452
|
+
|
|
453
|
+
```typescript
|
|
454
|
+
// ─────────────────────────────────────────────────────────────
|
|
455
|
+
// Core manifest types (generic, portable)
|
|
456
|
+
// ─────────────────────────────────────────────────────────────
|
|
457
|
+
|
|
458
|
+
export interface TeamManifest {
|
|
459
|
+
/** Team name (directory name) */
|
|
460
|
+
name: string;
|
|
461
|
+
/** Human-readable description */
|
|
462
|
+
description: string;
|
|
463
|
+
/** Schema version */
|
|
464
|
+
version: number;
|
|
465
|
+
|
|
466
|
+
/** Role names used by this team */
|
|
467
|
+
roles: string[];
|
|
468
|
+
|
|
469
|
+
/** Agent spawn topology */
|
|
470
|
+
topology: TeamTopology;
|
|
471
|
+
|
|
472
|
+
/** Communication topology */
|
|
473
|
+
communication: TeamCommunication;
|
|
474
|
+
|
|
475
|
+
/** macro-agent specific extensions */
|
|
476
|
+
macro_agent: MacroAgentExtensions;
|
|
477
|
+
|
|
478
|
+
/** Resolved role definitions (populated by TeamLoader) */
|
|
479
|
+
_resolvedRoles: Map<string, ResolvedTeamRole>;
|
|
480
|
+
/** Loaded prompt contents (populated by TeamLoader) */
|
|
481
|
+
_loadedPrompts: Map<string, string>;
|
|
482
|
+
/** Loaded MCP server configs (populated by TeamLoader) */
|
|
483
|
+
_mcpServers: Map<string, McpServerConfig[]>;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// ─────────────────────────────────────────────────────────────
|
|
487
|
+
// Topology
|
|
488
|
+
// ─────────────────────────────────────────────────────────────
|
|
489
|
+
|
|
490
|
+
export interface TeamTopology {
|
|
491
|
+
/** The initial agent spawned when the team starts */
|
|
492
|
+
root: TopologyNode;
|
|
493
|
+
/** Agents spawned alongside root (peers, not children) */
|
|
494
|
+
companions?: TopologyNode[];
|
|
495
|
+
/** Which roles can spawn which other roles */
|
|
496
|
+
spawn_rules?: Record<string, string[]>;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
export interface TopologyNode {
|
|
500
|
+
role: string;
|
|
501
|
+
prompt?: string; // path to prompt file relative to team dir
|
|
502
|
+
config?: {
|
|
503
|
+
model?: string;
|
|
504
|
+
[key: string]: unknown;
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// ─────────────────────────────────────────────────────────────
|
|
509
|
+
// Communication
|
|
510
|
+
// ─────────────────────────────────────────────────────────────
|
|
511
|
+
|
|
512
|
+
export interface TeamCommunication {
|
|
513
|
+
/** Named signal channels */
|
|
514
|
+
channels?: Record<string, ChannelDefinition>;
|
|
515
|
+
/** Per-role subscription declarations */
|
|
516
|
+
subscriptions?: Record<string, ChannelSubscription[]>;
|
|
517
|
+
/** Per-role emission declarations */
|
|
518
|
+
emissions?: Record<string, string[]>;
|
|
519
|
+
/** Routing configuration */
|
|
520
|
+
routing?: CommunicationRouting;
|
|
521
|
+
/** Enforcement level */
|
|
522
|
+
enforcement?: 'strict' | 'permissive' | 'audit';
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
export interface ChannelDefinition {
|
|
526
|
+
description?: string;
|
|
527
|
+
signals: string[];
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
export interface ChannelSubscription {
|
|
531
|
+
channel: string;
|
|
532
|
+
/** If omitted, subscribes to all signals in the channel */
|
|
533
|
+
signals?: string[];
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
export interface CommunicationRouting {
|
|
537
|
+
/** Status flow direction */
|
|
538
|
+
status?: 'upstream';
|
|
539
|
+
/** Explicit peer connections */
|
|
540
|
+
peers?: PeerConnection[];
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
export interface PeerConnection {
|
|
544
|
+
from: string; // role name
|
|
545
|
+
to: string; // role name
|
|
546
|
+
via: 'direct' | 'topic' | 'scope';
|
|
547
|
+
signals?: string[];
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// ─────────────────────────────────────────────────────────────
|
|
551
|
+
// macro-agent extensions
|
|
552
|
+
// ─────────────────────────────────────────────────────────────
|
|
553
|
+
|
|
554
|
+
export interface MacroAgentExtensions {
|
|
555
|
+
task_assignment?: {
|
|
556
|
+
mode: 'push' | 'pull';
|
|
557
|
+
pull?: {
|
|
558
|
+
idle_timeout_s?: number;
|
|
559
|
+
claim_retry_delay_ms?: number;
|
|
560
|
+
max_concurrent_per_agent?: number;
|
|
561
|
+
};
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
integration?: {
|
|
565
|
+
strategy: string; // 'queue' | 'trunk' | 'optimistic' | custom name
|
|
566
|
+
config?: Record<string, unknown>;
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
lifecycle?: {
|
|
570
|
+
continuations?: {
|
|
571
|
+
enabled: boolean;
|
|
572
|
+
max_history_messages?: number;
|
|
573
|
+
checkpoint_interval?: 'round_trip' | 'none';
|
|
574
|
+
};
|
|
575
|
+
scaling?: {
|
|
576
|
+
min_workers?: number;
|
|
577
|
+
max_workers?: number;
|
|
578
|
+
scale_on?: 'task_queue_depth' | 'manual';
|
|
579
|
+
idle_drain?: boolean;
|
|
580
|
+
};
|
|
581
|
+
};
|
|
582
|
+
|
|
583
|
+
observability?: {
|
|
584
|
+
metrics_window_s?: number;
|
|
585
|
+
snapshot_interval_s?: number;
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// ─────────────────────────────────────────────────────────────
|
|
590
|
+
// Role definition within a team template
|
|
591
|
+
// ─────────────────────────────────────────────────────────────
|
|
592
|
+
|
|
593
|
+
export interface TeamRoleDefinition {
|
|
594
|
+
name: string;
|
|
595
|
+
extends?: string;
|
|
596
|
+
display_name?: string;
|
|
597
|
+
description?: string;
|
|
598
|
+
|
|
599
|
+
/** Full replacement capability list */
|
|
600
|
+
capabilities?: string[];
|
|
601
|
+
|
|
602
|
+
/** Additive/subtractive capabilities (relative to extends) */
|
|
603
|
+
capabilities_add?: string[];
|
|
604
|
+
capabilities_remove?: string[];
|
|
605
|
+
|
|
606
|
+
/** Path to prompt file (relative to team dir) */
|
|
607
|
+
prompt?: string;
|
|
608
|
+
|
|
609
|
+
/** macro-agent specific role config */
|
|
610
|
+
macro_agent?: {
|
|
611
|
+
workspace?: {
|
|
612
|
+
type?: string;
|
|
613
|
+
branch_pattern?: string;
|
|
614
|
+
cleanup_on_terminate?: boolean;
|
|
615
|
+
};
|
|
616
|
+
lifecycle?: {
|
|
617
|
+
type?: 'ephemeral' | 'persistent' | 'daemon' | 'event-driven';
|
|
618
|
+
cascade_terminate?: boolean;
|
|
619
|
+
self_cleanup?: boolean;
|
|
620
|
+
task_bound?: boolean;
|
|
621
|
+
parent_bound?: boolean;
|
|
622
|
+
max_duration_ms?: number;
|
|
623
|
+
};
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/** Role definition with inheritance resolved and capabilities computed */
|
|
628
|
+
export interface ResolvedTeamRole {
|
|
629
|
+
name: string;
|
|
630
|
+
baseRole: string; // The built-in role this extends
|
|
631
|
+
capabilities: string[]; // Final computed capability set
|
|
632
|
+
prompt?: string; // Loaded prompt content
|
|
633
|
+
roleDefinition: import('../roles/types.js').RoleDefinition; // For RoleRegistry
|
|
634
|
+
}
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
### 1.2 TeamLoader (`src/teams/team-loader.ts`)
|
|
638
|
+
|
|
639
|
+
```
|
|
640
|
+
TeamLoader.load(teamName: string, basePath?: string): Promise<TeamManifest>
|
|
641
|
+
|
|
642
|
+
Steps:
|
|
643
|
+
1. Resolve directory: basePath ?? cwd / .macro-agent/teams/<teamName>/
|
|
644
|
+
2. Read and parse team.yaml (use yaml library, already in deps or add)
|
|
645
|
+
3. Validate manifest against TeamManifest schema (Zod validation)
|
|
646
|
+
4. For each role name in manifest.roles:
|
|
647
|
+
a. Check roles/<name>.yaml exists → parse TeamRoleDefinition
|
|
648
|
+
b. If not found, check if it's a built-in role name → use as-is
|
|
649
|
+
c. Resolve extends chain:
|
|
650
|
+
- Load parent role from RoleRegistry
|
|
651
|
+
- Compute final capabilities:
|
|
652
|
+
- If TeamRoleDefinition.capabilities is set → full replacement
|
|
653
|
+
- If .capabilities_add/.capabilities_remove → parent.capabilities + add - remove
|
|
654
|
+
d. Build ResolvedTeamRole with final RoleDefinition
|
|
655
|
+
5. For each prompt reference in topology and roles:
|
|
656
|
+
a. Read prompts/<name>.md → store in _loadedPrompts map
|
|
657
|
+
6. If tools/mcp-servers.json exists:
|
|
658
|
+
a. Parse → store per-role MCP server configs in _mcpServers map
|
|
659
|
+
7. Validate communication topology:
|
|
660
|
+
a. All subscription channel refs exist in channels
|
|
661
|
+
b. All emission signals exist in some channel
|
|
662
|
+
c. All peer connection roles exist in manifest.roles
|
|
663
|
+
8. Return fully resolved TeamManifest
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
**Dependencies**: `js-yaml` for YAML parsing (add to package.json), `zod` for validation (already used).
|
|
667
|
+
|
|
668
|
+
**Error handling**: TeamLoader throws typed errors (`TeamLoadError`) with specific codes: `MANIFEST_NOT_FOUND`, `INVALID_MANIFEST`, `ROLE_NOT_FOUND`, `PROMPT_NOT_FOUND`, `INVALID_COMMUNICATION`.
|
|
669
|
+
|
|
670
|
+
### 1.3 TeamRuntime (`src/teams/team-runtime.ts`)
|
|
671
|
+
|
|
672
|
+
```typescript
|
|
673
|
+
export class TeamRuntime {
|
|
674
|
+
private manifest: TeamManifest;
|
|
675
|
+
private services: TeamServices;
|
|
676
|
+
private integrationStrategy?: IntegrationStrategy;
|
|
677
|
+
private rootAgentId?: string;
|
|
678
|
+
private companionAgentIds: string[] = [];
|
|
679
|
+
|
|
680
|
+
constructor(manifest: TeamManifest, services: TeamServices);
|
|
681
|
+
|
|
682
|
+
/** Wire team config into running services */
|
|
683
|
+
async initialize(): Promise<void>;
|
|
684
|
+
|
|
685
|
+
/** Spawn root + companion agents */
|
|
686
|
+
async bootstrap(): Promise<{ rootId: string; companionIds: string[] }>;
|
|
687
|
+
|
|
688
|
+
/** Tear down team (terminate agents, clean up) */
|
|
689
|
+
async teardown(): Promise<void>;
|
|
690
|
+
|
|
691
|
+
/** Get integration strategy (for DoneToolDeps) */
|
|
692
|
+
getIntegrationStrategy(): IntegrationStrategy | undefined;
|
|
693
|
+
|
|
694
|
+
/** Get task mode (for DoneToolDeps) */
|
|
695
|
+
getTaskMode(): 'push' | 'pull';
|
|
696
|
+
|
|
697
|
+
/** Get topics a role should subscribe to (for spawn interceptor) */
|
|
698
|
+
getTopicsForRole(role: string): string[];
|
|
699
|
+
|
|
700
|
+
/** Get MCP servers for a role (for spawn interceptor) */
|
|
701
|
+
getMCPServersForRole(role: string): McpServerConfig[];
|
|
702
|
+
|
|
703
|
+
/** Get the loaded prompt for a role (for spawn interceptor) */
|
|
704
|
+
getPromptForRole(role: string): string | undefined;
|
|
705
|
+
|
|
706
|
+
/** Get interaction pattern injection sections (for spawn interceptor) */
|
|
707
|
+
getInteractionPatterns(): string[];
|
|
708
|
+
|
|
709
|
+
/** Check if a signal emission is allowed for a role */
|
|
710
|
+
isEmissionAllowed(role: string, signal: string): boolean;
|
|
711
|
+
|
|
712
|
+
/** Get active manifest (for API) */
|
|
713
|
+
getManifest(): TeamManifest;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
interface TeamServices {
|
|
717
|
+
roleRegistry: DefaultRoleRegistry;
|
|
718
|
+
messageRouter: MessageRouter;
|
|
719
|
+
taskBackend: TaskBackend;
|
|
720
|
+
eventStore: EventStore;
|
|
721
|
+
agentManager: AgentManager;
|
|
722
|
+
}
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
### 1.4 SpawnInterceptor in AgentManager
|
|
726
|
+
|
|
727
|
+
Changes to `src/agent/agent-manager.ts`:
|
|
728
|
+
|
|
729
|
+
1. Add `spawnInterceptor?: SpawnInterceptor` to `AgentManagerConfig`
|
|
730
|
+
2. Add `setSpawnInterceptor(fn)` method to AgentManager
|
|
731
|
+
3. In `spawn()`, call interceptor before proceeding (see A2 above)
|
|
732
|
+
4. Add `customPrompt?: string` to `SpawnAgentOptions` for team prompt injection
|
|
733
|
+
5. Expose `getRoleRegistry()` getter for TeamRuntime access
|
|
734
|
+
|
|
735
|
+
### 1.5 CLI Integration
|
|
736
|
+
|
|
737
|
+
Changes to `src/cli/index.ts`:
|
|
738
|
+
|
|
739
|
+
```typescript
|
|
740
|
+
program
|
|
741
|
+
.command("start")
|
|
742
|
+
.option("--team <name>", "Load team template")
|
|
743
|
+
.action(async (options) => {
|
|
744
|
+
const eventStore = await createEventStore({ inMemory: false });
|
|
745
|
+
const messageRouter = createMessageRouter(eventStore);
|
|
746
|
+
const agentManager = createAgentManager(eventStore, messageRouter);
|
|
747
|
+
|
|
748
|
+
// Load team if specified
|
|
749
|
+
let teamRuntime: TeamRuntime | null = null;
|
|
750
|
+
if (options.team) {
|
|
751
|
+
const manifest = await TeamLoader.load(options.team);
|
|
752
|
+
teamRuntime = new TeamRuntime(manifest, {
|
|
753
|
+
roleRegistry: agentManager.getRoleRegistry(),
|
|
754
|
+
messageRouter,
|
|
755
|
+
taskBackend: createInMemoryTaskBackend(eventStore),
|
|
756
|
+
eventStore,
|
|
757
|
+
agentManager,
|
|
758
|
+
});
|
|
759
|
+
await teamRuntime.initialize();
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
// Create API server (pass teamRuntime for API endpoints)
|
|
763
|
+
const server = createAPIServer(
|
|
764
|
+
{ eventStore, agentManager, taskManager, messageRouter, teamRuntime },
|
|
765
|
+
{ port: parseInt(options.port), host: options.host }
|
|
766
|
+
);
|
|
767
|
+
|
|
768
|
+
await server.start();
|
|
769
|
+
|
|
770
|
+
// Bootstrap team agents if team loaded
|
|
771
|
+
if (teamRuntime) {
|
|
772
|
+
const { rootId, companionIds } = await teamRuntime.bootstrap();
|
|
773
|
+
console.log(`Team '${options.team}' started: root=${rootId}, companions=${companionIds.join(', ')}`);
|
|
774
|
+
}
|
|
775
|
+
});
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+
### 1.6 Testing Strategy
|
|
779
|
+
|
|
780
|
+
**Unit tests** (`src/teams/__tests__/`):
|
|
781
|
+
- `team-loader.test.ts`: Parse valid manifest, handle missing files, validate schema, resolve role inheritance, compute capabilities
|
|
782
|
+
- `team-runtime.test.ts`: Role registration, spawn interceptor behavior, topic computation, prompt resolution, integration strategy selection
|
|
783
|
+
|
|
784
|
+
**Integration test** (`src/teams/__tests__/team-integration.test.ts`):
|
|
785
|
+
- Load a fixture team template from `src/teams/__tests__/fixtures/test-team/`
|
|
786
|
+
- Initialize TeamRuntime with real services
|
|
787
|
+
- Spawn an agent and verify: correct topics subscribed, correct MCP servers, correct prompt, correct env vars
|
|
788
|
+
|
|
789
|
+
---
|
|
790
|
+
|
|
791
|
+
## Phase 2: Pluggable Integration Strategies — Implementation Details
|
|
792
|
+
|
|
793
|
+
### New Module: `src/workspace/strategies/`
|
|
794
|
+
|
|
795
|
+
```
|
|
796
|
+
src/workspace/strategies/
|
|
797
|
+
├── types.ts # IntegrationStrategy, LandRequest, LandResult
|
|
798
|
+
├── registry.ts # IntegrationStrategyRegistry
|
|
799
|
+
├── queue.ts # QueueIntegrationStrategy (wraps merge queue)
|
|
800
|
+
├── trunk.ts # TrunkIntegrationStrategy (push + rebase)
|
|
801
|
+
├── optimistic.ts # OptimisticIntegrationStrategy (push + async validate)
|
|
802
|
+
└── index.ts # Re-exports
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
### IntegrationStrategy interface (`types.ts`)
|
|
806
|
+
|
|
807
|
+
As defined in plan-self-driving-support.md. No changes needed.
|
|
808
|
+
|
|
809
|
+
### QueueIntegrationStrategy (`queue.ts`)
|
|
810
|
+
|
|
811
|
+
This wraps the existing merge queue behavior from `handleWorkerDone` Step 4 (worker.ts lines ~229-387). It extracts that logic into the strategy:
|
|
812
|
+
|
|
813
|
+
```typescript
|
|
814
|
+
export class QueueIntegrationStrategy implements IntegrationStrategy {
|
|
815
|
+
readonly name = 'queue';
|
|
816
|
+
private mergeQueue?: MergeQueueInterface;
|
|
817
|
+
|
|
818
|
+
async initialize(streamId: string, config: Record<string, unknown>): Promise<void> {
|
|
819
|
+
// mergeQueue is injected via constructor or config
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
async land(request: LandRequest): Promise<LandResult> {
|
|
823
|
+
if (!this.mergeQueue) {
|
|
824
|
+
return { status: 'failed', error: 'No merge queue configured' };
|
|
825
|
+
}
|
|
826
|
+
// Extract logic from current worker.ts:
|
|
827
|
+
// 1. Detect source/target branch from workspace
|
|
828
|
+
// 2. Submit merge request to queue
|
|
829
|
+
// 3. Return result
|
|
830
|
+
const entry = await this.mergeQueue.submit({
|
|
831
|
+
streamId: request.streamId,
|
|
832
|
+
sourceBranch: request.workerBranch,
|
|
833
|
+
targetBranch: request.integrationBranch,
|
|
834
|
+
agentId: request.workerAgentId,
|
|
835
|
+
taskId: request.taskId,
|
|
836
|
+
});
|
|
837
|
+
return { status: 'landed', mergeCommit: entry.id };
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
```
|
|
841
|
+
|
|
842
|
+
### TrunkIntegrationStrategy (`trunk.ts`)
|
|
843
|
+
|
|
844
|
+
```typescript
|
|
845
|
+
export class TrunkIntegrationStrategy implements IntegrationStrategy {
|
|
846
|
+
readonly name = 'trunk';
|
|
847
|
+
|
|
848
|
+
async land(request: LandRequest): Promise<LandResult> {
|
|
849
|
+
const { maxRetries = 3, conflictAction = 'abandon' } = request.options ?? {};
|
|
850
|
+
|
|
851
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
852
|
+
try {
|
|
853
|
+
// 1. Fetch latest integration branch
|
|
854
|
+
await git.fetch(request.workspacePath, 'origin', request.integrationBranch);
|
|
855
|
+
|
|
856
|
+
// 2. Rebase worker branch onto integration branch
|
|
857
|
+
await git.rebase(request.workspacePath, `origin/${request.integrationBranch}`);
|
|
858
|
+
|
|
859
|
+
// 3. Push to integration branch
|
|
860
|
+
await git.push(request.workspacePath, 'origin', request.integrationBranch);
|
|
861
|
+
|
|
862
|
+
// 4. Get merge commit SHA
|
|
863
|
+
const sha = await git.getHead(request.workspacePath);
|
|
864
|
+
return { status: 'landed', mergeCommit: sha };
|
|
865
|
+
} catch (error) {
|
|
866
|
+
if (isConflictError(error)) {
|
|
867
|
+
await git.rebaseAbort(request.workspacePath);
|
|
868
|
+
if (attempt === maxRetries) {
|
|
869
|
+
const conflictFiles = await git.getConflictFiles(request.workspacePath);
|
|
870
|
+
return {
|
|
871
|
+
status: 'conflict',
|
|
872
|
+
conflictFiles,
|
|
873
|
+
action: conflictAction === 'abandon' ? 'abandoned' : 'queued_for_resolution',
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
// Retry with fresh state
|
|
877
|
+
continue;
|
|
878
|
+
}
|
|
879
|
+
return { status: 'failed', error: String(error) };
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
return { status: 'retry_exhausted', attempts: maxRetries };
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
```
|
|
886
|
+
|
|
887
|
+
**Git operations**: These will use the same git helpers used elsewhere in the codebase (check if `src/workspace/` has git utility functions, otherwise add a thin wrapper around child_process exec of git commands).
|
|
888
|
+
|
|
889
|
+
### IntegrationStrategyRegistry (`registry.ts`)
|
|
890
|
+
|
|
891
|
+
```typescript
|
|
892
|
+
type StrategyFactory = (config: Record<string, unknown>) => IntegrationStrategy;
|
|
893
|
+
|
|
894
|
+
export class IntegrationStrategyRegistry {
|
|
895
|
+
private factories = new Map<string, StrategyFactory>();
|
|
896
|
+
|
|
897
|
+
register(name: string, factory: StrategyFactory): void;
|
|
898
|
+
get(name: string, config?: Record<string, unknown>): IntegrationStrategy;
|
|
899
|
+
has(name: string): boolean;
|
|
900
|
+
list(): string[];
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
// Register built-ins at module load
|
|
904
|
+
export const defaultRegistry = new IntegrationStrategyRegistry();
|
|
905
|
+
defaultRegistry.register('queue', (config) => new QueueIntegrationStrategy(config));
|
|
906
|
+
defaultRegistry.register('trunk', (config) => new TrunkIntegrationStrategy(config));
|
|
907
|
+
defaultRegistry.register('optimistic', (config) => new OptimisticIntegrationStrategy(config));
|
|
908
|
+
```
|
|
909
|
+
|
|
910
|
+
### Worker handler refactor
|
|
911
|
+
|
|
912
|
+
The key change in `src/lifecycle/handlers/worker.ts` is replacing the direct merge queue calls in Step 4 with a strategy dispatch. The existing merge queue logic moves into `QueueIntegrationStrategy`. The handler becomes:
|
|
913
|
+
|
|
914
|
+
```
|
|
915
|
+
Step 1: Commit uncommitted changes (unchanged)
|
|
916
|
+
Step 2: Create checkpoints (unchanged)
|
|
917
|
+
Step 3: Handle blocked/deferred (unchanged)
|
|
918
|
+
Step 4: Emit WORKER_DONE signal (unchanged)
|
|
919
|
+
Step 5: Land changes via strategy (NEW)
|
|
920
|
+
- if integrationStrategy: call strategy.land()
|
|
921
|
+
- else if mergeQueue: existing merge queue logic (backward compat)
|
|
922
|
+
- else: skip integration
|
|
923
|
+
Step 6: Signal descendants (unchanged)
|
|
924
|
+
Step 7: Return shouldTerminate based on taskMode (MODIFIED for pull mode)
|
|
925
|
+
```
|
|
926
|
+
|
|
927
|
+
**Backward compatibility**: When no team is loaded and no integrationStrategy is set, the handler falls back to the existing mergeQueue path. Zero behavior change for existing users.
|
|
928
|
+
|
|
929
|
+
---
|
|
930
|
+
|
|
931
|
+
## Phase 3: Task Pull Model — Implementation Details
|
|
932
|
+
|
|
933
|
+
### TaskBackend interface additions (`src/task/backend/types.ts`)
|
|
934
|
+
|
|
935
|
+
```typescript
|
|
936
|
+
export interface TaskBackend {
|
|
937
|
+
// ... existing methods
|
|
938
|
+
|
|
939
|
+
// NEW: Pull model
|
|
940
|
+
claim(agentId: string, filters?: ClaimFilters): Promise<ExtendedTask | null>;
|
|
941
|
+
unclaim(taskId: TaskId, reason?: string): Promise<void>;
|
|
942
|
+
listClaimable(filters?: ClaimFilters): Promise<ExtendedTask[]>;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
export interface ClaimFilters {
|
|
946
|
+
/** Only tasks with these tags */
|
|
947
|
+
tags?: string[];
|
|
948
|
+
/** Only tasks with these statuses (default: ['pending']) */
|
|
949
|
+
status?: string[];
|
|
950
|
+
/** Exclude tasks that were previously claimed by this agent and failed */
|
|
951
|
+
excludePreviousFails?: boolean;
|
|
952
|
+
}
|
|
953
|
+
```
|
|
954
|
+
|
|
955
|
+
### Tags on tasks (`src/store/types/tasks.ts`)
|
|
956
|
+
|
|
957
|
+
Add `tags?: string[]` to the task type. Tags are set at creation time and used for filtered claiming.
|
|
958
|
+
|
|
959
|
+
### New MCP tools
|
|
960
|
+
|
|
961
|
+
**`src/mcp/tools/claim_task.ts`**:
|
|
962
|
+
```
|
|
963
|
+
claim_task(filters?: { tags?: string[], status?: string[] })
|
|
964
|
+
→ { task: ExtendedTask } | { empty: true, message: "No claimable tasks" }
|
|
965
|
+
```
|
|
966
|
+
|
|
967
|
+
**`src/mcp/tools/unclaim_task.ts`**:
|
|
968
|
+
```
|
|
969
|
+
unclaim_task(task_id: string, reason?: string)
|
|
970
|
+
→ { success: true }
|
|
971
|
+
```
|
|
972
|
+
|
|
973
|
+
**`src/mcp/tools/list_claimable_tasks.ts`**:
|
|
974
|
+
```
|
|
975
|
+
list_claimable_tasks(filters?: { tags?: string[] }, limit?: number)
|
|
976
|
+
→ { tasks: ExtendedTask[] }
|
|
977
|
+
```
|
|
978
|
+
|
|
979
|
+
### Capability gating
|
|
980
|
+
|
|
981
|
+
Add `task.claim` to `CAPABILITY_TOOL_MAP` in `src/roles/capabilities.ts`:
|
|
982
|
+
|
|
983
|
+
```typescript
|
|
984
|
+
'task.claim': ['claim_task', 'unclaim_task', 'list_claimable_tasks'],
|
|
985
|
+
```
|
|
986
|
+
|
|
987
|
+
### MCP registration
|
|
988
|
+
|
|
989
|
+
In `src/mcp/mcp-server.ts`, register the three new tools alongside existing task tools. They follow the same pattern as `create_task` — gated by capability, receive `taskBackend` from MCPServices.
|
|
990
|
+
|
|
991
|
+
---
|
|
992
|
+
|
|
993
|
+
## Phase 4: Session Continuations — Implementation Details
|
|
994
|
+
|
|
995
|
+
### Session history storage
|
|
996
|
+
|
|
997
|
+
Add a new event type: `conversation` events already exist in the EventStore. Session continuations build on the existing conversation/turn infrastructure:
|
|
998
|
+
|
|
999
|
+
- When an agent's `done()` is called with continuation enabled, the agent's conversation transcript is already stored as turns in the EventStore's conversation view.
|
|
1000
|
+
- `AgentManager.resume(agentId)` loads the conversation turns, formats them as a resume context, and spawns a new agent with that context prepended.
|
|
1001
|
+
|
|
1002
|
+
```typescript
|
|
1003
|
+
// In agent-manager.ts
|
|
1004
|
+
async function resume(agentId: string, options?: ResumeOptions): Promise<SpawnedAgent> {
|
|
1005
|
+
const agent = eventStore.getAgent(agentId);
|
|
1006
|
+
if (!agent) throw new Error(`Agent not found: ${agentId}`);
|
|
1007
|
+
|
|
1008
|
+
// Load conversation history
|
|
1009
|
+
const turns = eventStore.listTurns({ agent_id: agentId });
|
|
1010
|
+
const maxMessages = options?.maxMessages ?? 50;
|
|
1011
|
+
const recentTurns = turns.slice(-maxMessages);
|
|
1012
|
+
|
|
1013
|
+
// Build resume context
|
|
1014
|
+
const resumeContext = formatResumeContext(recentTurns, agent);
|
|
1015
|
+
|
|
1016
|
+
// Spawn new agent with same role, task, and resume context
|
|
1017
|
+
return spawn({
|
|
1018
|
+
task: agent.task,
|
|
1019
|
+
task_id: agent.task_id,
|
|
1020
|
+
parent: agent.parent,
|
|
1021
|
+
role: agent.role,
|
|
1022
|
+
customPrompt: resumeContext, // Prepended to system prompt
|
|
1023
|
+
});
|
|
1024
|
+
}
|
|
1025
|
+
```
|
|
1026
|
+
|
|
1027
|
+
This is the simplest approach — no new event types, no new storage. We build on the existing conversation tracking. The main implementation work is:
|
|
1028
|
+
1. Ensuring conversation turns are captured during agent operation (already happening via MailService)
|
|
1029
|
+
2. Building `formatResumeContext()` to create a useful summary
|
|
1030
|
+
3. Adding `resume()` to AgentManager
|
|
1031
|
+
4. Adding periodic checkpointing if turns aren't already captured at each round-trip
|
|
1032
|
+
|
|
1033
|
+
---
|
|
1034
|
+
|
|
1035
|
+
## File Change Summary
|
|
1036
|
+
|
|
1037
|
+
### New files
|
|
1038
|
+
| File | Description |
|
|
1039
|
+
|------|-------------|
|
|
1040
|
+
| `src/teams/types.ts` | TeamManifest, TeamTopology, TeamCommunication, MacroAgentExtensions types |
|
|
1041
|
+
| `src/teams/team-loader.ts` | TeamLoader — reads and validates team template directories |
|
|
1042
|
+
| `src/teams/team-runtime.ts` | TeamRuntime — wires team config into running services |
|
|
1043
|
+
| `src/teams/index.ts` | Re-exports |
|
|
1044
|
+
| `src/workspace/strategies/types.ts` | IntegrationStrategy, LandRequest, LandResult |
|
|
1045
|
+
| `src/workspace/strategies/registry.ts` | IntegrationStrategyRegistry |
|
|
1046
|
+
| `src/workspace/strategies/queue.ts` | QueueIntegrationStrategy |
|
|
1047
|
+
| `src/workspace/strategies/trunk.ts` | TrunkIntegrationStrategy |
|
|
1048
|
+
| `src/workspace/strategies/optimistic.ts` | OptimisticIntegrationStrategy |
|
|
1049
|
+
| `src/workspace/strategies/index.ts` | Re-exports |
|
|
1050
|
+
| `src/mcp/tools/claim_task.ts` | claim_task MCP tool |
|
|
1051
|
+
| `src/mcp/tools/unclaim_task.ts` | unclaim_task MCP tool |
|
|
1052
|
+
| `src/mcp/tools/list_claimable_tasks.ts` | list_claimable_tasks MCP tool |
|
|
1053
|
+
| `.macro-agent/teams/self-driving/` | Reference team template (team.yaml, roles/, prompts/) |
|
|
1054
|
+
| `.macro-agent/teams/structured/` | Backward-compat structured team template |
|
|
1055
|
+
|
|
1056
|
+
### Modified files
|
|
1057
|
+
| File | Change |
|
|
1058
|
+
|------|--------|
|
|
1059
|
+
| `src/agent/agent-manager.ts` | Add spawnInterceptor hook, customPrompt support, getRoleRegistry(), resume() |
|
|
1060
|
+
| `src/agent/system-prompt.ts` | Add teamPrompt and interactionPatterns to SystemPromptContext |
|
|
1061
|
+
| `src/lifecycle/handlers/index.ts` | Add integrationStrategy and taskMode to AllHandlerDeps |
|
|
1062
|
+
| `src/lifecycle/handlers/worker.ts` | Add strategy dispatch (A4), pull mode shouldTerminate (A5) |
|
|
1063
|
+
| `src/mcp/tools/done.ts` | Pass integrationStrategy/taskMode through DoneToolDeps |
|
|
1064
|
+
| `src/mcp/mcp-server.ts` | Register claim_task/unclaim_task/list_claimable_tasks, add MCPServices fields |
|
|
1065
|
+
| `src/task/backend/types.ts` | Add claim(), unclaim(), listClaimable(), ClaimFilters, tags |
|
|
1066
|
+
| `src/task/backend/memory.ts` | Implement claim/unclaim/listClaimable |
|
|
1067
|
+
| `src/store/types/tasks.ts` | Add tags field to task type |
|
|
1068
|
+
| `src/roles/capabilities.ts` | Add task.claim capability mapping |
|
|
1069
|
+
| `src/cli/index.ts` | Add --team flag, TeamLoader/TeamRuntime initialization, bootstrap |
|
|
1070
|
+
| `src/api/server.ts` | Add GET /api/team endpoint |
|
|
1071
|
+
|
|
1072
|
+
---
|
|
1073
|
+
|
|
1074
|
+
## Implementation Order and Dependencies
|
|
1075
|
+
|
|
1076
|
+
```
|
|
1077
|
+
Phase 1 (Foundation)
|
|
1078
|
+
1.1 Types → 1.2 Loader → 1.3 Runtime → 1.4 Spawn interceptor
|
|
1079
|
+
1.5 CLI integration → 1.6 Bootstrap → 1.7 API → 1.8-1.9 Tests → 1.10 Reference template
|
|
1080
|
+
|
|
1081
|
+
Phase 2 (Integration Strategies) ── can start after 1.3
|
|
1082
|
+
2.1 Types → 2.2 Registry → 2.3 Queue → 2.4 Trunk → 2.5 Optimistic
|
|
1083
|
+
2.6 Worker handler refactor → 2.7 Wire into TeamRuntime → 2.8-2.11 Tests
|
|
1084
|
+
|
|
1085
|
+
Phase 3 (Task Pull) ── can start after 1.3
|
|
1086
|
+
3.1 Tags → 3.2-3.4 Backend methods → 3.5 Capabilities → 3.6-3.8 MCP tools
|
|
1087
|
+
3.9 Registration → 3.10 Pull mode done → 3.11 Idle timeout → 3.12-3.13 Tests
|
|
1088
|
+
|
|
1089
|
+
Phase 4 (Session Continuations) ── can start after 1.3
|
|
1090
|
+
4.1-4.2 Storage → 4.3-4.4 Persistence hooks → 4.5 Resume
|
|
1091
|
+
4.6-4.7 Config → 4.8-4.9 Tests
|
|
1092
|
+
|
|
1093
|
+
Phase 5 (Observability) ── depends on 2 + 3
|
|
1094
|
+
5.1-5.2 Metric events → 5.3-5.5 Views → 5.6 API → 5.7-5.8 Tests
|
|
1095
|
+
|
|
1096
|
+
Phase 6 (Templates + Docs) ── depends on all
|
|
1097
|
+
6.1-6.4 Reference templates, docs, E2E test
|
|
1098
|
+
```
|
|
1099
|
+
|
|
1100
|
+
Phases 2, 3, and 4 can be developed in parallel once Phase 1.3 (TeamRuntime) is done, since they each hook into different parts of the system. Phase 5 requires 2 and 3 for the metric events to be meaningful.
|
|
1101
|
+
|
|
1102
|
+
---
|
|
1103
|
+
|
|
1104
|
+
## Resolved Open Questions
|
|
1105
|
+
|
|
1106
|
+
These questions were resolved during spec review. See `docs/spec-self-driving-support.md` for the binding decisions (RD1-RD7).
|
|
1107
|
+
|
|
1108
|
+
1. **YAML library**: `js-yaml` (RD7).
|
|
1109
|
+
|
|
1110
|
+
2. **Git operations for trunk strategy**: Need a thin wrapper around child_process git commands. Check if `src/workspace/` has existing helpers to reuse.
|
|
1111
|
+
|
|
1112
|
+
3. **Optimistic strategy validation**: The strategy is thin — push + emit event. Validation is the judge agent's responsibility via its role prompt (RD5).
|
|
1113
|
+
|
|
1114
|
+
4. **MCP subprocess team context**: Store `team_config` event in EventStore. MCP subprocess reads it to reconstruct strategy and taskMode (RD2). No HTTP API between processes.
|
|
1115
|
+
|
|
1116
|
+
5. **Team selection**: Via `.macro-agent/config.json` (new file), CLI `--team` overrides (RD6).
|
|
1117
|
+
|
|
1118
|
+
6. **spawn_rules vs capabilities**: spawn_rules are syntactic sugar — TeamLoader translates them into capability additions (RD3).
|
|
1119
|
+
|
|
1120
|
+
7. **Team prompt vs base role prompt**: Team prompt replaces base systemPrompt entirely (RD4).
|
|
1121
|
+
|
|
1122
|
+
8. **done() hardcoded roles**: Must be fixed as prerequisite (RD1). Replace with RoleRegistry lookup.
|
|
1123
|
+
|
|
1124
|
+
### Deferred
|
|
1125
|
+
|
|
1126
|
+
- **Team template hot-reloading**: Not needed for Phase 1. The `RoleRegistry` already supports file watching, so this could be added later.
|
|
1127
|
+
- **Multiple simultaneous teams**: Single team focus for now. Architecture supports it but CLI only accepts one `--team` flag.
|