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
|
@@ -11,10 +11,32 @@
|
|
|
11
11
|
import type { AgentManager } from "../../agent/agent-manager.js";
|
|
12
12
|
import type { EventStore } from "../../store/event-store.js";
|
|
13
13
|
import type { TaskManager } from "../../task/task-manager.js";
|
|
14
|
-
import type { AgentId } from "../../store/types/index.js";
|
|
14
|
+
import type { AgentId, TaskId } from "../../store/types/index.js";
|
|
15
|
+
import type { AgentConfig } from "../../agent/types.js";
|
|
16
|
+
import type { TaskFilter } from "../../task/types.js";
|
|
15
17
|
import { SessionMapper } from "../../acp/session-mapper.js";
|
|
16
18
|
import type { ACPSessionId } from "../../acp/types.js";
|
|
17
19
|
|
|
20
|
+
// ─────────────────────────────────────────────────────────────────
|
|
21
|
+
// Helpers
|
|
22
|
+
// ─────────────────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
/** Extract a plain-text output string from `rawOutput` (string | ContentBlock[] | undefined). */
|
|
25
|
+
function extractToolOutput(rawOutput: unknown): string | undefined {
|
|
26
|
+
if (typeof rawOutput === "string") return rawOutput;
|
|
27
|
+
if (Array.isArray(rawOutput)) {
|
|
28
|
+
return (
|
|
29
|
+
rawOutput
|
|
30
|
+
.filter(
|
|
31
|
+
(item: any) => item.type === "text" && typeof item.text === "string",
|
|
32
|
+
)
|
|
33
|
+
.map((item: any) => item.text as string)
|
|
34
|
+
.join("\n") || undefined
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
18
40
|
// ─────────────────────────────────────────────────────────────────
|
|
19
41
|
// Types
|
|
20
42
|
// ─────────────────────────────────────────────────────────────────
|
|
@@ -40,6 +62,18 @@ export interface ACPOverMAPConfig {
|
|
|
40
62
|
eventStore: EventStore;
|
|
41
63
|
taskManager: TaskManager;
|
|
42
64
|
defaultCwd?: string;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Callback when a new agent is created via session/new or session/loadSession.
|
|
68
|
+
* Used by MAPAdapter to emit agent.registered events to all subscribers.
|
|
69
|
+
*/
|
|
70
|
+
onAgentRegistered?: (agent: {
|
|
71
|
+
id: string;
|
|
72
|
+
name?: string;
|
|
73
|
+
role?: string;
|
|
74
|
+
parent?: string;
|
|
75
|
+
metadata?: Record<string, unknown>;
|
|
76
|
+
}) => void;
|
|
43
77
|
}
|
|
44
78
|
|
|
45
79
|
/**
|
|
@@ -54,6 +88,8 @@ interface StreamState {
|
|
|
54
88
|
sessionId?: string;
|
|
55
89
|
agentId?: AgentId;
|
|
56
90
|
abortController: AbortController;
|
|
91
|
+
/** Permission mode from initialization _meta */
|
|
92
|
+
permissionMode?: "auto-approve" | "auto-deny" | "callback" | "interactive";
|
|
57
93
|
}
|
|
58
94
|
|
|
59
95
|
// ─────────────────────────────────────────────────────────────────
|
|
@@ -65,18 +101,61 @@ export class ACPOverMAPHandler {
|
|
|
65
101
|
private eventStore: EventStore;
|
|
66
102
|
private taskManager: TaskManager;
|
|
67
103
|
private defaultCwd: string;
|
|
104
|
+
private onAgentRegistered?: ACPOverMAPConfig["onAgentRegistered"];
|
|
68
105
|
|
|
69
106
|
/** Stream states by streamId */
|
|
70
107
|
private streams: Map<string, StreamState> = new Map();
|
|
71
108
|
|
|
72
109
|
/** Session mapper for ACP session -> Agent mapping */
|
|
73
|
-
private sessionMapper: SessionMapper
|
|
110
|
+
private sessionMapper: SessionMapper;
|
|
74
111
|
|
|
75
112
|
constructor(config: ACPOverMAPConfig) {
|
|
76
113
|
this.agentManager = config.agentManager;
|
|
77
114
|
this.eventStore = config.eventStore;
|
|
78
115
|
this.taskManager = config.taskManager;
|
|
79
116
|
this.defaultCwd = config.defaultCwd ?? process.cwd();
|
|
117
|
+
this.onAgentRegistered = config.onAgentRegistered;
|
|
118
|
+
|
|
119
|
+
// Initialize session mapper with EventStore for persistence and recovery
|
|
120
|
+
this.sessionMapper = new SessionMapper(this.eventStore);
|
|
121
|
+
const recovered = this.sessionMapper.recoverFromStore();
|
|
122
|
+
if (recovered > 0) {
|
|
123
|
+
console.error(
|
|
124
|
+
`[ACP-over-MAP] Recovered ${recovered} session(s) from store`,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Notify subscribers that a new agent was registered.
|
|
131
|
+
* Looks up agent details from EventStore and calls the onAgentRegistered callback.
|
|
132
|
+
*/
|
|
133
|
+
private notifyAgentRegistered(agentId: string): void {
|
|
134
|
+
if (!this.onAgentRegistered) return;
|
|
135
|
+
const agent = this.eventStore.getAgent(agentId as AgentId);
|
|
136
|
+
this.onAgentRegistered({
|
|
137
|
+
id: agentId,
|
|
138
|
+
name: agent?.name,
|
|
139
|
+
role: agent?.role,
|
|
140
|
+
parent: agent?.parent ?? undefined,
|
|
141
|
+
metadata: agent?.metadata,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Abort all active streams targeting a specific agent.
|
|
147
|
+
* Called when an agent is stopped via MAP protocol to interrupt
|
|
148
|
+
* any in-progress ACP prompt streaming.
|
|
149
|
+
*/
|
|
150
|
+
abortStreamsForAgent(agentId: AgentId): void {
|
|
151
|
+
for (const [streamId, streamState] of this.streams) {
|
|
152
|
+
if (streamState.agentId === agentId) {
|
|
153
|
+
console.error(
|
|
154
|
+
`[ACP-over-MAP] Aborting stream ${streamId} for stopped agent ${agentId}`,
|
|
155
|
+
);
|
|
156
|
+
streamState.abortController.abort();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
80
159
|
}
|
|
81
160
|
|
|
82
161
|
/**
|
|
@@ -94,7 +173,9 @@ export class ACPOverMAPHandler {
|
|
|
94
173
|
const { streamId, sessionId } = acpContext;
|
|
95
174
|
const method = acp.method;
|
|
96
175
|
|
|
97
|
-
console.error(
|
|
176
|
+
console.error(
|
|
177
|
+
`[ACP-over-MAP] Processing - streamId=${streamId} method=${method}`,
|
|
178
|
+
);
|
|
98
179
|
|
|
99
180
|
// Get or create stream state
|
|
100
181
|
let streamState = this.streams.get(streamId);
|
|
@@ -117,11 +198,19 @@ export class ACPOverMAPHandler {
|
|
|
117
198
|
break;
|
|
118
199
|
|
|
119
200
|
case "session/new":
|
|
120
|
-
result = await this.handleNewSession(
|
|
201
|
+
result = await this.handleNewSession(
|
|
202
|
+
streamState,
|
|
203
|
+
acp.params,
|
|
204
|
+
emitNotification,
|
|
205
|
+
);
|
|
121
206
|
break;
|
|
122
207
|
|
|
123
208
|
case "session/load":
|
|
124
|
-
result = await this.handleLoadSession(
|
|
209
|
+
result = await this.handleLoadSession(
|
|
210
|
+
streamState,
|
|
211
|
+
acp.params,
|
|
212
|
+
emitNotification,
|
|
213
|
+
);
|
|
125
214
|
break;
|
|
126
215
|
|
|
127
216
|
case "authenticate":
|
|
@@ -129,7 +218,12 @@ export class ACPOverMAPHandler {
|
|
|
129
218
|
break;
|
|
130
219
|
|
|
131
220
|
case "session/prompt":
|
|
132
|
-
result = await this.handlePrompt(
|
|
221
|
+
result = await this.handlePrompt(
|
|
222
|
+
streamState,
|
|
223
|
+
acp.params,
|
|
224
|
+
sessionId,
|
|
225
|
+
emitNotification,
|
|
226
|
+
);
|
|
133
227
|
break;
|
|
134
228
|
|
|
135
229
|
case "session/cancel":
|
|
@@ -139,7 +233,11 @@ export class ACPOverMAPHandler {
|
|
|
139
233
|
default:
|
|
140
234
|
// Check for extension methods
|
|
141
235
|
if (method?.startsWith("_")) {
|
|
142
|
-
result = await this.handleExtension(
|
|
236
|
+
result = await this.handleExtension(
|
|
237
|
+
streamState,
|
|
238
|
+
method,
|
|
239
|
+
acp.params,
|
|
240
|
+
);
|
|
143
241
|
} else {
|
|
144
242
|
throw new Error(`Unknown ACP method: ${method}`);
|
|
145
243
|
}
|
|
@@ -181,6 +279,21 @@ export class ACPOverMAPHandler {
|
|
|
181
279
|
|
|
182
280
|
streamState.initialized = true;
|
|
183
281
|
|
|
282
|
+
// Extract permission mode from _meta.macroConfig if provided
|
|
283
|
+
const meta = (params as Record<string, unknown> | undefined)?._meta as
|
|
284
|
+
| Record<string, unknown>
|
|
285
|
+
| undefined;
|
|
286
|
+
const macroConfig = meta?.macroConfig as
|
|
287
|
+
| Record<string, unknown>
|
|
288
|
+
| undefined;
|
|
289
|
+
const defaultSubAgentConfig = macroConfig?.defaultSubAgentConfig as
|
|
290
|
+
| Record<string, unknown>
|
|
291
|
+
| undefined;
|
|
292
|
+
if (defaultSubAgentConfig?.permissionMode) {
|
|
293
|
+
streamState.permissionMode =
|
|
294
|
+
defaultSubAgentConfig.permissionMode as StreamState["permissionMode"];
|
|
295
|
+
}
|
|
296
|
+
|
|
184
297
|
return {
|
|
185
298
|
protocolVersion: 1,
|
|
186
299
|
agentCapabilities: {
|
|
@@ -207,13 +320,15 @@ export class ACPOverMAPHandler {
|
|
|
207
320
|
throw new Error("Must call initialize before newSession");
|
|
208
321
|
}
|
|
209
322
|
|
|
210
|
-
const { cwd, mcpServers } =
|
|
323
|
+
const { cwd, mcpServers } =
|
|
324
|
+
(params as { cwd?: string; mcpServers?: unknown[] }) ?? {};
|
|
211
325
|
const workingDir = cwd ?? this.defaultCwd;
|
|
212
326
|
|
|
213
327
|
// Spawn a new head manager for this session
|
|
214
328
|
const spawned = await this.agentManager.getOrCreateHeadManager({
|
|
215
329
|
cwd: workingDir,
|
|
216
330
|
forceNew: true,
|
|
331
|
+
permissionMode: streamState.permissionMode,
|
|
217
332
|
});
|
|
218
333
|
|
|
219
334
|
const sessionId = spawned.session_id;
|
|
@@ -223,7 +338,12 @@ export class ACPOverMAPHandler {
|
|
|
223
338
|
// Create session mapping
|
|
224
339
|
this.sessionMapper.createMapping(sessionId as ACPSessionId, spawned.id);
|
|
225
340
|
|
|
226
|
-
console.error(
|
|
341
|
+
console.error(
|
|
342
|
+
`[ACP-over-MAP] Created session ${sessionId} -> agent ${spawned.id}`,
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
// Notify subscribers that a new agent was registered
|
|
346
|
+
this.notifyAgentRegistered(spawned.id);
|
|
227
347
|
|
|
228
348
|
// Emit session_info_update so client has title/timestamps
|
|
229
349
|
this.emitSessionInfo(streamState, sessionId, emitNotification);
|
|
@@ -240,7 +360,11 @@ export class ACPOverMAPHandler {
|
|
|
240
360
|
throw new Error("Must call initialize before loadSession");
|
|
241
361
|
}
|
|
242
362
|
|
|
243
|
-
const {
|
|
363
|
+
const {
|
|
364
|
+
sessionId: rawSessionId,
|
|
365
|
+
cwd,
|
|
366
|
+
_meta,
|
|
367
|
+
} = (params as {
|
|
244
368
|
sessionId: string;
|
|
245
369
|
cwd?: string;
|
|
246
370
|
_meta?: Record<string, unknown>;
|
|
@@ -259,8 +383,46 @@ export class ACPOverMAPHandler {
|
|
|
259
383
|
}
|
|
260
384
|
sessionId = agent.session_id;
|
|
261
385
|
console.error(
|
|
262
|
-
`[ACP-over-MAP] loadSession: Resolved agentId ${metaAgentId} to session ${sessionId}
|
|
386
|
+
`[ACP-over-MAP] loadSession: Resolved agentId ${metaAgentId} to session ${sessionId}`,
|
|
263
387
|
);
|
|
388
|
+
|
|
389
|
+
// Handle the agent directly — works for both head managers and sub-agents.
|
|
390
|
+
// The previous code only searched listHeadManagers(), so sub-agents
|
|
391
|
+
// were never found and a new head manager was created instead.
|
|
392
|
+
if (this.agentManager.hasActiveSession(metaAgentId as AgentId)) {
|
|
393
|
+
console.error(
|
|
394
|
+
`[ACP-over-MAP] loadSession: Reusing active session for agent ${metaAgentId}`,
|
|
395
|
+
);
|
|
396
|
+
} else {
|
|
397
|
+
// Agent exists but no active session — resume it
|
|
398
|
+
console.error(
|
|
399
|
+
`[ACP-over-MAP] loadSession: Resuming agent ${metaAgentId}`,
|
|
400
|
+
);
|
|
401
|
+
try {
|
|
402
|
+
await this.agentManager.resume(
|
|
403
|
+
metaAgentId as AgentId,
|
|
404
|
+
streamState.permissionMode,
|
|
405
|
+
);
|
|
406
|
+
} catch (resumeErr) {
|
|
407
|
+
// ALREADY_RUNNING can happen in a race — safe to ignore
|
|
408
|
+
const code = (resumeErr as { code?: string }).code;
|
|
409
|
+
if (code !== "ALREADY_RUNNING") {
|
|
410
|
+
throw resumeErr;
|
|
411
|
+
}
|
|
412
|
+
console.error(
|
|
413
|
+
`[ACP-over-MAP] loadSession: Agent ${metaAgentId} already running (race), continuing`,
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
streamState.sessionId = sessionId;
|
|
419
|
+
streamState.agentId = metaAgentId as AgentId;
|
|
420
|
+
this.sessionMapper.createMapping(
|
|
421
|
+
sessionId as ACPSessionId,
|
|
422
|
+
metaAgentId as AgentId,
|
|
423
|
+
);
|
|
424
|
+
this.emitSessionInfo(streamState, sessionId, emitNotification);
|
|
425
|
+
return { sessionId };
|
|
264
426
|
}
|
|
265
427
|
|
|
266
428
|
const workingDir = cwd ?? this.defaultCwd;
|
|
@@ -272,17 +434,27 @@ export class ACPOverMAPHandler {
|
|
|
272
434
|
if (existing) {
|
|
273
435
|
// Check if the agent already has an active session
|
|
274
436
|
if (this.agentManager.hasActiveSession(existing.id)) {
|
|
275
|
-
console.error(
|
|
437
|
+
console.error(
|
|
438
|
+
`[ACP-over-MAP] loadSession: Reusing existing session for agent ${existing.id}`,
|
|
439
|
+
);
|
|
276
440
|
streamState.sessionId = sessionId;
|
|
277
441
|
streamState.agentId = existing.id;
|
|
278
|
-
this.sessionMapper.createMapping(
|
|
442
|
+
this.sessionMapper.createMapping(
|
|
443
|
+
sessionId as ACPSessionId,
|
|
444
|
+
existing.id,
|
|
445
|
+
);
|
|
279
446
|
this.emitSessionInfo(streamState, sessionId, emitNotification);
|
|
280
447
|
return { sessionId };
|
|
281
448
|
}
|
|
282
449
|
|
|
283
450
|
// Agent exists but no active session - resume it
|
|
284
|
-
console.error(
|
|
285
|
-
|
|
451
|
+
console.error(
|
|
452
|
+
`[ACP-over-MAP] loadSession: Resuming stopped agent ${existing.id}`,
|
|
453
|
+
);
|
|
454
|
+
const spawned = await this.agentManager.resume(
|
|
455
|
+
existing.id,
|
|
456
|
+
streamState.permissionMode,
|
|
457
|
+
);
|
|
286
458
|
streamState.sessionId = sessionId;
|
|
287
459
|
streamState.agentId = spawned.id;
|
|
288
460
|
this.sessionMapper.createMapping(sessionId as ACPSessionId, spawned.id);
|
|
@@ -291,15 +463,22 @@ export class ACPOverMAPHandler {
|
|
|
291
463
|
}
|
|
292
464
|
|
|
293
465
|
// No existing agent found - create new with the specified session ID
|
|
294
|
-
console.error(
|
|
466
|
+
console.error(
|
|
467
|
+
`[ACP-over-MAP] loadSession: Creating new agent for session ${sessionId}`,
|
|
468
|
+
);
|
|
295
469
|
const spawned = await this.agentManager.getOrCreateHeadManager({
|
|
296
470
|
cwd: workingDir,
|
|
297
471
|
sessionId,
|
|
472
|
+
permissionMode: streamState.permissionMode,
|
|
298
473
|
});
|
|
299
474
|
|
|
300
475
|
streamState.sessionId = sessionId;
|
|
301
476
|
streamState.agentId = spawned.id;
|
|
302
477
|
this.sessionMapper.createMapping(sessionId as ACPSessionId, spawned.id);
|
|
478
|
+
|
|
479
|
+
// Notify subscribers that a new agent was registered
|
|
480
|
+
this.notifyAgentRegistered(spawned.id);
|
|
481
|
+
|
|
303
482
|
this.emitSessionInfo(streamState, sessionId, emitNotification);
|
|
304
483
|
|
|
305
484
|
return { sessionId };
|
|
@@ -316,15 +495,17 @@ export class ACPOverMAPHandler {
|
|
|
316
495
|
sessionIdFromContext?: string,
|
|
317
496
|
emitNotification?: ACPNotificationEmitter,
|
|
318
497
|
): Promise<unknown> {
|
|
319
|
-
const { prompt, sessionId: paramSessionId } =
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
498
|
+
const { prompt, sessionId: paramSessionId } =
|
|
499
|
+
(params as {
|
|
500
|
+
prompt?: Array<{ type: string; text?: string }>;
|
|
501
|
+
sessionId?: string;
|
|
502
|
+
messages?: Array<{ role: string; content: string }>;
|
|
503
|
+
}) ?? {};
|
|
324
504
|
|
|
325
505
|
// Prefer the server's resolved session ID (set during loadSession) over the
|
|
326
506
|
// client's acpContext.sessionId which may be stale (e.g., "_resolve_" sentinel)
|
|
327
|
-
const sessionId =
|
|
507
|
+
const sessionId =
|
|
508
|
+
streamState.sessionId ?? paramSessionId ?? sessionIdFromContext;
|
|
328
509
|
if (!sessionId) {
|
|
329
510
|
throw new Error("No session - call newSession or loadSession first");
|
|
330
511
|
}
|
|
@@ -347,15 +528,21 @@ export class ACPOverMAPHandler {
|
|
|
347
528
|
.filter((block) => block.type === "text" && block.text)
|
|
348
529
|
.map((block) => block.text)
|
|
349
530
|
.join("\n");
|
|
350
|
-
} else if (
|
|
531
|
+
} else if (
|
|
532
|
+
(params as { messages?: Array<{ content: string }> })?.messages
|
|
533
|
+
) {
|
|
351
534
|
// Handle messages format (role/content array)
|
|
352
|
-
const messages = (
|
|
535
|
+
const messages = (
|
|
536
|
+
params as { messages: Array<{ role: string; content: string }> }
|
|
537
|
+
).messages;
|
|
353
538
|
messageContent = messages.map((m) => m.content).join("\n");
|
|
354
539
|
} else {
|
|
355
540
|
messageContent = JSON.stringify(params);
|
|
356
541
|
}
|
|
357
542
|
|
|
358
|
-
console.error(
|
|
543
|
+
console.error(
|
|
544
|
+
`[ACP-over-MAP] Prompting agent ${agentId} with: ${messageContent.slice(0, 100)}...`,
|
|
545
|
+
);
|
|
359
546
|
|
|
360
547
|
// Mark session as processing
|
|
361
548
|
this.sessionMapper.setProcessing(sessionId as ACPSessionId, true);
|
|
@@ -386,37 +573,106 @@ export class ACPOverMAPHandler {
|
|
|
386
573
|
this.ensureConversation(sessionId as ACPSessionId, agentId);
|
|
387
574
|
|
|
388
575
|
// Accumulate response content for history recording
|
|
389
|
-
const buffer: {
|
|
390
|
-
|
|
391
|
-
|
|
576
|
+
const buffer: {
|
|
577
|
+
parts: Array<
|
|
578
|
+
| { type: "text"; text: string }
|
|
579
|
+
| ({ type: "tool" } & Record<string, unknown>)
|
|
580
|
+
>;
|
|
581
|
+
} = {
|
|
582
|
+
parts: [],
|
|
392
583
|
};
|
|
393
584
|
|
|
585
|
+
// Track latest plan for persistence
|
|
586
|
+
let latestPlan: Array<{
|
|
587
|
+
content: string;
|
|
588
|
+
priority: string;
|
|
589
|
+
status: string;
|
|
590
|
+
}> | null = null;
|
|
591
|
+
|
|
592
|
+
// Track tool info from initial tool_call events (title, name, input)
|
|
593
|
+
// so we can merge them when tool_call_update arrives with status "completed"
|
|
594
|
+
const toolInfoCache = new Map<
|
|
595
|
+
string,
|
|
596
|
+
{ title?: string; name?: string; input?: unknown }
|
|
597
|
+
>();
|
|
598
|
+
|
|
394
599
|
try {
|
|
395
600
|
// Stream responses from the agent
|
|
396
601
|
let updateCount = 0;
|
|
397
|
-
for await (const update of this.agentManager.prompt(
|
|
602
|
+
for await (const update of this.agentManager.prompt(
|
|
603
|
+
agentId,
|
|
604
|
+
messageContent,
|
|
605
|
+
)) {
|
|
398
606
|
// Check for cancellation
|
|
399
607
|
if (streamState.abortController.signal.aborted) {
|
|
400
608
|
return { stopReason: "cancelled" };
|
|
401
609
|
}
|
|
402
610
|
|
|
403
|
-
// Accumulate content for history persistence
|
|
611
|
+
// Accumulate content for history persistence (preserving text/tool interleaving order)
|
|
404
612
|
const u = update as Record<string, unknown>;
|
|
405
|
-
const updateType = u.sessionUpdate as string ?? u.type as string;
|
|
613
|
+
const updateType = (u.sessionUpdate as string) ?? (u.type as string);
|
|
614
|
+
|
|
615
|
+
// Annotate permission_request updates with agentId so clients can respond
|
|
616
|
+
if (updateType === "permission_request") {
|
|
617
|
+
u._agentId = agentId;
|
|
618
|
+
}
|
|
406
619
|
if (updateType === "agent_message_chunk") {
|
|
407
|
-
const content = u.content as
|
|
620
|
+
const content = u.content as
|
|
621
|
+
| { type?: string; text?: string }
|
|
622
|
+
| undefined;
|
|
408
623
|
if (content?.text) {
|
|
409
|
-
buffer.
|
|
624
|
+
const last = buffer.parts[buffer.parts.length - 1];
|
|
625
|
+
if (last && last.type === "text") {
|
|
626
|
+
last.text += content.text;
|
|
627
|
+
} else {
|
|
628
|
+
buffer.parts.push({ type: "text", text: content.text });
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
} else if (updateType === "plan") {
|
|
632
|
+
const entries = (
|
|
633
|
+
u as {
|
|
634
|
+
entries?: Array<{
|
|
635
|
+
content: string;
|
|
636
|
+
priority: string;
|
|
637
|
+
status: string;
|
|
638
|
+
}>;
|
|
639
|
+
}
|
|
640
|
+
).entries;
|
|
641
|
+
if (entries) {
|
|
642
|
+
latestPlan = entries;
|
|
410
643
|
}
|
|
411
|
-
} else if (
|
|
644
|
+
} else if (
|
|
645
|
+
updateType === "tool_call" ||
|
|
646
|
+
updateType === "tool_call_update"
|
|
647
|
+
) {
|
|
648
|
+
const toolCallId = u.toolCallId as string | undefined;
|
|
412
649
|
const status = u.status as string | undefined;
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
650
|
+
const meta = u._meta as
|
|
651
|
+
| { claudeCode?: { toolName?: string } }
|
|
652
|
+
| undefined;
|
|
653
|
+
|
|
654
|
+
// Cache tool info from initial tool_call events
|
|
655
|
+
if (updateType === "tool_call" && toolCallId) {
|
|
656
|
+
toolInfoCache.set(toolCallId, {
|
|
657
|
+
title: u.title as string | undefined,
|
|
658
|
+
name: meta?.claudeCode?.toolName,
|
|
418
659
|
input: u.rawInput,
|
|
419
|
-
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
if (status === "completed" || status === "failed") {
|
|
664
|
+
// Merge cached info for tool_call_update events that lack title/input
|
|
665
|
+
const cached = toolCallId
|
|
666
|
+
? toolInfoCache.get(toolCallId)
|
|
667
|
+
: undefined;
|
|
668
|
+
buffer.parts.push({
|
|
669
|
+
type: "tool",
|
|
670
|
+
toolCallId,
|
|
671
|
+
title: u.title ?? cached?.title,
|
|
672
|
+
name: meta?.claudeCode?.toolName ?? cached?.name,
|
|
673
|
+
status: u.status,
|
|
674
|
+
input: u.rawInput ?? cached?.input,
|
|
675
|
+
output: extractToolOutput(u.rawOutput),
|
|
420
676
|
});
|
|
421
677
|
}
|
|
422
678
|
}
|
|
@@ -426,20 +682,58 @@ export class ACPOverMAPHandler {
|
|
|
426
682
|
updateCount++;
|
|
427
683
|
}
|
|
428
684
|
|
|
429
|
-
|
|
685
|
+
// Check if the loop ended because of cancellation
|
|
686
|
+
const wasCancelled = streamState.abortController.signal.aborted;
|
|
687
|
+
const stopReason = wasCancelled ? "cancelled" : "end_turn";
|
|
688
|
+
|
|
689
|
+
console.error(
|
|
690
|
+
`[ACP-over-MAP] Prompt completed for agent ${agentId}, ${updateCount} updates, stopReason=${stopReason}`,
|
|
691
|
+
);
|
|
430
692
|
|
|
431
693
|
// Persist conversation turns for history
|
|
432
|
-
this.recordPromptTurns(
|
|
694
|
+
this.recordPromptTurns(
|
|
695
|
+
sessionId as ACPSessionId,
|
|
696
|
+
agentId,
|
|
697
|
+
messageContent,
|
|
698
|
+
buffer,
|
|
699
|
+
);
|
|
700
|
+
|
|
701
|
+
// Persist latest plan in EventStore for history loading across restarts
|
|
702
|
+
if (latestPlan) {
|
|
703
|
+
this.eventStore.updateAgentPlan(agentId as AgentId, latestPlan);
|
|
704
|
+
}
|
|
433
705
|
|
|
434
706
|
// Emit updated session info after prompt completes
|
|
435
707
|
this.emitSessionInfo(streamState, sessionId, emitNotification);
|
|
436
708
|
|
|
437
|
-
return { stopReason
|
|
709
|
+
return { stopReason };
|
|
438
710
|
} catch (error) {
|
|
439
|
-
|
|
711
|
+
// Extract a meaningful error message — errors from the ACP SDK may be
|
|
712
|
+
// plain objects ({code, message}) rather than Error instances.
|
|
713
|
+
let errorMessage: string;
|
|
714
|
+
if (error instanceof Error) {
|
|
715
|
+
errorMessage = error.message;
|
|
716
|
+
} else if (
|
|
717
|
+
typeof error === "object" &&
|
|
718
|
+
error !== null &&
|
|
719
|
+
"message" in error &&
|
|
720
|
+
typeof (error as { message: unknown }).message === "string"
|
|
721
|
+
) {
|
|
722
|
+
errorMessage = (error as { message: string }).message;
|
|
723
|
+
} else {
|
|
724
|
+
try {
|
|
725
|
+
errorMessage = JSON.stringify(error);
|
|
726
|
+
} catch {
|
|
727
|
+
errorMessage = String(error);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
console.error(
|
|
731
|
+
`[ACP-over-MAP] Prompt error for agent ${agentId}:`,
|
|
732
|
+
errorMessage,
|
|
733
|
+
);
|
|
440
734
|
return {
|
|
441
735
|
stopReason: "end_turn",
|
|
442
|
-
error:
|
|
736
|
+
error: errorMessage,
|
|
443
737
|
};
|
|
444
738
|
} finally {
|
|
445
739
|
this.sessionMapper.setProcessing(sessionId as ACPSessionId, false);
|
|
@@ -451,33 +745,44 @@ export class ACPOverMAPHandler {
|
|
|
451
745
|
params: unknown,
|
|
452
746
|
sessionIdFromContext?: string,
|
|
453
747
|
): Promise<unknown> {
|
|
454
|
-
const { sessionId: paramSessionId } =
|
|
748
|
+
const { sessionId: paramSessionId } =
|
|
749
|
+
(params as { sessionId?: string }) ?? {};
|
|
455
750
|
// Prefer server's resolved session ID over client's potentially stale one
|
|
456
|
-
const sessionId =
|
|
751
|
+
const sessionId =
|
|
752
|
+
streamState.sessionId ?? paramSessionId ?? sessionIdFromContext;
|
|
457
753
|
|
|
458
|
-
|
|
459
|
-
|
|
754
|
+
const agentId = sessionId
|
|
755
|
+
? this.sessionMapper.getAgentId(sessionId as ACPSessionId)
|
|
756
|
+
: streamState.agentId;
|
|
460
757
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
758
|
+
console.error(
|
|
759
|
+
`[ACP-over-MAP] Cancel - streamId=${streamState.streamId} sessionId=${sessionId} agentId=${agentId}`,
|
|
760
|
+
);
|
|
464
761
|
|
|
465
|
-
//
|
|
466
|
-
|
|
467
|
-
if (!agentId) {
|
|
468
|
-
return { cancelled: true };
|
|
469
|
-
}
|
|
762
|
+
// 1. Abort the for-await loop in handlePrompt so it stops yielding updates
|
|
763
|
+
streamState.abortController.abort();
|
|
470
764
|
|
|
471
|
-
//
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
765
|
+
// 2. Cancel the agent's active session (sends session/cancel to the subprocess)
|
|
766
|
+
// This does NOT terminate the agent — it only interrupts the current prompt.
|
|
767
|
+
// The subprocess stays alive and can accept new prompts.
|
|
768
|
+
// Use map/agents/stop for full agent termination.
|
|
769
|
+
if (agentId) {
|
|
770
|
+
const session = this.agentManager.getSession(agentId);
|
|
771
|
+
if (session) {
|
|
772
|
+
try {
|
|
773
|
+
await session.cancel();
|
|
774
|
+
console.error(
|
|
775
|
+
`[ACP-over-MAP] Session cancelled for agent ${agentId}`,
|
|
776
|
+
);
|
|
777
|
+
} catch (error) {
|
|
778
|
+
console.warn(
|
|
779
|
+
`[ACP-over-MAP] session.cancel() failed for ${agentId}:`,
|
|
780
|
+
error,
|
|
781
|
+
);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
476
784
|
}
|
|
477
785
|
|
|
478
|
-
// Clean up
|
|
479
|
-
this.sessionMapper.removeMapping(sessionId as ACPSessionId);
|
|
480
|
-
|
|
481
786
|
return { cancelled: true };
|
|
482
787
|
}
|
|
483
788
|
|
|
@@ -490,7 +795,7 @@ export class ACPOverMAPHandler {
|
|
|
490
795
|
method: string,
|
|
491
796
|
params: unknown,
|
|
492
797
|
): Promise<unknown> {
|
|
493
|
-
const methodParams = params as Record<string, unknown> ?? {};
|
|
798
|
+
const methodParams = (params as Record<string, unknown>) ?? {};
|
|
494
799
|
|
|
495
800
|
switch (method) {
|
|
496
801
|
case "_macro/spawnAgent": {
|
|
@@ -498,7 +803,7 @@ export class ACPOverMAPHandler {
|
|
|
498
803
|
task: string;
|
|
499
804
|
cwd?: string;
|
|
500
805
|
topics?: string[];
|
|
501
|
-
config?:
|
|
806
|
+
config?: AgentConfig;
|
|
502
807
|
parentId?: string;
|
|
503
808
|
};
|
|
504
809
|
|
|
@@ -522,8 +827,13 @@ export class ACPOverMAPHandler {
|
|
|
522
827
|
task,
|
|
523
828
|
cwd: cwd ?? this.defaultCwd,
|
|
524
829
|
role: "worker",
|
|
830
|
+
topics,
|
|
831
|
+
config,
|
|
525
832
|
});
|
|
526
833
|
|
|
834
|
+
// Notify subscribers that a new agent was registered
|
|
835
|
+
this.notifyAgentRegistered(spawned.id);
|
|
836
|
+
|
|
527
837
|
return {
|
|
528
838
|
agentId: spawned.id,
|
|
529
839
|
sessionId: spawned.session_id,
|
|
@@ -560,10 +870,19 @@ export class ACPOverMAPHandler {
|
|
|
560
870
|
throw new Error(`Agent not found: ${agentId}`);
|
|
561
871
|
}
|
|
562
872
|
|
|
873
|
+
// If the agent is already running, return its current state
|
|
874
|
+
// instead of throwing. The caller likely just wants to ensure
|
|
875
|
+
// the agent is active, which it already is.
|
|
563
876
|
if (agent.state !== "stopped" && agent.state !== "failed") {
|
|
564
|
-
|
|
565
|
-
`Agent ${agentId} is ${agent.state}
|
|
877
|
+
console.error(
|
|
878
|
+
`[ACP-over-MAP] _macro/resume: Agent ${agentId} is already ${agent.state}, returning current state`,
|
|
566
879
|
);
|
|
880
|
+
return {
|
|
881
|
+
success: true,
|
|
882
|
+
agentId: agent.id,
|
|
883
|
+
sessionId: agent.session_id,
|
|
884
|
+
alreadyRunning: true,
|
|
885
|
+
};
|
|
567
886
|
}
|
|
568
887
|
|
|
569
888
|
const spawned = await this.agentManager.resume(agentId as AgentId);
|
|
@@ -575,7 +894,11 @@ export class ACPOverMAPHandler {
|
|
|
575
894
|
}
|
|
576
895
|
|
|
577
896
|
case "_macro/getHistory": {
|
|
578
|
-
const {
|
|
897
|
+
const {
|
|
898
|
+
sessionId,
|
|
899
|
+
agentId: historyAgentId,
|
|
900
|
+
limit,
|
|
901
|
+
} = methodParams as {
|
|
579
902
|
sessionId?: string;
|
|
580
903
|
agentId?: string;
|
|
581
904
|
limit?: number;
|
|
@@ -586,12 +909,12 @@ export class ACPOverMAPHandler {
|
|
|
586
909
|
// explicit sessionId. This allows history to survive across server
|
|
587
910
|
// restarts even when the ACP session ID changes (e.g., resume()
|
|
588
911
|
// fails → TUI creates new session with different ID).
|
|
912
|
+
const agent = historyAgentId
|
|
913
|
+
? this.eventStore.getAgent(historyAgentId as AgentId)
|
|
914
|
+
: undefined;
|
|
589
915
|
let conversationId: string | undefined;
|
|
590
|
-
if (
|
|
591
|
-
|
|
592
|
-
if (agent) {
|
|
593
|
-
conversationId = agent.session_id;
|
|
594
|
-
}
|
|
916
|
+
if (agent) {
|
|
917
|
+
conversationId = agent.session_id;
|
|
595
918
|
}
|
|
596
919
|
if (!conversationId) {
|
|
597
920
|
conversationId = sessionId;
|
|
@@ -600,11 +923,43 @@ export class ACPOverMAPHandler {
|
|
|
600
923
|
return { turns: [] };
|
|
601
924
|
}
|
|
602
925
|
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
926
|
+
// For forked agents, include the source agent's conversation history
|
|
927
|
+
// (pre-fork turns) followed by this agent's own turns.
|
|
928
|
+
// Only include source turns from before the fork to avoid leaking
|
|
929
|
+
// turns that the source recorded after the fork point.
|
|
930
|
+
const sourceAgentId = agent?.metadata?.fork_of as string | undefined;
|
|
931
|
+
let turns;
|
|
932
|
+
if (sourceAgentId) {
|
|
933
|
+
const sourceAgent = this.eventStore.getAgent(
|
|
934
|
+
sourceAgentId as AgentId,
|
|
935
|
+
);
|
|
936
|
+
const sourceConversationId = sourceAgent?.session_id;
|
|
937
|
+
const forkTimestamp = agent!.created_at;
|
|
938
|
+
const sourceTurns = sourceConversationId
|
|
939
|
+
? this.eventStore
|
|
940
|
+
.listTurns({
|
|
941
|
+
conversationId: sourceConversationId,
|
|
942
|
+
order: "asc",
|
|
943
|
+
limit: limit ?? 200,
|
|
944
|
+
})
|
|
945
|
+
.filter((t) => t.timestamp <= forkTimestamp)
|
|
946
|
+
: [];
|
|
947
|
+
const ownTurns = this.eventStore.listTurns({
|
|
948
|
+
conversationId,
|
|
949
|
+
order: "asc",
|
|
950
|
+
limit: limit ?? 200,
|
|
951
|
+
});
|
|
952
|
+
turns = [...sourceTurns, ...ownTurns];
|
|
953
|
+
} else {
|
|
954
|
+
turns = this.eventStore.listTurns({
|
|
955
|
+
conversationId,
|
|
956
|
+
order: "asc",
|
|
957
|
+
limit: limit ?? 200,
|
|
958
|
+
});
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
const plan = agent?.plan ?? [];
|
|
962
|
+
|
|
608
963
|
return {
|
|
609
964
|
turns: turns.map((turn) => ({
|
|
610
965
|
role:
|
|
@@ -614,7 +969,341 @@ export class ACPOverMAPHandler {
|
|
|
614
969
|
timestamp: turn.timestamp,
|
|
615
970
|
content: turn.content,
|
|
616
971
|
})),
|
|
972
|
+
plan,
|
|
973
|
+
cwd: agent?.cwd ?? null,
|
|
974
|
+
};
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
case "_macro/respondToPermission": {
|
|
978
|
+
const {
|
|
979
|
+
agentId: targetAgentId,
|
|
980
|
+
requestId,
|
|
981
|
+
optionId,
|
|
982
|
+
} = methodParams as {
|
|
983
|
+
agentId: string;
|
|
984
|
+
requestId: string;
|
|
985
|
+
optionId: string;
|
|
986
|
+
};
|
|
987
|
+
if (!targetAgentId || !requestId || !optionId) {
|
|
988
|
+
throw new Error("agentId, requestId, and optionId are required");
|
|
989
|
+
}
|
|
990
|
+
const success = this.agentManager.respondToPermission(
|
|
991
|
+
targetAgentId as AgentId,
|
|
992
|
+
requestId,
|
|
993
|
+
optionId,
|
|
994
|
+
);
|
|
995
|
+
return { success };
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
case "_macro/cancelPermission": {
|
|
999
|
+
const { agentId: targetAgentId, requestId } = methodParams as {
|
|
1000
|
+
agentId: string;
|
|
1001
|
+
requestId: string;
|
|
617
1002
|
};
|
|
1003
|
+
if (!targetAgentId || !requestId) {
|
|
1004
|
+
throw new Error("agentId and requestId are required");
|
|
1005
|
+
}
|
|
1006
|
+
const success = this.agentManager.cancelPermission(
|
|
1007
|
+
targetAgentId as AgentId,
|
|
1008
|
+
requestId,
|
|
1009
|
+
);
|
|
1010
|
+
return { success };
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
case "_macro/setPermissionMode": {
|
|
1014
|
+
const { agentId: targetAgentId, permissionMode } = methodParams as {
|
|
1015
|
+
agentId: string;
|
|
1016
|
+
permissionMode: string;
|
|
1017
|
+
};
|
|
1018
|
+
if (!targetAgentId || !permissionMode) {
|
|
1019
|
+
throw new Error("agentId and permissionMode are required");
|
|
1020
|
+
}
|
|
1021
|
+
const previousMode = this.agentManager.getPermissionMode(
|
|
1022
|
+
targetAgentId as AgentId,
|
|
1023
|
+
);
|
|
1024
|
+
const success = this.agentManager.setPermissionMode(
|
|
1025
|
+
targetAgentId as AgentId,
|
|
1026
|
+
permissionMode as
|
|
1027
|
+
| "auto-approve"
|
|
1028
|
+
| "auto-deny"
|
|
1029
|
+
| "callback"
|
|
1030
|
+
| "interactive",
|
|
1031
|
+
);
|
|
1032
|
+
if (success) {
|
|
1033
|
+
return { success: true, previousMode: previousMode ?? undefined };
|
|
1034
|
+
}
|
|
1035
|
+
return {
|
|
1036
|
+
success: false,
|
|
1037
|
+
error: `No active session found for agent ${targetAgentId}`,
|
|
1038
|
+
};
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
case "_macro/forkAgent": {
|
|
1042
|
+
const { agentId, name, prompt, cwd } = methodParams as {
|
|
1043
|
+
agentId: string;
|
|
1044
|
+
name?: string;
|
|
1045
|
+
prompt?: string;
|
|
1046
|
+
cwd?: string;
|
|
1047
|
+
};
|
|
1048
|
+
if (!agentId) {
|
|
1049
|
+
throw new Error("agentId is required");
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
const sourceAgent = this.eventStore.getAgent(agentId as AgentId);
|
|
1053
|
+
if (!sourceAgent) {
|
|
1054
|
+
throw new Error(`Agent not found: ${agentId}`);
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
const forked = await this.agentManager.forkAgent(agentId as AgentId, {
|
|
1058
|
+
name,
|
|
1059
|
+
prompt,
|
|
1060
|
+
cwd: cwd ?? sourceAgent.cwd ?? this.defaultCwd,
|
|
1061
|
+
});
|
|
1062
|
+
|
|
1063
|
+
// Fire-and-forget initial prompt if provided
|
|
1064
|
+
if (prompt) {
|
|
1065
|
+
(async () => {
|
|
1066
|
+
try {
|
|
1067
|
+
for await (const _update of this.agentManager.prompt(
|
|
1068
|
+
forked.id,
|
|
1069
|
+
prompt,
|
|
1070
|
+
)) {
|
|
1071
|
+
// drain iterator
|
|
1072
|
+
}
|
|
1073
|
+
} catch {
|
|
1074
|
+
// best-effort
|
|
1075
|
+
}
|
|
1076
|
+
})();
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
return {
|
|
1080
|
+
newAgentId: forked.id,
|
|
1081
|
+
newSessionId: forked.session_id,
|
|
1082
|
+
originalAgentId: agentId,
|
|
1083
|
+
providerSessionId: forked.session?.id,
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
case "_macro/agents/update": {
|
|
1088
|
+
const { agentId, name, plan, metadata } = methodParams as {
|
|
1089
|
+
agentId?: string;
|
|
1090
|
+
name?: string;
|
|
1091
|
+
plan?: Array<{ content: string; priority: string; status: string }>;
|
|
1092
|
+
metadata?: Record<string, unknown>;
|
|
1093
|
+
};
|
|
1094
|
+
if (!agentId) {
|
|
1095
|
+
throw new Error("agentId is required");
|
|
1096
|
+
}
|
|
1097
|
+
if (
|
|
1098
|
+
name === undefined &&
|
|
1099
|
+
plan === undefined &&
|
|
1100
|
+
metadata === undefined
|
|
1101
|
+
) {
|
|
1102
|
+
throw new Error(
|
|
1103
|
+
"At least one field to update is required (name, plan, or metadata)",
|
|
1104
|
+
);
|
|
1105
|
+
}
|
|
1106
|
+
if (name !== undefined && !name.trim()) {
|
|
1107
|
+
throw new Error("name must not be empty");
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
const agent = this.eventStore.getAgent(agentId as AgentId);
|
|
1111
|
+
if (!agent) {
|
|
1112
|
+
throw new Error(`Agent not found: ${agentId}`);
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
const updates: Record<string, unknown> = {};
|
|
1116
|
+
const updatedFields: string[] = [];
|
|
1117
|
+
|
|
1118
|
+
if (name !== undefined) {
|
|
1119
|
+
updates.name = name.trim();
|
|
1120
|
+
updatedFields.push("name");
|
|
1121
|
+
}
|
|
1122
|
+
if (plan !== undefined) {
|
|
1123
|
+
updates.plan = plan;
|
|
1124
|
+
updatedFields.push("plan");
|
|
1125
|
+
}
|
|
1126
|
+
if (metadata !== undefined) {
|
|
1127
|
+
updates.metadata = metadata;
|
|
1128
|
+
updatedFields.push("metadata");
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
this.eventStore.updateAgentMetadata(agentId as AgentId, updates);
|
|
1132
|
+
|
|
1133
|
+
return {
|
|
1134
|
+
success: true,
|
|
1135
|
+
agentId,
|
|
1136
|
+
updated: updatedFields,
|
|
1137
|
+
};
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
case "_macro/getModels": {
|
|
1141
|
+
const { sessionId } = methodParams as { sessionId: string };
|
|
1142
|
+
const agentId = this.sessionMapper.getAgentId(
|
|
1143
|
+
sessionId as ACPSessionId,
|
|
1144
|
+
);
|
|
1145
|
+
if (!agentId) {
|
|
1146
|
+
return { currentModelId: null, availableModels: [] };
|
|
1147
|
+
}
|
|
1148
|
+
const session = this.agentManager.getSession(agentId);
|
|
1149
|
+
if (!session) {
|
|
1150
|
+
return { currentModelId: null, availableModels: [] };
|
|
1151
|
+
}
|
|
1152
|
+
// Try clientHandler's model info store first (from _model_state_update notification)
|
|
1153
|
+
const clientHandler = (
|
|
1154
|
+
session as unknown as {
|
|
1155
|
+
clientHandler?: {
|
|
1156
|
+
getSessionModelInfo?: (id: string) => {
|
|
1157
|
+
currentModelId: string | null;
|
|
1158
|
+
availableModels: Array<{ modelId: string; name: string }>;
|
|
1159
|
+
} | null;
|
|
1160
|
+
};
|
|
1161
|
+
}
|
|
1162
|
+
).clientHandler;
|
|
1163
|
+
const modelInfo = clientHandler?.getSessionModelInfo?.(session.id);
|
|
1164
|
+
if (modelInfo && modelInfo.availableModels.length > 0) {
|
|
1165
|
+
return modelInfo;
|
|
1166
|
+
}
|
|
1167
|
+
// Fall back to Session.models (from initial session response — just IDs)
|
|
1168
|
+
if (session.models && session.models.length > 0) {
|
|
1169
|
+
return {
|
|
1170
|
+
currentModelId: session.models[0],
|
|
1171
|
+
availableModels: session.models.map((id: string) => ({
|
|
1172
|
+
modelId: id,
|
|
1173
|
+
name: id,
|
|
1174
|
+
})),
|
|
1175
|
+
};
|
|
1176
|
+
}
|
|
1177
|
+
return { currentModelId: null, availableModels: [] };
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
case "_session/setCompaction": {
|
|
1181
|
+
// Compaction is handled internally by the agent process.
|
|
1182
|
+
// Accept the request as a no-op so the client doesn't get an error.
|
|
1183
|
+
// TODO: Make sure this overrides if needed.
|
|
1184
|
+
return { success: true };
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
// ── Task Management Extensions ────────────────────────────────
|
|
1188
|
+
// These mirror the registered adapter extensions (_macro/task/*)
|
|
1189
|
+
// so they're accessible via ACP-over-MAP streams.
|
|
1190
|
+
|
|
1191
|
+
case "_macro/task/list": {
|
|
1192
|
+
const { filter } = (methodParams ?? {}) as {
|
|
1193
|
+
filter?: {
|
|
1194
|
+
status?: string;
|
|
1195
|
+
assignedAgent?: string;
|
|
1196
|
+
parentTask?: string;
|
|
1197
|
+
createdBy?: string;
|
|
1198
|
+
rootTasksOnly?: boolean;
|
|
1199
|
+
};
|
|
1200
|
+
};
|
|
1201
|
+
|
|
1202
|
+
const taskFilter: TaskFilter | undefined = filter
|
|
1203
|
+
? {
|
|
1204
|
+
status: filter.status as TaskFilter["status"],
|
|
1205
|
+
assigned_agent: filter.assignedAgent as AgentId | undefined,
|
|
1206
|
+
parent_task: filter.parentTask as TaskId | undefined,
|
|
1207
|
+
created_by: filter.createdBy as AgentId | undefined,
|
|
1208
|
+
rootTasksOnly: filter.rootTasksOnly,
|
|
1209
|
+
}
|
|
1210
|
+
: undefined;
|
|
1211
|
+
|
|
1212
|
+
const tasks = this.taskManager.list(taskFilter);
|
|
1213
|
+
return {
|
|
1214
|
+
tasks: tasks.map((t) => ({
|
|
1215
|
+
id: t.id,
|
|
1216
|
+
description: t.description,
|
|
1217
|
+
status: t.status,
|
|
1218
|
+
assignedAgent: t.assigned_agent,
|
|
1219
|
+
createdBy: t.created_by,
|
|
1220
|
+
createdAt: t.created_at,
|
|
1221
|
+
parentTask: t.parent_task,
|
|
1222
|
+
isBlocked: (t as any).isBlocked,
|
|
1223
|
+
externalId: (t as any).external_id,
|
|
1224
|
+
})),
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
case "_macro/task/get": {
|
|
1229
|
+
const { taskId } = methodParams as { taskId: string };
|
|
1230
|
+
if (!taskId) throw new Error("taskId is required");
|
|
1231
|
+
const task = this.taskManager.get(taskId as TaskId);
|
|
1232
|
+
if (!task) throw new Error(`Task not found: ${taskId}`);
|
|
1233
|
+
return {
|
|
1234
|
+
task: {
|
|
1235
|
+
id: task.id,
|
|
1236
|
+
description: task.description,
|
|
1237
|
+
status: task.status,
|
|
1238
|
+
assignedAgent: task.assigned_agent,
|
|
1239
|
+
createdBy: task.created_by,
|
|
1240
|
+
createdAt: task.created_at,
|
|
1241
|
+
parentTask: task.parent_task,
|
|
1242
|
+
isBlocked: (task as any).isBlocked,
|
|
1243
|
+
externalId: (task as any).external_id,
|
|
1244
|
+
},
|
|
1245
|
+
};
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
case "_macro/task/create": {
|
|
1249
|
+
const { description, parentTask, externalId } = methodParams as {
|
|
1250
|
+
description: string;
|
|
1251
|
+
parentTask?: string;
|
|
1252
|
+
externalId?: string;
|
|
1253
|
+
};
|
|
1254
|
+
if (!description) throw new Error("description is required");
|
|
1255
|
+
|
|
1256
|
+
// Determine who is creating the task — use the stream's agent or a default
|
|
1257
|
+
const createdBy = (streamState.agentId ?? "tui") as AgentId;
|
|
1258
|
+
|
|
1259
|
+
const task = this.taskManager.create({
|
|
1260
|
+
description,
|
|
1261
|
+
created_by: createdBy,
|
|
1262
|
+
parent_task: parentTask as TaskId | undefined,
|
|
1263
|
+
});
|
|
1264
|
+
|
|
1265
|
+
return {
|
|
1266
|
+
task: {
|
|
1267
|
+
id: task.id,
|
|
1268
|
+
description: task.description,
|
|
1269
|
+
status: task.status,
|
|
1270
|
+
assignedAgent: task.assigned_agent,
|
|
1271
|
+
createdBy: task.created_by,
|
|
1272
|
+
createdAt: task.created_at,
|
|
1273
|
+
parentTask: task.parent_task,
|
|
1274
|
+
},
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
case "_macro/task/assign": {
|
|
1279
|
+
const { taskId, agentId, role } = methodParams as {
|
|
1280
|
+
taskId: string;
|
|
1281
|
+
agentId: string;
|
|
1282
|
+
role?: string;
|
|
1283
|
+
};
|
|
1284
|
+
if (!taskId) throw new Error("taskId is required");
|
|
1285
|
+
if (!agentId) throw new Error("agentId is required");
|
|
1286
|
+
|
|
1287
|
+
this.taskManager.assign(taskId as TaskId, agentId as AgentId, role);
|
|
1288
|
+
return { success: true };
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
case "_macro/task/complete": {
|
|
1292
|
+
const { taskId, outputs } = methodParams as {
|
|
1293
|
+
taskId: string;
|
|
1294
|
+
outputs?: { summary?: string; data?: unknown };
|
|
1295
|
+
};
|
|
1296
|
+
if (!taskId) throw new Error("taskId is required");
|
|
1297
|
+
|
|
1298
|
+
// Update status to completed
|
|
1299
|
+
this.taskManager.updateStatus(taskId as TaskId, "completed");
|
|
1300
|
+
|
|
1301
|
+
// If outputs provided, update task metadata
|
|
1302
|
+
if (outputs) {
|
|
1303
|
+
this.taskManager.update(taskId as TaskId, { outputs });
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
return { success: true };
|
|
618
1307
|
}
|
|
619
1308
|
|
|
620
1309
|
default:
|
|
@@ -668,7 +1357,12 @@ export class ACPOverMAPHandler {
|
|
|
668
1357
|
acpSessionId: ACPSessionId,
|
|
669
1358
|
agentId: AgentId,
|
|
670
1359
|
userMessage: string,
|
|
671
|
-
buffer: {
|
|
1360
|
+
buffer: {
|
|
1361
|
+
parts: Array<
|
|
1362
|
+
| { type: "text"; text: string }
|
|
1363
|
+
| ({ type: "tool" } & Record<string, unknown>)
|
|
1364
|
+
>;
|
|
1365
|
+
},
|
|
672
1366
|
): void {
|
|
673
1367
|
const now = Date.now();
|
|
674
1368
|
|
|
@@ -691,17 +1385,8 @@ export class ACPOverMAPHandler {
|
|
|
691
1385
|
});
|
|
692
1386
|
}
|
|
693
1387
|
|
|
694
|
-
// Record assistant turn with accumulated content
|
|
695
|
-
const
|
|
696
|
-
const parts: unknown[] = [];
|
|
697
|
-
|
|
698
|
-
if (assistantText) {
|
|
699
|
-
parts.push({ type: "text", text: assistantText });
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
for (const tool of buffer.toolCalls) {
|
|
703
|
-
parts.push({ type: "tool", ...tool });
|
|
704
|
-
}
|
|
1388
|
+
// Record assistant turn with accumulated content (parts already in order)
|
|
1389
|
+
const parts = buffer.parts;
|
|
705
1390
|
|
|
706
1391
|
if (parts.length > 0) {
|
|
707
1392
|
this.eventStore.emit({
|