macro-agent 0.0.17 → 0.1.1
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/.claude/settings.local.json +3 -1
- package/.sudocode/specs.jsonl +4 -0
- package/CLAUDE.md +16 -14
- package/README.md +11 -29
- package/dist/acp/macro-agent.d.ts +17 -0
- package/dist/acp/macro-agent.d.ts.map +1 -1
- package/dist/acp/macro-agent.js +183 -55
- package/dist/acp/macro-agent.js.map +1 -1
- package/dist/acp/types.d.ts +32 -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 +65 -1
- package/dist/agent/agent-manager.d.ts.map +1 -1
- package/dist/agent/agent-manager.js +464 -183
- package/dist/agent/agent-manager.js.map +1 -1
- package/dist/agent/types.d.ts +1 -1
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/api/server.d.ts +3 -0
- package/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +37 -6
- package/dist/api/server.js.map +1 -1
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +2 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/token.d.ts +41 -0
- package/dist/auth/token.d.ts.map +1 -0
- package/dist/auth/token.js +73 -0
- package/dist/auth/token.js.map +1 -0
- package/dist/cli/acp.d.ts +2 -23
- package/dist/cli/acp.d.ts.map +1 -1
- package/dist/cli/acp.js +127 -61
- package/dist/cli/acp.js.map +1 -1
- package/dist/cli/index.js +147 -15
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp.d.ts +6 -0
- package/dist/cli/mcp.d.ts.map +1 -1
- package/dist/cli/mcp.js +268 -181
- package/dist/cli/mcp.js.map +1 -1
- package/dist/cli/parse-args.d.ts +20 -0
- package/dist/cli/parse-args.d.ts.map +1 -0
- package/dist/cli/parse-args.js +43 -0
- package/dist/cli/parse-args.js.map +1 -0
- package/dist/cli/stable-instance-id.d.ts +8 -0
- package/dist/cli/stable-instance-id.d.ts.map +1 -0
- package/dist/cli/stable-instance-id.js +14 -0
- package/dist/cli/stable-instance-id.js.map +1 -0
- package/dist/config/project-config.d.ts +74 -7
- package/dist/config/project-config.d.ts.map +1 -1
- package/dist/config/project-config.js +123 -20
- package/dist/config/project-config.js.map +1 -1
- package/dist/map/adapter/acp-over-map.d.ts +23 -0
- package/dist/map/adapter/acp-over-map.d.ts.map +1 -1
- package/dist/map/adapter/acp-over-map.js +482 -55
- package/dist/map/adapter/acp-over-map.js.map +1 -1
- package/dist/map/adapter/connection-manager.d.ts.map +1 -1
- package/dist/map/adapter/connection-manager.js +3 -0
- package/dist/map/adapter/connection-manager.js.map +1 -1
- 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.js +6 -6
- package/dist/map/adapter/event-translator.js.map +1 -1
- 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/index.d.ts +10 -1
- package/dist/map/adapter/extensions/index.d.ts.map +1 -1
- package/dist/map/adapter/extensions/index.js +34 -0
- package/dist/map/adapter/extensions/index.js.map +1 -1
- 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/rename.d.ts +29 -0
- package/dist/map/adapter/extensions/rename.d.ts.map +1 -0
- package/dist/map/adapter/extensions/rename.js +49 -0
- package/dist/map/adapter/extensions/rename.js.map +1 -0
- package/dist/map/adapter/extensions/task.d.ts.map +1 -1
- package/dist/map/adapter/extensions/task.js +10 -0
- package/dist/map/adapter/extensions/task.js.map +1 -1
- 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/index.d.ts +2 -1
- package/dist/map/adapter/index.d.ts.map +1 -1
- package/dist/map/adapter/index.js +8 -2
- package/dist/map/adapter/index.js.map +1 -1
- package/dist/map/adapter/interface.d.ts +2 -0
- package/dist/map/adapter/interface.d.ts.map +1 -1
- package/dist/map/adapter/map-adapter.d.ts +4 -0
- package/dist/map/adapter/map-adapter.d.ts.map +1 -1
- package/dist/map/adapter/map-adapter.js +302 -30
- package/dist/map/adapter/map-adapter.js.map +1 -1
- package/dist/map/adapter/subscription-manager.d.ts.map +1 -1
- package/dist/map/adapter/subscription-manager.js +5 -1
- package/dist/map/adapter/subscription-manager.js.map +1 -1
- package/dist/map/adapter/types.d.ts +2 -0
- package/dist/map/adapter/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 +14 -0
- package/dist/mcp/mcp-server.d.ts.map +1 -1
- package/dist/mcp/mcp-server.js +113 -85
- package/dist/mcp/mcp-server.js.map +1 -1
- package/dist/mcp/types.d.ts +9 -1
- package/dist/mcp/types.d.ts.map +1 -1
- package/dist/mcp/types.js.map +1 -1
- package/dist/metrics/metrics.js +1 -1
- package/dist/metrics/metrics.js.map +1 -1
- package/dist/roles/capabilities.d.ts +3 -1
- package/dist/roles/capabilities.d.ts.map +1 -1
- package/dist/roles/capabilities.js +17 -7
- package/dist/roles/capabilities.js.map +1 -1
- package/dist/roles/config-loader.d.ts +6 -6
- package/dist/roles/config-loader.d.ts.map +1 -1
- package/dist/roles/config-loader.js +6 -6
- package/dist/roles/config-loader.js.map +1 -1
- package/dist/roles/registry.d.ts +2 -2
- package/dist/roles/registry.js +2 -2
- package/dist/server/combined-server.d.ts +20 -0
- package/dist/server/combined-server.d.ts.map +1 -1
- package/dist/server/combined-server.js +107 -8
- package/dist/server/combined-server.js.map +1 -1
- package/dist/store/event-store.d.ts +7 -1
- package/dist/store/event-store.d.ts.map +1 -1
- package/dist/store/event-store.js +91 -8
- package/dist/store/event-store.js.map +1 -1
- package/dist/store/types/agents.d.ts +23 -0
- package/dist/store/types/agents.d.ts.map +1 -1
- package/dist/store/types/events.d.ts +1 -1
- package/dist/store/types/events.d.ts.map +1 -1
- package/dist/task/backend/index.d.ts +47 -29
- package/dist/task/backend/index.d.ts.map +1 -1
- package/dist/task/backend/index.js +109 -71
- package/dist/task/backend/index.js.map +1 -1
- package/dist/task/backend/memory.d.ts +1 -0
- package/dist/task/backend/memory.d.ts.map +1 -1
- package/dist/task/backend/memory.js +3 -0
- package/dist/task/backend/memory.js.map +1 -1
- 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/types.d.ts +33 -53
- package/dist/task/backend/types.d.ts.map +1 -1
- package/dist/task/backend/types.js +7 -11
- package/dist/task/backend/types.js.map +1 -1
- 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/teams/team-loader.d.ts +2 -2
- package/dist/teams/team-loader.js +3 -3
- package/dist/teams/team-loader.js.map +1 -1
- package/dist/teams/team-runtime.d.ts.map +1 -1
- package/dist/teams/team-runtime.js +2 -0
- package/dist/teams/team-runtime.js.map +1 -1
- package/docs/architecture.md +7 -6
- package/docs/configuration.md +26 -62
- package/docs/implementation-details.md +5 -5
- package/docs/implementation-summary.md +17 -17
- package/docs/plan-self-driving-support.md +4 -4
- package/docs/spec-self-driving-support.md +10 -10
- package/docs/team-templates.md +2 -2
- package/docs/teams.md +3 -3
- package/docs/troubleshooting.md +10 -11
- package/package.json +6 -4
- package/src/__tests__/e2e/agent-spawn-visibility.e2e.test.ts +761 -0
- package/src/__tests__/e2e/full-agent-conflict-resolution.e2e.test.ts +2 -2
- package/src/__tests__/e2e/mcp-thin-client-bridge.e2e.test.ts +304 -0
- package/src/__tests__/e2e/mcp-tools-available.e2e.test.ts +324 -0
- package/src/__tests__/e2e/multi-agent.e2e.test.ts +5 -5
- package/src/__tests__/e2e/spawn-session-streaming.e2e.test.ts +563 -0
- package/src/acp/__tests__/history.test.ts +8 -4
- package/src/acp/__tests__/integration.test.ts +56 -31
- package/src/acp/__tests__/macro-agent.test.ts +16 -7
- package/src/acp/macro-agent.ts +230 -62
- package/src/acp/types.ts +46 -1
- package/src/agent/__tests__/agent-manager.test.ts +228 -2
- package/src/agent/agent-manager.ts +714 -261
- package/src/agent/types.ts +3 -1
- package/src/api/server.ts +41 -7
- package/src/auth/__tests__/token.test.ts +100 -0
- package/src/auth/index.ts +1 -0
- package/src/auth/token.ts +82 -0
- package/src/cli/__tests__/acp.test.ts +1 -1
- package/src/cli/__tests__/stable-instance-id.test.ts +1 -1
- package/src/cli/acp.ts +130 -72
- package/src/cli/index.ts +120 -14
- package/src/cli/mcp.ts +311 -207
- package/src/cli/parse-args.ts +54 -0
- package/src/cli/stable-instance-id.ts +14 -0
- package/src/config/project-config.ts +190 -27
- package/src/lifecycle/__tests__/cascade-termination.test.ts +1 -1
- package/src/map/adapter/__tests__/acp-over-map-cancel.test.ts +820 -0
- package/src/map/adapter/__tests__/acp-over-map-getmodels.test.ts +355 -0
- package/src/map/adapter/__tests__/acp-over-map-history.test.ts +724 -2
- package/src/map/adapter/__tests__/acp-over-map-persistence.e2e.test.ts +1 -1
- package/src/map/adapter/__tests__/event-broadcast.test.ts +420 -0
- package/src/map/adapter/__tests__/event-log.test.ts +527 -0
- package/src/map/adapter/__tests__/event-translator.test.ts +3 -3
- package/src/map/adapter/__tests__/extensions.test.ts +408 -0
- package/src/map/adapter/__tests__/map-adapter.test.ts +99 -0
- package/src/map/adapter/__tests__/mcp-bridge.test.ts +1187 -0
- package/src/map/adapter/__tests__/multi-client-broadcast.test.ts +711 -0
- package/src/map/adapter/__tests__/websocket-integration.test.ts +218 -0
- package/src/map/adapter/acp-over-map.ts +777 -92
- package/src/map/adapter/connection-manager.ts +3 -0
- package/src/map/adapter/event-log.ts +208 -0
- package/src/map/adapter/event-translator.ts +6 -6
- package/src/map/adapter/extensions/agent-lifecycle.ts +267 -0
- package/src/map/adapter/extensions/index.ts +60 -0
- package/src/map/adapter/extensions/mcp-bridge.ts +995 -0
- package/src/map/adapter/extensions/task.ts +11 -0
- package/src/map/adapter/extensions/update-metadata.ts +126 -0
- package/src/map/adapter/index.ts +28 -0
- package/src/map/adapter/interface.ts +2 -0
- package/src/map/adapter/map-adapter.ts +373 -38
- package/src/map/adapter/subscription-manager.ts +5 -1
- package/src/map/adapter/types.ts +2 -0
- package/src/mcp/__tests__/map-client.test.ts +386 -0
- package/src/mcp/__tests__/mcp-server-thin-client.test.ts +368 -0
- package/src/mcp/__tests__/mcp-server.test.ts +100 -1
- package/src/mcp/map-client.ts +177 -0
- package/src/mcp/mcp-server.ts +191 -100
- package/src/mcp/types.ts +6 -1
- package/src/metrics/metrics.ts +1 -1
- package/src/monitor/__tests__/stale-agent-flow.integration.test.ts +1 -1
- package/src/roles/__tests__/config-loader.test.ts +7 -7
- package/src/roles/capabilities.ts +17 -7
- package/src/roles/config-loader.ts +6 -6
- package/src/roles/registry.ts +2 -2
- package/src/server/__tests__/combined-server.test.ts +94 -21
- package/src/server/combined-server.ts +189 -33
- package/src/steering/__tests__/steering-integration.test.ts +1 -1
- package/src/store/__tests__/event-store.test.ts +236 -1
- package/src/store/__tests__/instance.test.ts +3 -3
- package/src/store/event-store.ts +109 -8
- package/src/store/types/agents.ts +16 -0
- package/src/store/types/events.ts +1 -1
- package/src/task/backend/__tests__/create-task-backend.test.ts +225 -0
- package/src/task/backend/__tests__/e2e/unified-tool-provider-opentasks.e2e.test.ts +524 -0
- package/src/task/backend/__tests__/unified-tool-provider.test.ts +579 -0
- package/src/task/backend/index.ts +156 -106
- package/src/task/backend/memory.ts +4 -0
- package/src/task/backend/opentasks/__tests__/backend.test.ts +968 -0
- package/src/task/backend/opentasks/__tests__/daemon-manager.test.ts +406 -0
- package/src/task/backend/opentasks/__tests__/mapping.test.ts +84 -0
- package/src/task/backend/opentasks/__tests__/opentasks-backend.e2e.test.ts +1338 -0
- package/src/task/backend/opentasks/backend.ts +1323 -0
- package/src/task/backend/opentasks/client.ts +652 -0
- package/src/task/backend/opentasks/daemon-manager.ts +253 -0
- package/src/task/backend/opentasks/index.ts +69 -0
- package/src/task/backend/opentasks/mapping.ts +94 -0
- package/src/task/backend/types.ts +42 -66
- package/src/task/backend/unified-tool-provider.ts +779 -0
- package/src/teams/__tests__/cross-subsystem.integration.test.ts +1 -1
- package/src/teams/team-loader.ts +3 -3
- package/src/teams/team-runtime.ts +2 -0
- package/test_fixtures/README.md +2 -3
- package/test_fixtures/fixtures/index.ts +0 -3
- package/test_fixtures/fixtures/projects/project-with-specs.ts +7 -149
- package/test_fixtures/fixtures/repos/index.ts +1 -3
- package/test_fixtures/fixtures/repos/temp-repo-factory.ts +0 -116
- package/test_fixtures/fixtures/repos/types.ts +0 -11
- package/test_fixtures/harness/__tests__/fixtures.test.ts +10 -102
- package/test_fixtures/harness/__tests__/temp-repo-and-simulator.test.ts +0 -33
- package/test_fixtures/harness/simulator/agent-simulator.ts +4 -4
- package/vitest.config.ts +1 -1
- package/vitest.e2e.config.ts +1 -1
- package/vitest.setup.ts +1 -30
- package/.macro-agent/teams/self-driving/prompts/grinder.md +0 -27
- package/.macro-agent/teams/self-driving/prompts/judge.md +0 -27
- package/.macro-agent/teams/self-driving/prompts/planner.md +0 -33
- package/.macro-agent/teams/self-driving/roles/grinder.yaml +0 -17
- package/.macro-agent/teams/self-driving/roles/judge.yaml +0 -24
- package/.macro-agent/teams/self-driving/roles/planner.yaml +0 -18
- package/.macro-agent/teams/self-driving/team.yaml +0 -103
- package/.macro-agent/teams/structured/prompts/developer.md +0 -26
- package/.macro-agent/teams/structured/prompts/lead.md +0 -25
- package/.macro-agent/teams/structured/prompts/reviewer.md +0 -24
- package/.macro-agent/teams/structured/roles/developer.yaml +0 -12
- package/.macro-agent/teams/structured/roles/lead.yaml +0 -11
- package/.macro-agent/teams/structured/roles/reviewer.yaml +0 -19
- package/.macro-agent/teams/structured/team.yaml +0 -89
- package/docs/sudocode-integration.md +0 -383
- package/src/task/backend/__tests__/backend-parity.test.ts +0 -451
- package/src/task/backend/__tests__/tool-provider-edge-cases.test.ts +0 -430
- package/src/task/backend/__tests__/tool-provider.test.ts +0 -983
- package/src/task/backend/sudocode/__tests__/backend-edge-cases.test.ts +0 -575
- package/src/task/backend/sudocode/__tests__/backend.test.ts +0 -1194
- package/src/task/backend/sudocode/__tests__/client-integration.test.ts +0 -418
- package/src/task/backend/sudocode/__tests__/client.test.ts +0 -345
- package/src/task/backend/sudocode/__tests__/e2e/backend.e2e.test.ts +0 -753
- package/src/task/backend/sudocode/__tests__/e2e/server-client.e2e.test.ts +0 -680
- package/src/task/backend/sudocode/__tests__/e2e-workflow.test.ts +0 -666
- package/src/task/backend/sudocode/__tests__/integration/standalone-client.integration.test.ts +0 -396
- package/src/task/backend/sudocode/__tests__/integration/sudocode-cli.integration.test.ts +0 -328
- package/src/task/backend/sudocode/__tests__/integration/test-utils.ts +0 -175
- package/src/task/backend/sudocode/__tests__/mapping-edge-cases.test.ts +0 -265
- package/src/task/backend/sudocode/__tests__/server-client.test.ts +0 -675
- package/src/task/backend/sudocode/__tests__/sync-policy-edge-cases.test.ts +0 -521
- package/src/task/backend/sudocode/__tests__/sync-policy.test.ts +0 -519
- package/src/task/backend/sudocode/__tests__/tools.test.ts +0 -471
- package/src/task/backend/sudocode/backend.ts +0 -1237
- package/src/task/backend/sudocode/client.ts +0 -515
- package/src/task/backend/sudocode/index.ts +0 -120
- package/src/task/backend/sudocode/mapping.ts +0 -93
- package/src/task/backend/sudocode/server-client.ts +0 -522
- package/src/task/backend/sudocode/standalone-client.ts +0 -623
- package/src/task/backend/sudocode/sync-policy.ts +0 -387
- package/src/task/backend/sudocode/tools.ts +0 -896
- package/src/task/backend/tool-provider.ts +0 -506
- package/test_fixtures/fixtures/sudocode/index.ts +0 -29
- package/test_fixtures/fixtures/sudocode/issues.ts +0 -185
- package/test_fixtures/fixtures/sudocode/specs.ts +0 -159
|
@@ -9,6 +9,21 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { SessionMapper } from "../../acp/session-mapper.js";
|
|
11
11
|
// ─────────────────────────────────────────────────────────────────
|
|
12
|
+
// Helpers
|
|
13
|
+
// ─────────────────────────────────────────────────────────────────
|
|
14
|
+
/** Extract a plain-text output string from `rawOutput` (string | ContentBlock[] | undefined). */
|
|
15
|
+
function extractToolOutput(rawOutput) {
|
|
16
|
+
if (typeof rawOutput === "string")
|
|
17
|
+
return rawOutput;
|
|
18
|
+
if (Array.isArray(rawOutput)) {
|
|
19
|
+
return (rawOutput
|
|
20
|
+
.filter((item) => item.type === "text" && typeof item.text === "string")
|
|
21
|
+
.map((item) => item.text)
|
|
22
|
+
.join("\n") || undefined);
|
|
23
|
+
}
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
// ─────────────────────────────────────────────────────────────────
|
|
12
27
|
// ACP-over-MAP Handler
|
|
13
28
|
// ─────────────────────────────────────────────────────────────────
|
|
14
29
|
export class ACPOverMAPHandler {
|
|
@@ -16,15 +31,52 @@ export class ACPOverMAPHandler {
|
|
|
16
31
|
eventStore;
|
|
17
32
|
taskManager;
|
|
18
33
|
defaultCwd;
|
|
34
|
+
onAgentRegistered;
|
|
19
35
|
/** Stream states by streamId */
|
|
20
36
|
streams = new Map();
|
|
21
37
|
/** Session mapper for ACP session -> Agent mapping */
|
|
22
|
-
sessionMapper
|
|
38
|
+
sessionMapper;
|
|
23
39
|
constructor(config) {
|
|
24
40
|
this.agentManager = config.agentManager;
|
|
25
41
|
this.eventStore = config.eventStore;
|
|
26
42
|
this.taskManager = config.taskManager;
|
|
27
43
|
this.defaultCwd = config.defaultCwd ?? process.cwd();
|
|
44
|
+
this.onAgentRegistered = config.onAgentRegistered;
|
|
45
|
+
// Initialize session mapper with EventStore for persistence and recovery
|
|
46
|
+
this.sessionMapper = new SessionMapper(this.eventStore);
|
|
47
|
+
const recovered = this.sessionMapper.recoverFromStore();
|
|
48
|
+
if (recovered > 0) {
|
|
49
|
+
console.error(`[ACP-over-MAP] Recovered ${recovered} session(s) from store`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Notify subscribers that a new agent was registered.
|
|
54
|
+
* Looks up agent details from EventStore and calls the onAgentRegistered callback.
|
|
55
|
+
*/
|
|
56
|
+
notifyAgentRegistered(agentId) {
|
|
57
|
+
if (!this.onAgentRegistered)
|
|
58
|
+
return;
|
|
59
|
+
const agent = this.eventStore.getAgent(agentId);
|
|
60
|
+
this.onAgentRegistered({
|
|
61
|
+
id: agentId,
|
|
62
|
+
name: agent?.name,
|
|
63
|
+
role: agent?.role,
|
|
64
|
+
parent: agent?.parent ?? undefined,
|
|
65
|
+
metadata: agent?.metadata,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Abort all active streams targeting a specific agent.
|
|
70
|
+
* Called when an agent is stopped via MAP protocol to interrupt
|
|
71
|
+
* any in-progress ACP prompt streaming.
|
|
72
|
+
*/
|
|
73
|
+
abortStreamsForAgent(agentId) {
|
|
74
|
+
for (const [streamId, streamState] of this.streams) {
|
|
75
|
+
if (streamState.agentId === agentId) {
|
|
76
|
+
console.error(`[ACP-over-MAP] Aborting stream ${streamId} for stopped agent ${agentId}`);
|
|
77
|
+
streamState.abortController.abort();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
28
80
|
}
|
|
29
81
|
/**
|
|
30
82
|
* Process an ACP request and return the response.
|
|
@@ -108,6 +160,14 @@ export class ACPOverMAPHandler {
|
|
|
108
160
|
throw new Error("Stream already initialized");
|
|
109
161
|
}
|
|
110
162
|
streamState.initialized = true;
|
|
163
|
+
// Extract permission mode from _meta.macroConfig if provided
|
|
164
|
+
const meta = params?._meta;
|
|
165
|
+
const macroConfig = meta?.macroConfig;
|
|
166
|
+
const defaultSubAgentConfig = macroConfig?.defaultSubAgentConfig;
|
|
167
|
+
if (defaultSubAgentConfig?.permissionMode) {
|
|
168
|
+
streamState.permissionMode =
|
|
169
|
+
defaultSubAgentConfig.permissionMode;
|
|
170
|
+
}
|
|
111
171
|
return {
|
|
112
172
|
protocolVersion: 1,
|
|
113
173
|
agentCapabilities: {
|
|
@@ -134,6 +194,7 @@ export class ACPOverMAPHandler {
|
|
|
134
194
|
const spawned = await this.agentManager.getOrCreateHeadManager({
|
|
135
195
|
cwd: workingDir,
|
|
136
196
|
forceNew: true,
|
|
197
|
+
permissionMode: streamState.permissionMode,
|
|
137
198
|
});
|
|
138
199
|
const sessionId = spawned.session_id;
|
|
139
200
|
streamState.sessionId = sessionId;
|
|
@@ -141,6 +202,8 @@ export class ACPOverMAPHandler {
|
|
|
141
202
|
// Create session mapping
|
|
142
203
|
this.sessionMapper.createMapping(sessionId, spawned.id);
|
|
143
204
|
console.error(`[ACP-over-MAP] Created session ${sessionId} -> agent ${spawned.id}`);
|
|
205
|
+
// Notify subscribers that a new agent was registered
|
|
206
|
+
this.notifyAgentRegistered(spawned.id);
|
|
144
207
|
// Emit session_info_update so client has title/timestamps
|
|
145
208
|
this.emitSessionInfo(streamState, sessionId, emitNotification);
|
|
146
209
|
return { sessionId };
|
|
@@ -149,7 +212,7 @@ export class ACPOverMAPHandler {
|
|
|
149
212
|
if (!streamState.initialized) {
|
|
150
213
|
throw new Error("Must call initialize before loadSession");
|
|
151
214
|
}
|
|
152
|
-
const { sessionId: rawSessionId, cwd, _meta } = params ?? {};
|
|
215
|
+
const { sessionId: rawSessionId, cwd, _meta, } = params ?? {};
|
|
153
216
|
if (!rawSessionId) {
|
|
154
217
|
throw new Error("sessionId required");
|
|
155
218
|
}
|
|
@@ -163,6 +226,32 @@ export class ACPOverMAPHandler {
|
|
|
163
226
|
}
|
|
164
227
|
sessionId = agent.session_id;
|
|
165
228
|
console.error(`[ACP-over-MAP] loadSession: Resolved agentId ${metaAgentId} to session ${sessionId}`);
|
|
229
|
+
// Handle the agent directly — works for both head managers and sub-agents.
|
|
230
|
+
// The previous code only searched listHeadManagers(), so sub-agents
|
|
231
|
+
// were never found and a new head manager was created instead.
|
|
232
|
+
if (this.agentManager.hasActiveSession(metaAgentId)) {
|
|
233
|
+
console.error(`[ACP-over-MAP] loadSession: Reusing active session for agent ${metaAgentId}`);
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
// Agent exists but no active session — resume it
|
|
237
|
+
console.error(`[ACP-over-MAP] loadSession: Resuming agent ${metaAgentId}`);
|
|
238
|
+
try {
|
|
239
|
+
await this.agentManager.resume(metaAgentId, streamState.permissionMode);
|
|
240
|
+
}
|
|
241
|
+
catch (resumeErr) {
|
|
242
|
+
// ALREADY_RUNNING can happen in a race — safe to ignore
|
|
243
|
+
const code = resumeErr.code;
|
|
244
|
+
if (code !== "ALREADY_RUNNING") {
|
|
245
|
+
throw resumeErr;
|
|
246
|
+
}
|
|
247
|
+
console.error(`[ACP-over-MAP] loadSession: Agent ${metaAgentId} already running (race), continuing`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
streamState.sessionId = sessionId;
|
|
251
|
+
streamState.agentId = metaAgentId;
|
|
252
|
+
this.sessionMapper.createMapping(sessionId, metaAgentId);
|
|
253
|
+
this.emitSessionInfo(streamState, sessionId, emitNotification);
|
|
254
|
+
return { sessionId };
|
|
166
255
|
}
|
|
167
256
|
const workingDir = cwd ?? this.defaultCwd;
|
|
168
257
|
// Try to find an existing head manager with this session ID
|
|
@@ -180,7 +269,7 @@ export class ACPOverMAPHandler {
|
|
|
180
269
|
}
|
|
181
270
|
// Agent exists but no active session - resume it
|
|
182
271
|
console.error(`[ACP-over-MAP] loadSession: Resuming stopped agent ${existing.id}`);
|
|
183
|
-
const spawned = await this.agentManager.resume(existing.id);
|
|
272
|
+
const spawned = await this.agentManager.resume(existing.id, streamState.permissionMode);
|
|
184
273
|
streamState.sessionId = sessionId;
|
|
185
274
|
streamState.agentId = spawned.id;
|
|
186
275
|
this.sessionMapper.createMapping(sessionId, spawned.id);
|
|
@@ -192,10 +281,13 @@ export class ACPOverMAPHandler {
|
|
|
192
281
|
const spawned = await this.agentManager.getOrCreateHeadManager({
|
|
193
282
|
cwd: workingDir,
|
|
194
283
|
sessionId,
|
|
284
|
+
permissionMode: streamState.permissionMode,
|
|
195
285
|
});
|
|
196
286
|
streamState.sessionId = sessionId;
|
|
197
287
|
streamState.agentId = spawned.id;
|
|
198
288
|
this.sessionMapper.createMapping(sessionId, spawned.id);
|
|
289
|
+
// Notify subscribers that a new agent was registered
|
|
290
|
+
this.notifyAgentRegistered(spawned.id);
|
|
199
291
|
this.emitSessionInfo(streamState, sessionId, emitNotification);
|
|
200
292
|
return { sessionId };
|
|
201
293
|
}
|
|
@@ -264,9 +356,13 @@ export class ACPOverMAPHandler {
|
|
|
264
356
|
this.ensureConversation(sessionId, agentId);
|
|
265
357
|
// Accumulate response content for history recording
|
|
266
358
|
const buffer = {
|
|
267
|
-
|
|
268
|
-
toolCalls: [],
|
|
359
|
+
parts: [],
|
|
269
360
|
};
|
|
361
|
+
// Track latest plan for persistence
|
|
362
|
+
let latestPlan = null;
|
|
363
|
+
// Track tool info from initial tool_call events (title, name, input)
|
|
364
|
+
// so we can merge them when tool_call_update arrives with status "completed"
|
|
365
|
+
const toolInfoCache = new Map();
|
|
270
366
|
try {
|
|
271
367
|
// Stream responses from the agent
|
|
272
368
|
let updateCount = 0;
|
|
@@ -275,24 +371,57 @@ export class ACPOverMAPHandler {
|
|
|
275
371
|
if (streamState.abortController.signal.aborted) {
|
|
276
372
|
return { stopReason: "cancelled" };
|
|
277
373
|
}
|
|
278
|
-
// Accumulate content for history persistence
|
|
374
|
+
// Accumulate content for history persistence (preserving text/tool interleaving order)
|
|
279
375
|
const u = update;
|
|
280
376
|
const updateType = u.sessionUpdate ?? u.type;
|
|
377
|
+
// Annotate permission_request updates with agentId so clients can respond
|
|
378
|
+
if (updateType === "permission_request") {
|
|
379
|
+
u._agentId = agentId;
|
|
380
|
+
}
|
|
281
381
|
if (updateType === "agent_message_chunk") {
|
|
282
382
|
const content = u.content;
|
|
283
383
|
if (content?.text) {
|
|
284
|
-
buffer.
|
|
384
|
+
const last = buffer.parts[buffer.parts.length - 1];
|
|
385
|
+
if (last && last.type === "text") {
|
|
386
|
+
last.text += content.text;
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
buffer.parts.push({ type: "text", text: content.text });
|
|
390
|
+
}
|
|
285
391
|
}
|
|
286
392
|
}
|
|
287
|
-
else if (updateType === "
|
|
393
|
+
else if (updateType === "plan") {
|
|
394
|
+
const entries = u.entries;
|
|
395
|
+
if (entries) {
|
|
396
|
+
latestPlan = entries;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
else if (updateType === "tool_call" ||
|
|
400
|
+
updateType === "tool_call_update") {
|
|
401
|
+
const toolCallId = u.toolCallId;
|
|
288
402
|
const status = u.status;
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
403
|
+
const meta = u._meta;
|
|
404
|
+
// Cache tool info from initial tool_call events
|
|
405
|
+
if (updateType === "tool_call" && toolCallId) {
|
|
406
|
+
toolInfoCache.set(toolCallId, {
|
|
292
407
|
title: u.title,
|
|
293
|
-
|
|
408
|
+
name: meta?.claudeCode?.toolName,
|
|
294
409
|
input: u.rawInput,
|
|
295
|
-
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
if (status === "completed" || status === "failed") {
|
|
413
|
+
// Merge cached info for tool_call_update events that lack title/input
|
|
414
|
+
const cached = toolCallId
|
|
415
|
+
? toolInfoCache.get(toolCallId)
|
|
416
|
+
: undefined;
|
|
417
|
+
buffer.parts.push({
|
|
418
|
+
type: "tool",
|
|
419
|
+
toolCallId,
|
|
420
|
+
title: u.title ?? cached?.title,
|
|
421
|
+
name: meta?.claudeCode?.toolName ?? cached?.name,
|
|
422
|
+
status: u.status,
|
|
423
|
+
input: u.rawInput ?? cached?.input,
|
|
424
|
+
output: extractToolOutput(u.rawOutput),
|
|
296
425
|
});
|
|
297
426
|
}
|
|
298
427
|
}
|
|
@@ -300,18 +429,45 @@ export class ACPOverMAPHandler {
|
|
|
300
429
|
emitSessionUpdate(update);
|
|
301
430
|
updateCount++;
|
|
302
431
|
}
|
|
303
|
-
|
|
432
|
+
// Check if the loop ended because of cancellation
|
|
433
|
+
const wasCancelled = streamState.abortController.signal.aborted;
|
|
434
|
+
const stopReason = wasCancelled ? "cancelled" : "end_turn";
|
|
435
|
+
console.error(`[ACP-over-MAP] Prompt completed for agent ${agentId}, ${updateCount} updates, stopReason=${stopReason}`);
|
|
304
436
|
// Persist conversation turns for history
|
|
305
437
|
this.recordPromptTurns(sessionId, agentId, messageContent, buffer);
|
|
438
|
+
// Persist latest plan in EventStore for history loading across restarts
|
|
439
|
+
if (latestPlan) {
|
|
440
|
+
this.eventStore.updateAgentPlan(agentId, latestPlan);
|
|
441
|
+
}
|
|
306
442
|
// Emit updated session info after prompt completes
|
|
307
443
|
this.emitSessionInfo(streamState, sessionId, emitNotification);
|
|
308
|
-
return { stopReason
|
|
444
|
+
return { stopReason };
|
|
309
445
|
}
|
|
310
446
|
catch (error) {
|
|
311
|
-
|
|
447
|
+
// Extract a meaningful error message — errors from the ACP SDK may be
|
|
448
|
+
// plain objects ({code, message}) rather than Error instances.
|
|
449
|
+
let errorMessage;
|
|
450
|
+
if (error instanceof Error) {
|
|
451
|
+
errorMessage = error.message;
|
|
452
|
+
}
|
|
453
|
+
else if (typeof error === "object" &&
|
|
454
|
+
error !== null &&
|
|
455
|
+
"message" in error &&
|
|
456
|
+
typeof error.message === "string") {
|
|
457
|
+
errorMessage = error.message;
|
|
458
|
+
}
|
|
459
|
+
else {
|
|
460
|
+
try {
|
|
461
|
+
errorMessage = JSON.stringify(error);
|
|
462
|
+
}
|
|
463
|
+
catch {
|
|
464
|
+
errorMessage = String(error);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
console.error(`[ACP-over-MAP] Prompt error for agent ${agentId}:`, errorMessage);
|
|
312
468
|
return {
|
|
313
469
|
stopReason: "end_turn",
|
|
314
|
-
error:
|
|
470
|
+
error: errorMessage,
|
|
315
471
|
};
|
|
316
472
|
}
|
|
317
473
|
finally {
|
|
@@ -322,25 +478,28 @@ export class ACPOverMAPHandler {
|
|
|
322
478
|
const { sessionId: paramSessionId } = params ?? {};
|
|
323
479
|
// Prefer server's resolved session ID over client's potentially stale one
|
|
324
480
|
const sessionId = streamState.sessionId ?? paramSessionId ?? sessionIdFromContext;
|
|
325
|
-
|
|
481
|
+
const agentId = sessionId
|
|
482
|
+
? this.sessionMapper.getAgentId(sessionId)
|
|
483
|
+
: streamState.agentId;
|
|
484
|
+
console.error(`[ACP-over-MAP] Cancel - streamId=${streamState.streamId} sessionId=${sessionId} agentId=${agentId}`);
|
|
485
|
+
// 1. Abort the for-await loop in handlePrompt so it stops yielding updates
|
|
326
486
|
streamState.abortController.abort();
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
//
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
487
|
+
// 2. Cancel the agent's active session (sends session/cancel to the subprocess)
|
|
488
|
+
// This does NOT terminate the agent — it only interrupts the current prompt.
|
|
489
|
+
// The subprocess stays alive and can accept new prompts.
|
|
490
|
+
// Use map/agents/stop for full agent termination.
|
|
491
|
+
if (agentId) {
|
|
492
|
+
const session = this.agentManager.getSession(agentId);
|
|
493
|
+
if (session) {
|
|
494
|
+
try {
|
|
495
|
+
await session.cancel();
|
|
496
|
+
console.error(`[ACP-over-MAP] Session cancelled for agent ${agentId}`);
|
|
497
|
+
}
|
|
498
|
+
catch (error) {
|
|
499
|
+
console.warn(`[ACP-over-MAP] session.cancel() failed for ${agentId}:`, error);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
341
502
|
}
|
|
342
|
-
// Clean up
|
|
343
|
-
this.sessionMapper.removeMapping(sessionId);
|
|
344
503
|
return { cancelled: true };
|
|
345
504
|
}
|
|
346
505
|
// ─────────────────────────────────────────────────────────────────
|
|
@@ -370,7 +529,11 @@ export class ACPOverMAPHandler {
|
|
|
370
529
|
task,
|
|
371
530
|
cwd: cwd ?? this.defaultCwd,
|
|
372
531
|
role: "worker",
|
|
532
|
+
topics,
|
|
533
|
+
config,
|
|
373
534
|
});
|
|
535
|
+
// Notify subscribers that a new agent was registered
|
|
536
|
+
this.notifyAgentRegistered(spawned.id);
|
|
374
537
|
return {
|
|
375
538
|
agentId: spawned.id,
|
|
376
539
|
sessionId: spawned.session_id,
|
|
@@ -402,8 +565,17 @@ export class ACPOverMAPHandler {
|
|
|
402
565
|
if (!agent) {
|
|
403
566
|
throw new Error(`Agent not found: ${agentId}`);
|
|
404
567
|
}
|
|
568
|
+
// If the agent is already running, return its current state
|
|
569
|
+
// instead of throwing. The caller likely just wants to ensure
|
|
570
|
+
// the agent is active, which it already is.
|
|
405
571
|
if (agent.state !== "stopped" && agent.state !== "failed") {
|
|
406
|
-
|
|
572
|
+
console.error(`[ACP-over-MAP] _macro/resume: Agent ${agentId} is already ${agent.state}, returning current state`);
|
|
573
|
+
return {
|
|
574
|
+
success: true,
|
|
575
|
+
agentId: agent.id,
|
|
576
|
+
sessionId: agent.session_id,
|
|
577
|
+
alreadyRunning: true,
|
|
578
|
+
};
|
|
407
579
|
}
|
|
408
580
|
const spawned = await this.agentManager.resume(agentId);
|
|
409
581
|
return {
|
|
@@ -413,18 +585,18 @@ export class ACPOverMAPHandler {
|
|
|
413
585
|
};
|
|
414
586
|
}
|
|
415
587
|
case "_macro/getHistory": {
|
|
416
|
-
const { sessionId, agentId: historyAgentId, limit } = methodParams;
|
|
588
|
+
const { sessionId, agentId: historyAgentId, limit, } = methodParams;
|
|
417
589
|
// Resolve conversationId: prefer agentId lookup (resolves to the
|
|
418
590
|
// original session_id where turns were recorded), fall back to
|
|
419
591
|
// explicit sessionId. This allows history to survive across server
|
|
420
592
|
// restarts even when the ACP session ID changes (e.g., resume()
|
|
421
593
|
// fails → TUI creates new session with different ID).
|
|
594
|
+
const agent = historyAgentId
|
|
595
|
+
? this.eventStore.getAgent(historyAgentId)
|
|
596
|
+
: undefined;
|
|
422
597
|
let conversationId;
|
|
423
|
-
if (
|
|
424
|
-
|
|
425
|
-
if (agent) {
|
|
426
|
-
conversationId = agent.session_id;
|
|
427
|
-
}
|
|
598
|
+
if (agent) {
|
|
599
|
+
conversationId = agent.session_id;
|
|
428
600
|
}
|
|
429
601
|
if (!conversationId) {
|
|
430
602
|
conversationId = sessionId;
|
|
@@ -432,11 +604,40 @@ export class ACPOverMAPHandler {
|
|
|
432
604
|
if (!conversationId) {
|
|
433
605
|
return { turns: [] };
|
|
434
606
|
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
607
|
+
// For forked agents, include the source agent's conversation history
|
|
608
|
+
// (pre-fork turns) followed by this agent's own turns.
|
|
609
|
+
// Only include source turns from before the fork to avoid leaking
|
|
610
|
+
// turns that the source recorded after the fork point.
|
|
611
|
+
const sourceAgentId = agent?.metadata?.fork_of;
|
|
612
|
+
let turns;
|
|
613
|
+
if (sourceAgentId) {
|
|
614
|
+
const sourceAgent = this.eventStore.getAgent(sourceAgentId);
|
|
615
|
+
const sourceConversationId = sourceAgent?.session_id;
|
|
616
|
+
const forkTimestamp = agent.created_at;
|
|
617
|
+
const sourceTurns = sourceConversationId
|
|
618
|
+
? this.eventStore
|
|
619
|
+
.listTurns({
|
|
620
|
+
conversationId: sourceConversationId,
|
|
621
|
+
order: "asc",
|
|
622
|
+
limit: limit ?? 200,
|
|
623
|
+
})
|
|
624
|
+
.filter((t) => t.timestamp <= forkTimestamp)
|
|
625
|
+
: [];
|
|
626
|
+
const ownTurns = this.eventStore.listTurns({
|
|
627
|
+
conversationId,
|
|
628
|
+
order: "asc",
|
|
629
|
+
limit: limit ?? 200,
|
|
630
|
+
});
|
|
631
|
+
turns = [...sourceTurns, ...ownTurns];
|
|
632
|
+
}
|
|
633
|
+
else {
|
|
634
|
+
turns = this.eventStore.listTurns({
|
|
635
|
+
conversationId,
|
|
636
|
+
order: "asc",
|
|
637
|
+
limit: limit ?? 200,
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
const plan = agent?.plan ?? [];
|
|
440
641
|
return {
|
|
441
642
|
turns: turns.map((turn) => ({
|
|
442
643
|
role: turn.contentType === "user_prompt"
|
|
@@ -445,8 +646,241 @@ export class ACPOverMAPHandler {
|
|
|
445
646
|
timestamp: turn.timestamp,
|
|
446
647
|
content: turn.content,
|
|
447
648
|
})),
|
|
649
|
+
plan,
|
|
650
|
+
cwd: agent?.cwd ?? null,
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
case "_macro/respondToPermission": {
|
|
654
|
+
const { agentId: targetAgentId, requestId, optionId, } = methodParams;
|
|
655
|
+
if (!targetAgentId || !requestId || !optionId) {
|
|
656
|
+
throw new Error("agentId, requestId, and optionId are required");
|
|
657
|
+
}
|
|
658
|
+
const success = this.agentManager.respondToPermission(targetAgentId, requestId, optionId);
|
|
659
|
+
return { success };
|
|
660
|
+
}
|
|
661
|
+
case "_macro/cancelPermission": {
|
|
662
|
+
const { agentId: targetAgentId, requestId } = methodParams;
|
|
663
|
+
if (!targetAgentId || !requestId) {
|
|
664
|
+
throw new Error("agentId and requestId are required");
|
|
665
|
+
}
|
|
666
|
+
const success = this.agentManager.cancelPermission(targetAgentId, requestId);
|
|
667
|
+
return { success };
|
|
668
|
+
}
|
|
669
|
+
case "_macro/setPermissionMode": {
|
|
670
|
+
const { agentId: targetAgentId, permissionMode } = methodParams;
|
|
671
|
+
if (!targetAgentId || !permissionMode) {
|
|
672
|
+
throw new Error("agentId and permissionMode are required");
|
|
673
|
+
}
|
|
674
|
+
const previousMode = this.agentManager.getPermissionMode(targetAgentId);
|
|
675
|
+
const success = this.agentManager.setPermissionMode(targetAgentId, permissionMode);
|
|
676
|
+
if (success) {
|
|
677
|
+
return { success: true, previousMode: previousMode ?? undefined };
|
|
678
|
+
}
|
|
679
|
+
return {
|
|
680
|
+
success: false,
|
|
681
|
+
error: `No active session found for agent ${targetAgentId}`,
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
case "_macro/forkAgent": {
|
|
685
|
+
const { agentId, name, prompt, cwd } = methodParams;
|
|
686
|
+
if (!agentId) {
|
|
687
|
+
throw new Error("agentId is required");
|
|
688
|
+
}
|
|
689
|
+
const sourceAgent = this.eventStore.getAgent(agentId);
|
|
690
|
+
if (!sourceAgent) {
|
|
691
|
+
throw new Error(`Agent not found: ${agentId}`);
|
|
692
|
+
}
|
|
693
|
+
const forked = await this.agentManager.forkAgent(agentId, {
|
|
694
|
+
name,
|
|
695
|
+
prompt,
|
|
696
|
+
cwd: cwd ?? sourceAgent.cwd ?? this.defaultCwd,
|
|
697
|
+
});
|
|
698
|
+
// Fire-and-forget initial prompt if provided
|
|
699
|
+
if (prompt) {
|
|
700
|
+
(async () => {
|
|
701
|
+
try {
|
|
702
|
+
for await (const _update of this.agentManager.prompt(forked.id, prompt)) {
|
|
703
|
+
// drain iterator
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
catch {
|
|
707
|
+
// best-effort
|
|
708
|
+
}
|
|
709
|
+
})();
|
|
710
|
+
}
|
|
711
|
+
return {
|
|
712
|
+
newAgentId: forked.id,
|
|
713
|
+
newSessionId: forked.session_id,
|
|
714
|
+
originalAgentId: agentId,
|
|
715
|
+
providerSessionId: forked.session?.id,
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
case "_macro/agents/update": {
|
|
719
|
+
const { agentId, name, plan, metadata } = methodParams;
|
|
720
|
+
if (!agentId) {
|
|
721
|
+
throw new Error("agentId is required");
|
|
722
|
+
}
|
|
723
|
+
if (name === undefined &&
|
|
724
|
+
plan === undefined &&
|
|
725
|
+
metadata === undefined) {
|
|
726
|
+
throw new Error("At least one field to update is required (name, plan, or metadata)");
|
|
727
|
+
}
|
|
728
|
+
if (name !== undefined && !name.trim()) {
|
|
729
|
+
throw new Error("name must not be empty");
|
|
730
|
+
}
|
|
731
|
+
const agent = this.eventStore.getAgent(agentId);
|
|
732
|
+
if (!agent) {
|
|
733
|
+
throw new Error(`Agent not found: ${agentId}`);
|
|
734
|
+
}
|
|
735
|
+
const updates = {};
|
|
736
|
+
const updatedFields = [];
|
|
737
|
+
if (name !== undefined) {
|
|
738
|
+
updates.name = name.trim();
|
|
739
|
+
updatedFields.push("name");
|
|
740
|
+
}
|
|
741
|
+
if (plan !== undefined) {
|
|
742
|
+
updates.plan = plan;
|
|
743
|
+
updatedFields.push("plan");
|
|
744
|
+
}
|
|
745
|
+
if (metadata !== undefined) {
|
|
746
|
+
updates.metadata = metadata;
|
|
747
|
+
updatedFields.push("metadata");
|
|
748
|
+
}
|
|
749
|
+
this.eventStore.updateAgentMetadata(agentId, updates);
|
|
750
|
+
return {
|
|
751
|
+
success: true,
|
|
752
|
+
agentId,
|
|
753
|
+
updated: updatedFields,
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
case "_macro/getModels": {
|
|
757
|
+
const { sessionId } = methodParams;
|
|
758
|
+
const agentId = this.sessionMapper.getAgentId(sessionId);
|
|
759
|
+
if (!agentId) {
|
|
760
|
+
return { currentModelId: null, availableModels: [] };
|
|
761
|
+
}
|
|
762
|
+
const session = this.agentManager.getSession(agentId);
|
|
763
|
+
if (!session) {
|
|
764
|
+
return { currentModelId: null, availableModels: [] };
|
|
765
|
+
}
|
|
766
|
+
// Try clientHandler's model info store first (from _model_state_update notification)
|
|
767
|
+
const clientHandler = session.clientHandler;
|
|
768
|
+
const modelInfo = clientHandler?.getSessionModelInfo?.(session.id);
|
|
769
|
+
if (modelInfo && modelInfo.availableModels.length > 0) {
|
|
770
|
+
return modelInfo;
|
|
771
|
+
}
|
|
772
|
+
// Fall back to Session.models (from initial session response — just IDs)
|
|
773
|
+
if (session.models && session.models.length > 0) {
|
|
774
|
+
return {
|
|
775
|
+
currentModelId: session.models[0],
|
|
776
|
+
availableModels: session.models.map((id) => ({
|
|
777
|
+
modelId: id,
|
|
778
|
+
name: id,
|
|
779
|
+
})),
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
return { currentModelId: null, availableModels: [] };
|
|
783
|
+
}
|
|
784
|
+
case "_session/setCompaction": {
|
|
785
|
+
// Compaction is handled internally by the agent process.
|
|
786
|
+
// Accept the request as a no-op so the client doesn't get an error.
|
|
787
|
+
// TODO: Make sure this overrides if needed.
|
|
788
|
+
return { success: true };
|
|
789
|
+
}
|
|
790
|
+
// ── Task Management Extensions ────────────────────────────────
|
|
791
|
+
// These mirror the registered adapter extensions (_macro/task/*)
|
|
792
|
+
// so they're accessible via ACP-over-MAP streams.
|
|
793
|
+
case "_macro/task/list": {
|
|
794
|
+
const { filter } = (methodParams ?? {});
|
|
795
|
+
const taskFilter = filter
|
|
796
|
+
? {
|
|
797
|
+
status: filter.status,
|
|
798
|
+
assigned_agent: filter.assignedAgent,
|
|
799
|
+
parent_task: filter.parentTask,
|
|
800
|
+
created_by: filter.createdBy,
|
|
801
|
+
rootTasksOnly: filter.rootTasksOnly,
|
|
802
|
+
}
|
|
803
|
+
: undefined;
|
|
804
|
+
const tasks = this.taskManager.list(taskFilter);
|
|
805
|
+
return {
|
|
806
|
+
tasks: tasks.map((t) => ({
|
|
807
|
+
id: t.id,
|
|
808
|
+
description: t.description,
|
|
809
|
+
status: t.status,
|
|
810
|
+
assignedAgent: t.assigned_agent,
|
|
811
|
+
createdBy: t.created_by,
|
|
812
|
+
createdAt: t.created_at,
|
|
813
|
+
parentTask: t.parent_task,
|
|
814
|
+
isBlocked: t.isBlocked,
|
|
815
|
+
externalId: t.external_id,
|
|
816
|
+
})),
|
|
448
817
|
};
|
|
449
818
|
}
|
|
819
|
+
case "_macro/task/get": {
|
|
820
|
+
const { taskId } = methodParams;
|
|
821
|
+
if (!taskId)
|
|
822
|
+
throw new Error("taskId is required");
|
|
823
|
+
const task = this.taskManager.get(taskId);
|
|
824
|
+
if (!task)
|
|
825
|
+
throw new Error(`Task not found: ${taskId}`);
|
|
826
|
+
return {
|
|
827
|
+
task: {
|
|
828
|
+
id: task.id,
|
|
829
|
+
description: task.description,
|
|
830
|
+
status: task.status,
|
|
831
|
+
assignedAgent: task.assigned_agent,
|
|
832
|
+
createdBy: task.created_by,
|
|
833
|
+
createdAt: task.created_at,
|
|
834
|
+
parentTask: task.parent_task,
|
|
835
|
+
isBlocked: task.isBlocked,
|
|
836
|
+
externalId: task.external_id,
|
|
837
|
+
},
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
case "_macro/task/create": {
|
|
841
|
+
const { description, parentTask, externalId } = methodParams;
|
|
842
|
+
if (!description)
|
|
843
|
+
throw new Error("description is required");
|
|
844
|
+
// Determine who is creating the task — use the stream's agent or a default
|
|
845
|
+
const createdBy = (streamState.agentId ?? "tui");
|
|
846
|
+
const task = this.taskManager.create({
|
|
847
|
+
description,
|
|
848
|
+
created_by: createdBy,
|
|
849
|
+
parent_task: parentTask,
|
|
850
|
+
});
|
|
851
|
+
return {
|
|
852
|
+
task: {
|
|
853
|
+
id: task.id,
|
|
854
|
+
description: task.description,
|
|
855
|
+
status: task.status,
|
|
856
|
+
assignedAgent: task.assigned_agent,
|
|
857
|
+
createdBy: task.created_by,
|
|
858
|
+
createdAt: task.created_at,
|
|
859
|
+
parentTask: task.parent_task,
|
|
860
|
+
},
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
case "_macro/task/assign": {
|
|
864
|
+
const { taskId, agentId, role } = methodParams;
|
|
865
|
+
if (!taskId)
|
|
866
|
+
throw new Error("taskId is required");
|
|
867
|
+
if (!agentId)
|
|
868
|
+
throw new Error("agentId is required");
|
|
869
|
+
this.taskManager.assign(taskId, agentId, role);
|
|
870
|
+
return { success: true };
|
|
871
|
+
}
|
|
872
|
+
case "_macro/task/complete": {
|
|
873
|
+
const { taskId, outputs } = methodParams;
|
|
874
|
+
if (!taskId)
|
|
875
|
+
throw new Error("taskId is required");
|
|
876
|
+
// Update status to completed
|
|
877
|
+
this.taskManager.updateStatus(taskId, "completed");
|
|
878
|
+
// If outputs provided, update task metadata
|
|
879
|
+
if (outputs) {
|
|
880
|
+
this.taskManager.update(taskId, { outputs });
|
|
881
|
+
}
|
|
882
|
+
return { success: true };
|
|
883
|
+
}
|
|
450
884
|
default:
|
|
451
885
|
throw new Error(`Unknown extension method: ${method}`);
|
|
452
886
|
}
|
|
@@ -505,15 +939,8 @@ export class ACPOverMAPHandler {
|
|
|
505
939
|
},
|
|
506
940
|
});
|
|
507
941
|
}
|
|
508
|
-
// Record assistant turn with accumulated content
|
|
509
|
-
const
|
|
510
|
-
const parts = [];
|
|
511
|
-
if (assistantText) {
|
|
512
|
-
parts.push({ type: "text", text: assistantText });
|
|
513
|
-
}
|
|
514
|
-
for (const tool of buffer.toolCalls) {
|
|
515
|
-
parts.push({ type: "tool", ...tool });
|
|
516
|
-
}
|
|
942
|
+
// Record assistant turn with accumulated content (parts already in order)
|
|
943
|
+
const parts = buffer.parts;
|
|
517
944
|
if (parts.length > 0) {
|
|
518
945
|
this.eventStore.emit({
|
|
519
946
|
type: "turn",
|