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
|
@@ -687,8 +687,8 @@ Commit "Resolve conflict".
|
|
|
687
687
|
// The agents are terminated manually because they don't autonomously call done()
|
|
688
688
|
const allEvents = eventStore.query({});
|
|
689
689
|
const spawnCount = allEvents.filter((e) => e.type === "spawn").length;
|
|
690
|
-
const terminateCount = allEvents.filter((e) => e.type === "
|
|
691
|
-
log(`✓ Events: ${spawnCount} spawns, ${terminateCount}
|
|
690
|
+
const terminateCount = allEvents.filter((e) => e.type === "stop").length;
|
|
691
|
+
log(`✓ Events: ${spawnCount} spawns, ${terminateCount} stops`);
|
|
692
692
|
|
|
693
693
|
expect(spawnCount).toBeGreaterThanOrEqual(3); // w1, w2, resolver
|
|
694
694
|
expect(terminateCount).toBeGreaterThanOrEqual(3); // w1, w2, resolver
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E2E Integration tests for MCP Thin-Client Bridge
|
|
3
|
+
*
|
|
4
|
+
* Starts a real combined server with real services, then uses mapCall()
|
|
5
|
+
* over WebSocket to verify the complete round-trip through MCP bridge
|
|
6
|
+
* extension handlers.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
10
|
+
import { createEventStore, type EventStore } from "../../store/event-store.js";
|
|
11
|
+
import { createAgentManager, type AgentManager } from "../../agent/agent-manager.js";
|
|
12
|
+
import { createTaskManager, type TaskManager } from "../../task/task-manager.js";
|
|
13
|
+
import { createMessageRouter, type MessageRouter } from "../../router/message-router.js";
|
|
14
|
+
import {
|
|
15
|
+
createCombinedServer,
|
|
16
|
+
type CombinedServer,
|
|
17
|
+
} from "../../server/combined-server.js";
|
|
18
|
+
import { mapCall, MapCallError } from "../../mcp/map-client.js";
|
|
19
|
+
|
|
20
|
+
// =============================================================================
|
|
21
|
+
// Test Helpers
|
|
22
|
+
// =============================================================================
|
|
23
|
+
|
|
24
|
+
function getRandomPort(): number {
|
|
25
|
+
return 10000 + Math.floor(Math.random() * 50000);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Valid agent context for bridge calls.
|
|
30
|
+
* Must match an agent that exists in EventStore.
|
|
31
|
+
*/
|
|
32
|
+
function agentContext(agentId: string, taskId?: string) {
|
|
33
|
+
return {
|
|
34
|
+
agent_id: agentId,
|
|
35
|
+
session_id: "sess_test",
|
|
36
|
+
task_id: taskId ?? "task_test",
|
|
37
|
+
lineage: [],
|
|
38
|
+
cwd: "/test/cwd",
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// =============================================================================
|
|
43
|
+
// Tests
|
|
44
|
+
// =============================================================================
|
|
45
|
+
|
|
46
|
+
describe("MCP Thin-Client Bridge E2E", () => {
|
|
47
|
+
let eventStore: EventStore;
|
|
48
|
+
let agentManager: AgentManager;
|
|
49
|
+
let taskManager: TaskManager;
|
|
50
|
+
let messageRouter: MessageRouter;
|
|
51
|
+
let server: CombinedServer;
|
|
52
|
+
let port: number;
|
|
53
|
+
let serverUrl: string;
|
|
54
|
+
|
|
55
|
+
beforeEach(async () => {
|
|
56
|
+
port = getRandomPort();
|
|
57
|
+
serverUrl = `http://localhost:${port}`;
|
|
58
|
+
|
|
59
|
+
// Create real services with in-memory EventStore
|
|
60
|
+
eventStore = await createEventStore({ inMemory: true });
|
|
61
|
+
messageRouter = createMessageRouter(eventStore);
|
|
62
|
+
agentManager = createAgentManager(eventStore, messageRouter, {
|
|
63
|
+
defaultPermissionMode: "auto-approve",
|
|
64
|
+
defaultCwd: "/test/cwd",
|
|
65
|
+
});
|
|
66
|
+
taskManager = createTaskManager(eventStore);
|
|
67
|
+
|
|
68
|
+
// Create and start combined server (includes MCP bridge registration)
|
|
69
|
+
server = createCombinedServer(
|
|
70
|
+
{ eventStore, agentManager, taskManager, messageRouter },
|
|
71
|
+
{ port, host: "localhost" }
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
await server.start();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
afterEach(async () => {
|
|
78
|
+
await server.stop();
|
|
79
|
+
await agentManager.close();
|
|
80
|
+
await eventStore.close();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// ─────────────────────────────────────────────────────────────────
|
|
84
|
+
// emit_status round-trip
|
|
85
|
+
// ─────────────────────────────────────────────────────────────────
|
|
86
|
+
|
|
87
|
+
describe("emit_status round-trip", () => {
|
|
88
|
+
it("emits a status event via bridge and verifies it in EventStore", async () => {
|
|
89
|
+
// Seed an agent in the EventStore
|
|
90
|
+
eventStore.emit({
|
|
91
|
+
type: "spawn",
|
|
92
|
+
source: { agent_id: "system" },
|
|
93
|
+
payload: {
|
|
94
|
+
agent_id: "agent_e2e",
|
|
95
|
+
session_id: "sess_e2e",
|
|
96
|
+
task: "E2E test task",
|
|
97
|
+
task_id: "task_e2e",
|
|
98
|
+
parent: null,
|
|
99
|
+
config: {},
|
|
100
|
+
cwd: "/test/cwd",
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const result = await mapCall<{ event_id: string; task_updated: boolean }>(
|
|
105
|
+
serverUrl,
|
|
106
|
+
"_macro/mcp/emit_status",
|
|
107
|
+
{
|
|
108
|
+
context: agentContext("agent_e2e", "task_e2e"),
|
|
109
|
+
status_type: "checkpoint",
|
|
110
|
+
summary: "50% complete",
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
expect(result.event_id).toBeDefined();
|
|
115
|
+
expect(result.task_updated).toBe(false);
|
|
116
|
+
|
|
117
|
+
// Verify the event was actually stored
|
|
118
|
+
const events = eventStore.query({
|
|
119
|
+
type: "status",
|
|
120
|
+
source_agent_id: "agent_e2e",
|
|
121
|
+
});
|
|
122
|
+
expect(events.length).toBeGreaterThanOrEqual(1);
|
|
123
|
+
expect(events[0].payload.summary).toBe("50% complete");
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// ─────────────────────────────────────────────────────────────────
|
|
128
|
+
// query_index round-trip
|
|
129
|
+
// ─────────────────────────────────────────────────────────────────
|
|
130
|
+
|
|
131
|
+
describe("query_index round-trip", () => {
|
|
132
|
+
it("queries agents via bridge after seeding data", async () => {
|
|
133
|
+
// Seed agents
|
|
134
|
+
eventStore.emit({
|
|
135
|
+
type: "spawn",
|
|
136
|
+
source: { agent_id: "system" },
|
|
137
|
+
payload: {
|
|
138
|
+
agent_id: "agent_1",
|
|
139
|
+
session_id: "sess_1",
|
|
140
|
+
task: "First task",
|
|
141
|
+
task_id: "task_1",
|
|
142
|
+
parent: null,
|
|
143
|
+
config: {},
|
|
144
|
+
cwd: "/test",
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
eventStore.emit({
|
|
148
|
+
type: "spawn",
|
|
149
|
+
source: { agent_id: "system" },
|
|
150
|
+
payload: {
|
|
151
|
+
agent_id: "agent_2",
|
|
152
|
+
session_id: "sess_2",
|
|
153
|
+
task: "Second task",
|
|
154
|
+
task_id: "task_2",
|
|
155
|
+
parent: null,
|
|
156
|
+
config: {},
|
|
157
|
+
cwd: "/test",
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
const result = await mapCall<{
|
|
162
|
+
entries: Array<{ type: string; id: string; summary: string }>;
|
|
163
|
+
total: number;
|
|
164
|
+
has_more: boolean;
|
|
165
|
+
}>(serverUrl, "_macro/mcp/query_index", {
|
|
166
|
+
context: agentContext("agent_1"),
|
|
167
|
+
type: "agents",
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
expect(result.entries.length).toBeGreaterThanOrEqual(2);
|
|
171
|
+
expect(result.entries.some((e) => e.id === "agent_1")).toBe(true);
|
|
172
|
+
expect(result.entries.some((e) => e.id === "agent_2")).toBe(true);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("queries tasks via bridge", async () => {
|
|
176
|
+
// Create a task
|
|
177
|
+
taskManager.create({
|
|
178
|
+
description: "E2E test task",
|
|
179
|
+
created_by: "agent_test",
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const result = await mapCall<{
|
|
183
|
+
entries: Array<{ type: string; id: string; summary: string }>;
|
|
184
|
+
total: number;
|
|
185
|
+
}>(serverUrl, "_macro/mcp/query_index", {
|
|
186
|
+
context: agentContext("agent_test"),
|
|
187
|
+
type: "tasks",
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
expect(result.entries.length).toBeGreaterThanOrEqual(1);
|
|
191
|
+
expect(result.entries[0].type).toBe("task");
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// ─────────────────────────────────────────────────────────────────
|
|
196
|
+
// send_message + check_messages round-trip
|
|
197
|
+
// ─────────────────────────────────────────────────────────────────
|
|
198
|
+
|
|
199
|
+
describe("send_message + check_messages round-trip", () => {
|
|
200
|
+
it("sends and receives a message via bridge", async () => {
|
|
201
|
+
// Seed two agents
|
|
202
|
+
eventStore.emit({
|
|
203
|
+
type: "spawn",
|
|
204
|
+
source: { agent_id: "system" },
|
|
205
|
+
payload: {
|
|
206
|
+
agent_id: "agent_sender",
|
|
207
|
+
session_id: "sess_sender",
|
|
208
|
+
task: "Sender task",
|
|
209
|
+
task_id: "task_sender",
|
|
210
|
+
parent: null,
|
|
211
|
+
config: {},
|
|
212
|
+
cwd: "/test",
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
eventStore.emit({
|
|
216
|
+
type: "spawn",
|
|
217
|
+
source: { agent_id: "system" },
|
|
218
|
+
payload: {
|
|
219
|
+
agent_id: "agent_receiver",
|
|
220
|
+
session_id: "sess_receiver",
|
|
221
|
+
task: "Receiver task",
|
|
222
|
+
task_id: "task_receiver",
|
|
223
|
+
parent: null,
|
|
224
|
+
config: {},
|
|
225
|
+
cwd: "/test",
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// Subscribe receiver to their own address
|
|
230
|
+
eventStore.addSubscription("agent_receiver", "agent:agent_receiver");
|
|
231
|
+
|
|
232
|
+
// Send message via bridge
|
|
233
|
+
const sendResult = await mapCall<{ message_id: string; delivered_to: number }>(
|
|
234
|
+
serverUrl,
|
|
235
|
+
"_macro/mcp/send_message",
|
|
236
|
+
{
|
|
237
|
+
context: agentContext("agent_sender"),
|
|
238
|
+
to: { agent_id: "agent_receiver" },
|
|
239
|
+
content: "Hello from E2E test",
|
|
240
|
+
}
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
expect(sendResult.message_id).toBeDefined();
|
|
244
|
+
expect(sendResult.delivered_to).toBeGreaterThanOrEqual(1);
|
|
245
|
+
|
|
246
|
+
// Check messages via bridge
|
|
247
|
+
const checkResult = await mapCall<{
|
|
248
|
+
messages: Array<{ id: string; content: string; from: string }>;
|
|
249
|
+
total_pending: number;
|
|
250
|
+
}>(serverUrl, "_macro/mcp/check_messages", {
|
|
251
|
+
context: agentContext("agent_receiver"),
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
expect(checkResult.messages.length).toBeGreaterThanOrEqual(1);
|
|
255
|
+
expect(checkResult.messages.some((m) => m.content === "Hello from E2E test")).toBe(true);
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// ─────────────────────────────────────────────────────────────────
|
|
260
|
+
// Context validation over the wire
|
|
261
|
+
// ─────────────────────────────────────────────────────────────────
|
|
262
|
+
|
|
263
|
+
describe("context validation over the wire", () => {
|
|
264
|
+
it("rejects calls without context", async () => {
|
|
265
|
+
try {
|
|
266
|
+
await mapCall(serverUrl, "_macro/mcp/query_index", { type: "agents" });
|
|
267
|
+
expect.fail("Should have thrown");
|
|
268
|
+
} catch (err) {
|
|
269
|
+
expect(err).toBeInstanceOf(MapCallError);
|
|
270
|
+
expect((err as MapCallError).message).toContain("agent_id is required");
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// ─────────────────────────────────────────────────────────────────
|
|
276
|
+
// Error propagation
|
|
277
|
+
// ─────────────────────────────────────────────────────────────────
|
|
278
|
+
|
|
279
|
+
describe("error propagation", () => {
|
|
280
|
+
it("returns error for non-existent agent in get_agent_summary", async () => {
|
|
281
|
+
try {
|
|
282
|
+
await mapCall(serverUrl, "_macro/mcp/get_agent_summary", {
|
|
283
|
+
context: agentContext("agent_test"),
|
|
284
|
+
agent_id: "nonexistent_agent",
|
|
285
|
+
});
|
|
286
|
+
expect.fail("Should have thrown");
|
|
287
|
+
} catch (err) {
|
|
288
|
+
expect(err).toBeInstanceOf(MapCallError);
|
|
289
|
+
expect((err as MapCallError).message).toContain("not found");
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it("returns error for non-existent extension method", async () => {
|
|
294
|
+
try {
|
|
295
|
+
await mapCall(serverUrl, "_macro/mcp/nonexistent_method", {
|
|
296
|
+
context: agentContext("agent_test"),
|
|
297
|
+
});
|
|
298
|
+
expect.fail("Should have thrown");
|
|
299
|
+
} catch (err) {
|
|
300
|
+
expect(err).toBeInstanceOf(MapCallError);
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
});
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Tools Availability E2E Test
|
|
3
|
+
*
|
|
4
|
+
* Verifies that agents spawned through acp-factory have access to the
|
|
5
|
+
* macro-agent MCP tools. This tests the full chain:
|
|
6
|
+
* agent-manager → acp-factory → claude-code-acp → Claude Agent SDK → MCP subprocess
|
|
7
|
+
*
|
|
8
|
+
* REQUIRES: RUN_FULL_AGENT_TESTS=true environment variable
|
|
9
|
+
*
|
|
10
|
+
* Run with:
|
|
11
|
+
* RUN_FULL_AGENT_TESTS=true npm run test:e2e -- src/__tests__/e2e/mcp-tools-available.e2e.test.ts
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
15
|
+
import * as fs from "fs";
|
|
16
|
+
import * as path from "path";
|
|
17
|
+
import * as os from "os";
|
|
18
|
+
import { execSync } from "child_process";
|
|
19
|
+
|
|
20
|
+
import { createEventStore, type EventStore } from "../../store/event-store.js";
|
|
21
|
+
import {
|
|
22
|
+
createAgentManager,
|
|
23
|
+
type AgentManager,
|
|
24
|
+
} from "../../agent/agent-manager.js";
|
|
25
|
+
import {
|
|
26
|
+
createMessageRouter,
|
|
27
|
+
type MessageRouter,
|
|
28
|
+
} from "../../router/message-router.js";
|
|
29
|
+
|
|
30
|
+
// ─────────────────────────────────────────────────────────────────
|
|
31
|
+
// Test Configuration
|
|
32
|
+
// ─────────────────────────────────────────────────────────────────
|
|
33
|
+
|
|
34
|
+
const RUN_FULL_AGENT = !!process.env.RUN_FULL_AGENT_TESTS;
|
|
35
|
+
const testFn = RUN_FULL_AGENT ? it : it.skip;
|
|
36
|
+
|
|
37
|
+
// MCP server name as configured in agent-manager.ts
|
|
38
|
+
const MCP_SERVER_NAME = "macro-agent";
|
|
39
|
+
|
|
40
|
+
// Core coordination tools always registered for any agent role
|
|
41
|
+
const EXPECTED_CORE_TOOLS = [
|
|
42
|
+
"done",
|
|
43
|
+
"spawn_agent",
|
|
44
|
+
"emit_status",
|
|
45
|
+
"send_message",
|
|
46
|
+
"check_messages",
|
|
47
|
+
"get_hierarchy",
|
|
48
|
+
"get_agent_summary",
|
|
49
|
+
"query_index",
|
|
50
|
+
"inject_context",
|
|
51
|
+
"wait_for_activity",
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
// Peer communication tools (registered when PeerManager is available)
|
|
55
|
+
const PEER_TOOLS = [
|
|
56
|
+
"send_peer_message",
|
|
57
|
+
"send_peer_request",
|
|
58
|
+
"respond_to_peer_request",
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
// Role-gated tools that may not be available to all roles
|
|
62
|
+
const ROLE_GATED_TOOLS = [
|
|
63
|
+
"stop_agent", // requires agent.terminate capability
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
// Task backend tools (memory backend, push mode) — registered via taskToolProvider
|
|
67
|
+
const TASK_BACKEND_TOOLS = [
|
|
68
|
+
"create_task",
|
|
69
|
+
"get_task",
|
|
70
|
+
"list_tasks",
|
|
71
|
+
"assign_task",
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Build the full MCP tool name as it appears in Claude Code.
|
|
76
|
+
* Format: mcp__<server-name>__<tool-name>
|
|
77
|
+
*/
|
|
78
|
+
function mcpToolName(tool: string): string {
|
|
79
|
+
return `mcp__${MCP_SERVER_NAME}__${tool}`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ─────────────────────────────────────────────────────────────────
|
|
83
|
+
// Helpers
|
|
84
|
+
// ─────────────────────────────────────────────────────────────────
|
|
85
|
+
|
|
86
|
+
interface ToolCallInfo {
|
|
87
|
+
toolName: string;
|
|
88
|
+
toolCallId: string;
|
|
89
|
+
status: string;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Extract tool_call events from session update stream.
|
|
94
|
+
*
|
|
95
|
+
* Claude Code reports MCP tool usage via session updates:
|
|
96
|
+
* { sessionUpdate: "tool_call", _meta: { claudeCode: { toolName } }, toolCallId, status }
|
|
97
|
+
*/
|
|
98
|
+
function extractToolCalls(updates: Record<string, unknown>[]): ToolCallInfo[] {
|
|
99
|
+
const calls: ToolCallInfo[] = [];
|
|
100
|
+
for (const update of updates) {
|
|
101
|
+
if (update.sessionUpdate === "tool_call") {
|
|
102
|
+
const meta = update._meta as
|
|
103
|
+
| { claudeCode?: { toolName?: string } }
|
|
104
|
+
| undefined;
|
|
105
|
+
const toolName = meta?.claudeCode?.toolName ?? (update as { title?: string }).title ?? "unknown";
|
|
106
|
+
calls.push({
|
|
107
|
+
toolName,
|
|
108
|
+
toolCallId: (update.toolCallId as string) ?? "",
|
|
109
|
+
status: (update.status as string) ?? "",
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return calls;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ─────────────────────────────────────────────────────────────────
|
|
117
|
+
// Tests
|
|
118
|
+
// ─────────────────────────────────────────────────────────────────
|
|
119
|
+
|
|
120
|
+
describe("MCP Tools Availability", () => {
|
|
121
|
+
let eventStore: EventStore;
|
|
122
|
+
let agentManager: AgentManager;
|
|
123
|
+
let messageRouter: MessageRouter;
|
|
124
|
+
let tmpDir: string;
|
|
125
|
+
let testRepoPath: string;
|
|
126
|
+
|
|
127
|
+
beforeEach(async () => {
|
|
128
|
+
if (!RUN_FULL_AGENT) return;
|
|
129
|
+
|
|
130
|
+
// File-based EventStore so MCP subprocess can access the same database
|
|
131
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "mcp-tools-e2e-"));
|
|
132
|
+
const instanceId = `test-mcp-tools-${Date.now()}`;
|
|
133
|
+
|
|
134
|
+
// Isolated git repo for agent cwd
|
|
135
|
+
testRepoPath = path.join(tmpDir, "test-repo");
|
|
136
|
+
fs.mkdirSync(testRepoPath);
|
|
137
|
+
execSync("git init", { cwd: testRepoPath });
|
|
138
|
+
execSync('git config user.email "test@test.com"', { cwd: testRepoPath });
|
|
139
|
+
execSync('git config user.name "Test User"', { cwd: testRepoPath });
|
|
140
|
+
fs.writeFileSync(path.join(testRepoPath, "README.md"), "# Test Repo\n");
|
|
141
|
+
execSync("git add -A", { cwd: testRepoPath });
|
|
142
|
+
execSync('git commit -m "Initial commit"', { cwd: testRepoPath });
|
|
143
|
+
|
|
144
|
+
eventStore = await createEventStore({ instanceId, baseDir: tmpDir });
|
|
145
|
+
messageRouter = createMessageRouter(eventStore);
|
|
146
|
+
agentManager = createAgentManager(eventStore, messageRouter, {
|
|
147
|
+
defaultPermissionMode: "auto-approve",
|
|
148
|
+
defaultCwd: testRepoPath,
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
afterEach(async () => {
|
|
153
|
+
if (!RUN_FULL_AGENT) return;
|
|
154
|
+
|
|
155
|
+
// Terminate all running agents
|
|
156
|
+
try {
|
|
157
|
+
for (const agent of agentManager.list()) {
|
|
158
|
+
if (agent.state === "running") {
|
|
159
|
+
await agentManager.terminate(agent.id, "cancelled");
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
} catch {
|
|
163
|
+
// Ignore cleanup errors
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
await agentManager?.close();
|
|
167
|
+
await eventStore?.close();
|
|
168
|
+
|
|
169
|
+
if (tmpDir) {
|
|
170
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
testFn(
|
|
175
|
+
"spawned agent can use macro-agent MCP tools (emit_status, check_messages, done)",
|
|
176
|
+
async () => {
|
|
177
|
+
// Spawn a worker agent
|
|
178
|
+
const spawnResult = await agentManager.spawn({
|
|
179
|
+
task: "Use MCP tools as instructed.",
|
|
180
|
+
role: "worker",
|
|
181
|
+
cwd: testRepoPath,
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// Prompt the agent to call several MCP tools so we can observe tool_call events
|
|
185
|
+
const allUpdates: Record<string, unknown>[] = [];
|
|
186
|
+
|
|
187
|
+
for await (const update of agentManager.prompt(
|
|
188
|
+
spawnResult.id,
|
|
189
|
+
`You have access to MCP tools from the "macro-agent" server. Please do the following steps IN ORDER:
|
|
190
|
+
|
|
191
|
+
1. Call emit_status with status_type "checkpoint" and summary "mcp tool verification"
|
|
192
|
+
2. Call check_messages to check your inbox
|
|
193
|
+
3. Call done with status "completed" and summary "all tools verified"
|
|
194
|
+
|
|
195
|
+
Do these steps now. Do NOT do any other work.`,
|
|
196
|
+
)) {
|
|
197
|
+
allUpdates.push(update as Record<string, unknown>);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Extract tool calls from the update stream
|
|
201
|
+
const toolCalls = extractToolCalls(allUpdates);
|
|
202
|
+
const toolNames = toolCalls.map((tc) => tc.toolName);
|
|
203
|
+
|
|
204
|
+
// Log for diagnostics
|
|
205
|
+
console.log(`\n[MCP Tools E2E] Total session updates: ${allUpdates.length}`);
|
|
206
|
+
console.log(`[MCP Tools E2E] Tool calls observed: ${toolCalls.length}`);
|
|
207
|
+
for (const tc of toolCalls) {
|
|
208
|
+
console.log(` - ${tc.toolName} (status: ${tc.status})`);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// ── Assertions ──────────────────────────────────────────────
|
|
212
|
+
|
|
213
|
+
// Agent should have made at least one tool call
|
|
214
|
+
expect(toolCalls.length).toBeGreaterThan(0);
|
|
215
|
+
|
|
216
|
+
// Verify the tools used were macro-agent MCP tools
|
|
217
|
+
const macroAgentCalls = toolNames.filter((n) =>
|
|
218
|
+
n.startsWith(`mcp__${MCP_SERVER_NAME}__`),
|
|
219
|
+
);
|
|
220
|
+
expect(
|
|
221
|
+
macroAgentCalls.length,
|
|
222
|
+
"Expected at least one macro-agent MCP tool call",
|
|
223
|
+
).toBeGreaterThan(0);
|
|
224
|
+
|
|
225
|
+
// Verify done() was called via MCP
|
|
226
|
+
expect(toolNames).toContain(mcpToolName("done"));
|
|
227
|
+
|
|
228
|
+
// Verify EventStore received the done event from MCP subprocess
|
|
229
|
+
await eventStore.reload();
|
|
230
|
+
const statusEvents = eventStore.query({ type: "status" });
|
|
231
|
+
const doneEvent = statusEvents.find(
|
|
232
|
+
(e) =>
|
|
233
|
+
e.source?.agent_id === spawnResult.id &&
|
|
234
|
+
e.payload?.status_type === "completed",
|
|
235
|
+
);
|
|
236
|
+
expect(doneEvent).toBeDefined();
|
|
237
|
+
|
|
238
|
+
// Terminate agent (may already be stopped from done())
|
|
239
|
+
const agent = agentManager.get(spawnResult.id);
|
|
240
|
+
if (agent?.state === "running") {
|
|
241
|
+
await agentManager.terminate(spawnResult.id, "completed");
|
|
242
|
+
}
|
|
243
|
+
},
|
|
244
|
+
{ timeout: 120_000 },
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
testFn(
|
|
248
|
+
"spawned agent has all expected MCP tools registered",
|
|
249
|
+
async () => {
|
|
250
|
+
// Spawn a worker and ask it to list all its MCP tools by trying to use them.
|
|
251
|
+
// We verify the tool set by asking the agent to describe its mcp__macro-agent__ tools.
|
|
252
|
+
const spawnResult = await agentManager.spawn({
|
|
253
|
+
task: "Report on your available MCP tools.",
|
|
254
|
+
role: "worker",
|
|
255
|
+
cwd: testRepoPath,
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
let responseText = "";
|
|
259
|
+
const allUpdates: Record<string, unknown>[] = [];
|
|
260
|
+
|
|
261
|
+
for await (const update of agentManager.prompt(
|
|
262
|
+
spawnResult.id,
|
|
263
|
+
`List ALL tools you have that start with "mcp__macro-agent__".
|
|
264
|
+
Just list them by name, one per line. Be exhaustive - include every single one.
|
|
265
|
+
After listing them, call done() with status "completed".`,
|
|
266
|
+
)) {
|
|
267
|
+
const updateObj = update as Record<string, unknown>;
|
|
268
|
+
allUpdates.push(updateObj);
|
|
269
|
+
|
|
270
|
+
// Collect text response
|
|
271
|
+
if (updateObj.sessionUpdate === "agent_message_chunk") {
|
|
272
|
+
const content = updateObj.content as { type?: string; text?: string } | undefined;
|
|
273
|
+
if (content?.type === "text" && content?.text) {
|
|
274
|
+
responseText += content.text;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const toolCalls = extractToolCalls(allUpdates);
|
|
280
|
+
const toolNames = toolCalls.map((tc) => tc.toolName);
|
|
281
|
+
|
|
282
|
+
console.log(`\n[MCP Tools E2E] Agent response about tools:\n${responseText}`);
|
|
283
|
+
console.log(`\n[MCP Tools E2E] Tool calls made: ${toolNames.join(", ")}`);
|
|
284
|
+
|
|
285
|
+
// The response should mention macro-agent MCP tools
|
|
286
|
+
const responseLower = responseText.toLowerCase();
|
|
287
|
+
expect(
|
|
288
|
+
responseLower.includes("mcp__macro-agent__") || responseLower.includes("done"),
|
|
289
|
+
"Agent should mention macro-agent MCP tools in response",
|
|
290
|
+
).toBe(true);
|
|
291
|
+
|
|
292
|
+
// Verify the core tools are mentioned in the response.
|
|
293
|
+
const mentionedCoreTools = EXPECTED_CORE_TOOLS.filter(
|
|
294
|
+
(tool) =>
|
|
295
|
+
responseLower.includes(mcpToolName(tool)) ||
|
|
296
|
+
responseLower.includes(tool),
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
console.log(
|
|
300
|
+
`[MCP Tools E2E] Core tools mentioned: ${mentionedCoreTools.length}/${EXPECTED_CORE_TOOLS.length}`,
|
|
301
|
+
);
|
|
302
|
+
console.log(` Mentioned: ${mentionedCoreTools.join(", ")}`);
|
|
303
|
+
const missingCoreTools = EXPECTED_CORE_TOOLS.filter(
|
|
304
|
+
(t) => !mentionedCoreTools.includes(t),
|
|
305
|
+
);
|
|
306
|
+
if (missingCoreTools.length > 0) {
|
|
307
|
+
console.log(` Missing: ${missingCoreTools.join(", ")}`);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// All core tools should be mentioned
|
|
311
|
+
expect(mentionedCoreTools.length).toBe(EXPECTED_CORE_TOOLS.length);
|
|
312
|
+
|
|
313
|
+
// done() should have been called
|
|
314
|
+
expect(toolNames).toContain(mcpToolName("done"));
|
|
315
|
+
|
|
316
|
+
// Terminate
|
|
317
|
+
const agent = agentManager.get(spawnResult.id);
|
|
318
|
+
if (agent?.state === "running") {
|
|
319
|
+
await agentManager.terminate(spawnResult.id, "completed");
|
|
320
|
+
}
|
|
321
|
+
},
|
|
322
|
+
{ timeout: 120_000 },
|
|
323
|
+
);
|
|
324
|
+
});
|
|
@@ -542,10 +542,10 @@ describe("Part 1: Multi-Agent Lifecycle E2E", () => {
|
|
|
542
542
|
`✓ Grandchild state: ${grandchildAgent?.state}, reason: ${grandchildAgent?.stop_reason}`,
|
|
543
543
|
);
|
|
544
544
|
|
|
545
|
-
// Verify
|
|
546
|
-
const terminateEvents = eventStore.query({ type: "
|
|
545
|
+
// Verify stop events
|
|
546
|
+
const terminateEvents = eventStore.query({ type: "stop" });
|
|
547
547
|
expect(terminateEvents.length).toBeGreaterThanOrEqual(3);
|
|
548
|
-
log(`✓
|
|
548
|
+
log(`✓ Stop events: ${terminateEvents.length}`);
|
|
549
549
|
},
|
|
550
550
|
{ timeout: TIMEOUT.HIERARCHY },
|
|
551
551
|
);
|
|
@@ -961,8 +961,8 @@ describe("Part 3: Event Storage E2E", () => {
|
|
|
961
961
|
// Terminate agent
|
|
962
962
|
await agentManager.terminate(headId, "completed");
|
|
963
963
|
|
|
964
|
-
// Verify
|
|
965
|
-
const terminateEvents = eventStore.query({ type: "
|
|
964
|
+
// Verify stop event
|
|
965
|
+
const terminateEvents = eventStore.query({ type: "stop" });
|
|
966
966
|
expect(terminateEvents.length).toBeGreaterThanOrEqual(1);
|
|
967
967
|
const termEvent = terminateEvents.find(
|
|
968
968
|
(e) => e.payload.agent_id === headId,
|