claude-code-swarm 0.3.3 → 0.3.5
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-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +22 -1
- package/.claude-plugin/run-agent-inbox-mcp.sh +76 -0
- package/.claude-plugin/run-minimem-mcp.sh +98 -0
- package/.claude-plugin/run-opentasks-mcp.sh +65 -0
- package/CLAUDE.md +200 -36
- package/README.md +65 -0
- package/e2e/helpers/cleanup.mjs +17 -3
- package/e2e/helpers/map-mock-server.mjs +201 -25
- package/e2e/helpers/sidecar.mjs +222 -0
- package/e2e/helpers/workspace.mjs +2 -1
- package/e2e/tier5-sidecar-inbox.test.mjs +900 -0
- package/e2e/tier6-inbox-mcp.test.mjs +173 -0
- package/e2e/tier6-live-agent.test.mjs +759 -0
- package/e2e/vitest.config.e2e.mjs +1 -1
- package/hooks/hooks.json +15 -8
- package/package.json +13 -1
- package/references/agent-inbox/CLAUDE.md +151 -0
- package/references/agent-inbox/README.md +238 -0
- package/references/agent-inbox/docs/CLAUDE-CODE-SWARM-PROPOSAL.md +137 -0
- package/references/agent-inbox/docs/DESIGN.md +1156 -0
- package/references/agent-inbox/hooks/inbox-hook.mjs +119 -0
- package/references/agent-inbox/hooks/register-hook.mjs +69 -0
- package/references/agent-inbox/package-lock.json +3347 -0
- package/references/agent-inbox/package.json +58 -0
- package/references/agent-inbox/rules/agent-inbox.md +78 -0
- package/references/agent-inbox/src/federation/address.ts +61 -0
- package/references/agent-inbox/src/federation/connection-manager.ts +573 -0
- package/references/agent-inbox/src/federation/delivery-queue.ts +222 -0
- package/references/agent-inbox/src/federation/index.ts +6 -0
- package/references/agent-inbox/src/federation/routing-engine.ts +188 -0
- package/references/agent-inbox/src/federation/trust.ts +71 -0
- package/references/agent-inbox/src/index.ts +390 -0
- package/references/agent-inbox/src/ipc/ipc-server.ts +207 -0
- package/references/agent-inbox/src/jsonrpc/mail-server.ts +382 -0
- package/references/agent-inbox/src/map/map-client.ts +414 -0
- package/references/agent-inbox/src/mcp/mcp-server.ts +272 -0
- package/references/agent-inbox/src/mesh/delivery-bridge.ts +110 -0
- package/references/agent-inbox/src/mesh/mesh-connector.ts +41 -0
- package/references/agent-inbox/src/mesh/mesh-transport.ts +157 -0
- package/references/agent-inbox/src/mesh/type-mapper.ts +239 -0
- package/references/agent-inbox/src/push/notifier.ts +233 -0
- package/references/agent-inbox/src/registry/warm-registry.ts +255 -0
- package/references/agent-inbox/src/router/message-router.ts +175 -0
- package/references/agent-inbox/src/storage/interface.ts +48 -0
- package/references/agent-inbox/src/storage/memory.ts +145 -0
- package/references/agent-inbox/src/storage/sqlite.ts +671 -0
- package/references/agent-inbox/src/traceability/traceability.ts +183 -0
- package/references/agent-inbox/src/types.ts +303 -0
- package/references/agent-inbox/test/federation/address.test.ts +101 -0
- package/references/agent-inbox/test/federation/connection-manager.test.ts +546 -0
- package/references/agent-inbox/test/federation/delivery-queue.test.ts +159 -0
- package/references/agent-inbox/test/federation/integration.test.ts +857 -0
- package/references/agent-inbox/test/federation/routing-engine.test.ts +117 -0
- package/references/agent-inbox/test/federation/sdk-integration.test.ts +744 -0
- package/references/agent-inbox/test/federation/trust.test.ts +89 -0
- package/references/agent-inbox/test/ipc-jsonrpc.test.ts +113 -0
- package/references/agent-inbox/test/ipc-server.test.ts +197 -0
- package/references/agent-inbox/test/mail-server.test.ts +285 -0
- package/references/agent-inbox/test/map-client.test.ts +408 -0
- package/references/agent-inbox/test/mesh/delivery-bridge.test.ts +178 -0
- package/references/agent-inbox/test/mesh/e2e-mesh.test.ts +527 -0
- package/references/agent-inbox/test/mesh/e2e-real-meshpeer.test.ts +629 -0
- package/references/agent-inbox/test/mesh/federation-mesh.test.ts +269 -0
- package/references/agent-inbox/test/mesh/mesh-connector.test.ts +66 -0
- package/references/agent-inbox/test/mesh/mesh-transport.test.ts +191 -0
- package/references/agent-inbox/test/mesh/meshpeer-integration.test.ts +442 -0
- package/references/agent-inbox/test/mesh/mock-mesh.ts +125 -0
- package/references/agent-inbox/test/mesh/mock-meshpeer.ts +266 -0
- package/references/agent-inbox/test/mesh/type-mapper.test.ts +226 -0
- package/references/agent-inbox/test/message-router.test.ts +184 -0
- package/references/agent-inbox/test/push-notifier.test.ts +139 -0
- package/references/agent-inbox/test/registry/warm-registry.test.ts +171 -0
- package/references/agent-inbox/test/sqlite-prefix.test.ts +192 -0
- package/references/agent-inbox/test/sqlite-storage.test.ts +243 -0
- package/references/agent-inbox/test/storage.test.ts +196 -0
- package/references/agent-inbox/test/traceability.test.ts +123 -0
- package/references/agent-inbox/test/wake.test.ts +330 -0
- package/references/agent-inbox/tsconfig.json +20 -0
- package/references/agent-inbox/tsup.config.ts +10 -0
- package/references/agent-inbox/vitest.config.ts +8 -0
- package/references/minimem/.claude/settings.json +7 -0
- package/references/minimem/.sudocode/issues.jsonl +18 -0
- package/references/minimem/.sudocode/specs.jsonl +1 -0
- package/references/minimem/CLAUDE.md +329 -0
- package/references/minimem/README.md +565 -0
- package/references/minimem/claude-plugin/.claude-plugin/plugin.json +10 -0
- package/references/minimem/claude-plugin/.mcp.json +7 -0
- package/references/minimem/claude-plugin/README.md +158 -0
- package/references/minimem/claude-plugin/commands/recall.md +47 -0
- package/references/minimem/claude-plugin/commands/remember.md +41 -0
- package/references/minimem/claude-plugin/hooks/__tests__/hooks.test.ts +272 -0
- package/references/minimem/claude-plugin/hooks/hooks.json +27 -0
- package/references/minimem/claude-plugin/hooks/session-end.sh +86 -0
- package/references/minimem/claude-plugin/hooks/session-start.sh +85 -0
- package/references/minimem/claude-plugin/skills/memory/SKILL.md +108 -0
- package/references/minimem/media/banner.png +0 -0
- package/references/minimem/package-lock.json +5373 -0
- package/references/minimem/package.json +76 -0
- package/references/minimem/scripts/postbuild.js +49 -0
- package/references/minimem/src/__tests__/edge-cases.test.ts +371 -0
- package/references/minimem/src/__tests__/errors.test.ts +265 -0
- package/references/minimem/src/__tests__/helpers.ts +199 -0
- package/references/minimem/src/__tests__/internal.test.ts +407 -0
- package/references/minimem/src/__tests__/knowledge-frontmatter.test.ts +148 -0
- package/references/minimem/src/__tests__/knowledge.test.ts +148 -0
- package/references/minimem/src/__tests__/minimem.integration.test.ts +1127 -0
- package/references/minimem/src/__tests__/session.test.ts +190 -0
- package/references/minimem/src/cli/__tests__/commands.test.ts +760 -0
- package/references/minimem/src/cli/__tests__/contained-layout.test.ts +286 -0
- package/references/minimem/src/cli/commands/__tests__/conflicts.test.ts +141 -0
- package/references/minimem/src/cli/commands/append.ts +76 -0
- package/references/minimem/src/cli/commands/config.ts +262 -0
- package/references/minimem/src/cli/commands/conflicts.ts +415 -0
- package/references/minimem/src/cli/commands/daemon.ts +169 -0
- package/references/minimem/src/cli/commands/index.ts +12 -0
- package/references/minimem/src/cli/commands/init.ts +166 -0
- package/references/minimem/src/cli/commands/mcp.ts +221 -0
- package/references/minimem/src/cli/commands/push-pull.ts +213 -0
- package/references/minimem/src/cli/commands/search.ts +223 -0
- package/references/minimem/src/cli/commands/status.ts +84 -0
- package/references/minimem/src/cli/commands/store.ts +189 -0
- package/references/minimem/src/cli/commands/sync-init.ts +290 -0
- package/references/minimem/src/cli/commands/sync.ts +70 -0
- package/references/minimem/src/cli/commands/upsert.ts +197 -0
- package/references/minimem/src/cli/config.ts +611 -0
- package/references/minimem/src/cli/index.ts +299 -0
- package/references/minimem/src/cli/shared.ts +189 -0
- package/references/minimem/src/cli/sync/__tests__/central.test.ts +152 -0
- package/references/minimem/src/cli/sync/__tests__/conflicts.test.ts +209 -0
- package/references/minimem/src/cli/sync/__tests__/daemon.test.ts +118 -0
- package/references/minimem/src/cli/sync/__tests__/detection.test.ts +207 -0
- package/references/minimem/src/cli/sync/__tests__/integration.test.ts +476 -0
- package/references/minimem/src/cli/sync/__tests__/registry.test.ts +363 -0
- package/references/minimem/src/cli/sync/__tests__/state.test.ts +255 -0
- package/references/minimem/src/cli/sync/__tests__/validation.test.ts +193 -0
- package/references/minimem/src/cli/sync/__tests__/watcher.test.ts +178 -0
- package/references/minimem/src/cli/sync/central.ts +292 -0
- package/references/minimem/src/cli/sync/conflicts.ts +205 -0
- package/references/minimem/src/cli/sync/daemon.ts +407 -0
- package/references/minimem/src/cli/sync/detection.ts +138 -0
- package/references/minimem/src/cli/sync/index.ts +107 -0
- package/references/minimem/src/cli/sync/operations.ts +373 -0
- package/references/minimem/src/cli/sync/registry.ts +279 -0
- package/references/minimem/src/cli/sync/state.ts +358 -0
- package/references/minimem/src/cli/sync/validation.ts +206 -0
- package/references/minimem/src/cli/sync/watcher.ts +237 -0
- package/references/minimem/src/cli/version.ts +34 -0
- package/references/minimem/src/core/index.ts +9 -0
- package/references/minimem/src/core/indexer.ts +628 -0
- package/references/minimem/src/core/searcher.ts +221 -0
- package/references/minimem/src/db/schema.ts +183 -0
- package/references/minimem/src/db/sqlite-vec.ts +24 -0
- package/references/minimem/src/embeddings/__tests__/embeddings.test.ts +431 -0
- package/references/minimem/src/embeddings/batch-gemini.ts +392 -0
- package/references/minimem/src/embeddings/batch-openai.ts +409 -0
- package/references/minimem/src/embeddings/embeddings.ts +434 -0
- package/references/minimem/src/index.ts +132 -0
- package/references/minimem/src/internal.ts +299 -0
- package/references/minimem/src/minimem.ts +1291 -0
- package/references/minimem/src/search/__tests__/hybrid.test.ts +247 -0
- package/references/minimem/src/search/graph.ts +234 -0
- package/references/minimem/src/search/hybrid.ts +151 -0
- package/references/minimem/src/search/search.ts +256 -0
- package/references/minimem/src/server/__tests__/mcp.test.ts +347 -0
- package/references/minimem/src/server/__tests__/tools.test.ts +364 -0
- package/references/minimem/src/server/mcp.ts +326 -0
- package/references/minimem/src/server/tools.ts +720 -0
- package/references/minimem/src/session.ts +460 -0
- package/references/minimem/src/store/__tests__/manifest.test.ts +177 -0
- package/references/minimem/src/store/__tests__/materialize.test.ts +52 -0
- package/references/minimem/src/store/__tests__/store-graph.test.ts +228 -0
- package/references/minimem/src/store/index.ts +27 -0
- package/references/minimem/src/store/manifest.ts +203 -0
- package/references/minimem/src/store/materialize.ts +185 -0
- package/references/minimem/src/store/store-graph.ts +252 -0
- package/references/minimem/tsconfig.json +19 -0
- package/references/minimem/tsup.config.ts +26 -0
- package/references/minimem/vitest.config.ts +29 -0
- package/references/openteams/src/cli/generate.ts +23 -1
- package/references/openteams/src/generators/agent-prompt-generator.test.ts +94 -0
- package/references/openteams/src/generators/agent-prompt-generator.ts +42 -13
- package/references/openteams/src/generators/package-generator.ts +9 -1
- package/references/openteams/src/generators/skill-generator.test.ts +28 -0
- package/references/openteams/src/generators/skill-generator.ts +10 -4
- package/references/skill-tree/.claude/settings.json +6 -0
- package/references/skill-tree/.sudocode/issues.jsonl +19 -0
- package/references/skill-tree/.sudocode/specs.jsonl +3 -0
- package/references/skill-tree/CLAUDE.md +132 -0
- package/references/skill-tree/README.md +396 -0
- package/references/skill-tree/docs/GAPS_v1.md +221 -0
- package/references/skill-tree/docs/INTEGRATION_PLAN.md +467 -0
- package/references/skill-tree/docs/TODOS.md +91 -0
- package/references/skill-tree/docs/anthropic_skill_guide.md +1364 -0
- package/references/skill-tree/docs/design/federated-skill-trees.md +524 -0
- package/references/skill-tree/docs/design/multi-agent-sync.md +759 -0
- package/references/skill-tree/docs/scraper/BRAINSTORM.md +583 -0
- package/references/skill-tree/docs/scraper/POC_PLAN.md +420 -0
- package/references/skill-tree/docs/scraper/README.md +170 -0
- package/references/skill-tree/examples/basic-usage.ts +157 -0
- package/references/skill-tree/package-lock.json +1852 -0
- package/references/skill-tree/package.json +66 -0
- package/references/skill-tree/plan.md +78 -0
- package/references/skill-tree/scraper/README.md +123 -0
- package/references/skill-tree/scraper/docs/DESIGN.md +683 -0
- package/references/skill-tree/scraper/docs/PLAN.md +336 -0
- package/references/skill-tree/scraper/drizzle.config.ts +10 -0
- package/references/skill-tree/scraper/package-lock.json +6329 -0
- package/references/skill-tree/scraper/package.json +68 -0
- package/references/skill-tree/scraper/test/fixtures/invalid-skill/missing-description.md +7 -0
- package/references/skill-tree/scraper/test/fixtures/invalid-skill/missing-name.md +7 -0
- package/references/skill-tree/scraper/test/fixtures/minimal-skill/SKILL.md +27 -0
- package/references/skill-tree/scraper/test/fixtures/skill-json/SKILL.json +21 -0
- package/references/skill-tree/scraper/test/fixtures/skill-with-meta/SKILL.md +54 -0
- package/references/skill-tree/scraper/test/fixtures/skill-with-meta/_meta.json +24 -0
- package/references/skill-tree/scraper/test/fixtures/valid-skill/SKILL.md +93 -0
- package/references/skill-tree/scraper/test/fixtures/valid-skill/_meta.json +22 -0
- package/references/skill-tree/scraper/tsup.config.ts +14 -0
- package/references/skill-tree/scraper/vitest.config.ts +17 -0
- package/references/skill-tree/scripts/convert-to-vitest.ts +166 -0
- package/references/skill-tree/skills/skill-writer/SKILL.md +339 -0
- package/references/skill-tree/skills/skill-writer/references/examples.md +326 -0
- package/references/skill-tree/skills/skill-writer/references/patterns.md +210 -0
- package/references/skill-tree/skills/skill-writer/references/quality-checklist.md +123 -0
- package/references/skill-tree/test/run-all.ts +106 -0
- package/references/skill-tree/test/utils.ts +128 -0
- package/references/skill-tree/vitest.config.ts +16 -0
- package/references/swarmkit/src/commands/init/phases/configure.ts +0 -22
- package/references/swarmkit/src/commands/init/phases/global-setup.ts +5 -3
- package/references/swarmkit/src/commands/init/wizard.ts +2 -2
- package/references/swarmkit/src/packages/setup.test.ts +53 -7
- package/references/swarmkit/src/packages/setup.ts +37 -1
- package/scripts/bootstrap.mjs +26 -1
- package/scripts/generate-agents.mjs +5 -1
- package/scripts/map-hook.mjs +97 -64
- package/scripts/map-sidecar.mjs +179 -25
- package/scripts/team-loader.mjs +12 -41
- package/skills/swarm/SKILL.md +89 -25
- package/src/__tests__/agent-generator.test.mjs +6 -13
- package/src/__tests__/bootstrap.test.mjs +124 -1
- package/src/__tests__/config.test.mjs +200 -27
- package/src/__tests__/e2e-live-map.test.mjs +536 -0
- package/src/__tests__/e2e-mesh-sidecar.test.mjs +570 -0
- package/src/__tests__/e2e-native-task-hooks.test.mjs +376 -0
- package/src/__tests__/e2e-sidecar-bridge.test.mjs +477 -0
- package/src/__tests__/helpers.mjs +13 -0
- package/src/__tests__/inbox.test.mjs +22 -89
- package/src/__tests__/index.test.mjs +35 -9
- package/src/__tests__/integration.test.mjs +513 -0
- package/src/__tests__/map-events.test.mjs +514 -150
- package/src/__tests__/mesh-connection.test.mjs +308 -0
- package/src/__tests__/opentasks-client.test.mjs +517 -0
- package/src/__tests__/paths.test.mjs +185 -41
- package/src/__tests__/sidecar-client.test.mjs +35 -0
- package/src/__tests__/sidecar-server.test.mjs +124 -0
- package/src/__tests__/skilltree-client.test.mjs +80 -0
- package/src/agent-generator.mjs +104 -33
- package/src/bootstrap.mjs +150 -10
- package/src/config.mjs +81 -17
- package/src/context-output.mjs +58 -8
- package/src/inbox.mjs +9 -54
- package/src/index.mjs +39 -8
- package/src/map-connection.mjs +4 -3
- package/src/map-events.mjs +350 -80
- package/src/mesh-connection.mjs +148 -0
- package/src/opentasks-client.mjs +269 -0
- package/src/paths.mjs +182 -27
- package/src/sessionlog.mjs +14 -9
- package/src/sidecar-client.mjs +81 -27
- package/src/sidecar-server.mjs +175 -16
- package/src/skilltree-client.mjs +173 -0
- package/src/template.mjs +68 -4
- package/vitest.config.mjs +1 -0
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* End-to-end tests: Two real agent-inbox instances communicating
|
|
3
|
+
* through agentic-mesh federation.
|
|
4
|
+
*
|
|
5
|
+
* Each test sets up two complete agent-inbox stacks (storage, router,
|
|
6
|
+
* traceability, notifier, mapClient, federation) connected via linked
|
|
7
|
+
* MockMeshPeers. Messages sent from agents on one system are delivered
|
|
8
|
+
* to agents on the other system through the full pipeline:
|
|
9
|
+
*
|
|
10
|
+
* Agent on System-A → router.routeMessage()
|
|
11
|
+
* → ConnectionManager.route() → FederationGateway.route()
|
|
12
|
+
* → [linked mock mesh] →
|
|
13
|
+
* → System-B MapServer.deliverLocally()
|
|
14
|
+
* → DeliveryBridge.deliverToAgent()
|
|
15
|
+
* → storage.putMessage() + events.emit("message.created")
|
|
16
|
+
* → traceability + push notifier
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
20
|
+
import { EventEmitter } from "node:events";
|
|
21
|
+
import { InMemoryStorage } from "../../src/storage/memory.js";
|
|
22
|
+
import { MessageRouter } from "../../src/router/message-router.js";
|
|
23
|
+
import { TraceabilityLayer } from "../../src/traceability/traceability.js";
|
|
24
|
+
import { MapClient } from "../../src/map/map-client.js";
|
|
25
|
+
import type { MeshPeerLike } from "../../src/map/map-client.js";
|
|
26
|
+
import { ConnectionManager } from "../../src/federation/connection-manager.js";
|
|
27
|
+
import { DeliveryBridge } from "../../src/mesh/delivery-bridge.js";
|
|
28
|
+
import type { Message } from "../../src/types.js";
|
|
29
|
+
import { createLinkedMeshPeerPair, LinkedMockMeshPeer } from "./mock-meshpeer.js";
|
|
30
|
+
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// Helper: wire up a full agent-inbox stack on a MeshPeer
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
interface InboxStack {
|
|
36
|
+
peerId: string;
|
|
37
|
+
systemId: string;
|
|
38
|
+
storage: InMemoryStorage;
|
|
39
|
+
events: EventEmitter;
|
|
40
|
+
router: MessageRouter;
|
|
41
|
+
traceability: TraceabilityLayer;
|
|
42
|
+
mapClient: MapClient;
|
|
43
|
+
federation: ConnectionManager;
|
|
44
|
+
meshPeer: LinkedMockMeshPeer;
|
|
45
|
+
stop(): Promise<void>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function createInboxOnMesh(
|
|
49
|
+
meshPeer: LinkedMockMeshPeer,
|
|
50
|
+
scope = "default"
|
|
51
|
+
): Promise<InboxStack> {
|
|
52
|
+
const storage = new InMemoryStorage();
|
|
53
|
+
const events = new EventEmitter();
|
|
54
|
+
const router = new MessageRouter(storage, events, scope);
|
|
55
|
+
const traceability = new TraceabilityLayer(storage, events);
|
|
56
|
+
|
|
57
|
+
const mapClient = new MapClient(storage, router, events);
|
|
58
|
+
const meshSystemId = await mapClient.connectViaMesh(meshPeer);
|
|
59
|
+
|
|
60
|
+
// Install DeliveryBridge on MapServer
|
|
61
|
+
const bridge = new DeliveryBridge(storage, events, scope);
|
|
62
|
+
const server = meshPeer.server;
|
|
63
|
+
server.setDeliveryHandler(bridge);
|
|
64
|
+
|
|
65
|
+
// Set up federation
|
|
66
|
+
const federation = new ConnectionManager(events, {
|
|
67
|
+
systemId: meshSystemId,
|
|
68
|
+
trust: { allowedServers: [], scopePermissions: {}, requireAuth: false },
|
|
69
|
+
}, {
|
|
70
|
+
meshPeer: meshPeer as unknown as MeshPeerLike,
|
|
71
|
+
});
|
|
72
|
+
router.setFederation(federation);
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
peerId: meshPeer.peerId,
|
|
76
|
+
systemId: meshSystemId,
|
|
77
|
+
storage,
|
|
78
|
+
events,
|
|
79
|
+
router,
|
|
80
|
+
traceability,
|
|
81
|
+
mapClient,
|
|
82
|
+
federation,
|
|
83
|
+
meshPeer,
|
|
84
|
+
async stop() {
|
|
85
|
+
await federation.destroy();
|
|
86
|
+
await mapClient.disconnect();
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
// Tests
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
|
|
95
|
+
describe("E2E: Two agent-inboxes over agentic-mesh", () => {
|
|
96
|
+
let meshPeerA: LinkedMockMeshPeer;
|
|
97
|
+
let meshPeerB: LinkedMockMeshPeer;
|
|
98
|
+
let inboxA: InboxStack;
|
|
99
|
+
let inboxB: InboxStack;
|
|
100
|
+
|
|
101
|
+
beforeEach(async () => {
|
|
102
|
+
[meshPeerA, meshPeerB] = createLinkedMeshPeerPair(
|
|
103
|
+
{ peerId: "peer-a", systemId: "system-a" },
|
|
104
|
+
{ peerId: "peer-b", systemId: "system-b" }
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
inboxA = await createInboxOnMesh(meshPeerA);
|
|
108
|
+
inboxB = await createInboxOnMesh(meshPeerB);
|
|
109
|
+
|
|
110
|
+
// Register local agents
|
|
111
|
+
inboxA.storage.putAgent({
|
|
112
|
+
agent_id: "alice",
|
|
113
|
+
scope: "default",
|
|
114
|
+
status: "active",
|
|
115
|
+
metadata: {},
|
|
116
|
+
registered_at: new Date().toISOString(),
|
|
117
|
+
last_active_at: new Date().toISOString(),
|
|
118
|
+
});
|
|
119
|
+
inboxB.storage.putAgent({
|
|
120
|
+
agent_id: "bob",
|
|
121
|
+
scope: "default",
|
|
122
|
+
status: "active",
|
|
123
|
+
metadata: {},
|
|
124
|
+
registered_at: new Date().toISOString(),
|
|
125
|
+
last_active_at: new Date().toISOString(),
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// Federate: A ↔ B
|
|
129
|
+
await inboxA.federation.federate({
|
|
130
|
+
systemId: "system-b",
|
|
131
|
+
meshPeerId: "peer-b",
|
|
132
|
+
});
|
|
133
|
+
await inboxB.federation.federate({
|
|
134
|
+
systemId: "system-a",
|
|
135
|
+
meshPeerId: "peer-a",
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
afterEach(async () => {
|
|
140
|
+
await inboxA.stop();
|
|
141
|
+
await inboxB.stop();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// -----------------------------------------------------------------------
|
|
145
|
+
// Basic message delivery
|
|
146
|
+
// -----------------------------------------------------------------------
|
|
147
|
+
|
|
148
|
+
it("should deliver a message from alice@system-a to bob@system-b", async () => {
|
|
149
|
+
await inboxA.router.routeMessage({
|
|
150
|
+
from: "alice",
|
|
151
|
+
to: "bob@system-b",
|
|
152
|
+
payload: "Hello Bob, this is Alice!",
|
|
153
|
+
importance: "normal",
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Wait for async delivery through the gateway
|
|
157
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
158
|
+
|
|
159
|
+
// Verify bob received the message on system-b
|
|
160
|
+
const bobInbox = inboxB.storage.getInbox("bob");
|
|
161
|
+
expect(bobInbox).toHaveLength(1);
|
|
162
|
+
expect(bobInbox[0].content).toEqual({ type: "text", text: "Hello Bob, this is Alice!" });
|
|
163
|
+
expect(bobInbox[0].sender_id).toBe("alice");
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it("should deliver a message from bob@system-b to alice@system-a", async () => {
|
|
167
|
+
await inboxB.router.routeMessage({
|
|
168
|
+
from: "bob",
|
|
169
|
+
to: "alice@system-a",
|
|
170
|
+
payload: "Hey Alice!",
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
174
|
+
|
|
175
|
+
const aliceInbox = inboxA.storage.getInbox("alice");
|
|
176
|
+
expect(aliceInbox).toHaveLength(1);
|
|
177
|
+
expect(aliceInbox[0].content).toEqual({ type: "text", text: "Hey Alice!" });
|
|
178
|
+
expect(aliceInbox[0].sender_id).toBe("bob");
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// -----------------------------------------------------------------------
|
|
182
|
+
// Reply chain
|
|
183
|
+
// -----------------------------------------------------------------------
|
|
184
|
+
|
|
185
|
+
it("should support a full reply chain: A→B→A", async () => {
|
|
186
|
+
// Step 1: Alice sends to Bob
|
|
187
|
+
const original = await inboxA.router.routeMessage({
|
|
188
|
+
from: "alice",
|
|
189
|
+
to: "bob@system-b",
|
|
190
|
+
payload: "Can you review PR #42?",
|
|
191
|
+
subject: "PR Review",
|
|
192
|
+
threadTag: "pr-42",
|
|
193
|
+
importance: "high",
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
197
|
+
|
|
198
|
+
// Verify Bob received it
|
|
199
|
+
const bobInbox = inboxB.storage.getInbox("bob");
|
|
200
|
+
expect(bobInbox).toHaveLength(1);
|
|
201
|
+
expect(bobInbox[0].subject).toBe("PR Review");
|
|
202
|
+
expect(bobInbox[0].importance).toBe("high");
|
|
203
|
+
|
|
204
|
+
// Step 2: Bob replies to Alice
|
|
205
|
+
const bobMessage = bobInbox[0];
|
|
206
|
+
await inboxB.router.routeMessage({
|
|
207
|
+
from: "bob",
|
|
208
|
+
to: "alice@system-a",
|
|
209
|
+
payload: "LGTM, approved!",
|
|
210
|
+
subject: "PR Review",
|
|
211
|
+
threadTag: "pr-42",
|
|
212
|
+
inReplyTo: bobMessage.id,
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
216
|
+
|
|
217
|
+
// Verify Alice received the reply
|
|
218
|
+
const aliceInbox = inboxA.storage.getInbox("alice");
|
|
219
|
+
expect(aliceInbox).toHaveLength(1);
|
|
220
|
+
expect(aliceInbox[0].content).toEqual({ type: "text", text: "LGTM, approved!" });
|
|
221
|
+
expect(aliceInbox[0].subject).toBe("PR Review");
|
|
222
|
+
expect(aliceInbox[0].thread_tag).toBe("pr-42");
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
// -----------------------------------------------------------------------
|
|
226
|
+
// Subject / thread_tag / importance preservation
|
|
227
|
+
// -----------------------------------------------------------------------
|
|
228
|
+
|
|
229
|
+
it("should preserve subject, thread_tag, and importance through federation", async () => {
|
|
230
|
+
await inboxA.router.routeMessage({
|
|
231
|
+
from: "alice",
|
|
232
|
+
to: "bob@system-b",
|
|
233
|
+
payload: { type: "data", schema: "review", data: { pr: 42, status: "open" } },
|
|
234
|
+
subject: "Urgent: production hotfix",
|
|
235
|
+
threadTag: "hotfix-2024",
|
|
236
|
+
importance: "urgent",
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
240
|
+
|
|
241
|
+
const bobInbox = inboxB.storage.getInbox("bob");
|
|
242
|
+
expect(bobInbox).toHaveLength(1);
|
|
243
|
+
const msg = bobInbox[0];
|
|
244
|
+
expect(msg.subject).toBe("Urgent: production hotfix");
|
|
245
|
+
expect(msg.thread_tag).toBe("hotfix-2024");
|
|
246
|
+
expect(msg.importance).toBe("urgent");
|
|
247
|
+
expect(msg.content).toEqual({
|
|
248
|
+
type: "data",
|
|
249
|
+
schema: "review",
|
|
250
|
+
data: { pr: 42, status: "open" },
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// -----------------------------------------------------------------------
|
|
255
|
+
// Multiple agents, multiple messages
|
|
256
|
+
// -----------------------------------------------------------------------
|
|
257
|
+
|
|
258
|
+
it("should handle multiple agents sending messages in both directions", async () => {
|
|
259
|
+
// Register a second agent on system-a
|
|
260
|
+
inboxA.storage.putAgent({
|
|
261
|
+
agent_id: "carol",
|
|
262
|
+
scope: "default",
|
|
263
|
+
status: "active",
|
|
264
|
+
metadata: {},
|
|
265
|
+
registered_at: new Date().toISOString(),
|
|
266
|
+
last_active_at: new Date().toISOString(),
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// Alice → Bob
|
|
270
|
+
await inboxA.router.routeMessage({
|
|
271
|
+
from: "alice",
|
|
272
|
+
to: "bob@system-b",
|
|
273
|
+
payload: "Message 1 from alice",
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
// Carol → Bob
|
|
277
|
+
await inboxA.router.routeMessage({
|
|
278
|
+
from: "carol",
|
|
279
|
+
to: "bob@system-b",
|
|
280
|
+
payload: "Message 2 from carol",
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// Bob → Alice
|
|
284
|
+
await inboxB.router.routeMessage({
|
|
285
|
+
from: "bob",
|
|
286
|
+
to: "alice@system-a",
|
|
287
|
+
payload: "Reply from bob",
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
291
|
+
|
|
292
|
+
// Bob should have 2 messages
|
|
293
|
+
const bobInbox = inboxB.storage.getInbox("bob");
|
|
294
|
+
expect(bobInbox).toHaveLength(2);
|
|
295
|
+
const senders = bobInbox.map((m) => m.sender_id).sort();
|
|
296
|
+
expect(senders).toEqual(["alice", "carol"]);
|
|
297
|
+
|
|
298
|
+
// Alice should have 1 message
|
|
299
|
+
const aliceInbox = inboxA.storage.getInbox("alice");
|
|
300
|
+
expect(aliceInbox).toHaveLength(1);
|
|
301
|
+
expect(aliceInbox[0].sender_id).toBe("bob");
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
// -----------------------------------------------------------------------
|
|
305
|
+
// Traceability: conversations and turns
|
|
306
|
+
// -----------------------------------------------------------------------
|
|
307
|
+
|
|
308
|
+
it("should trigger traceability on both sides", async () => {
|
|
309
|
+
const traceSpy = vi.fn();
|
|
310
|
+
inboxB.events.on("message.created", traceSpy);
|
|
311
|
+
|
|
312
|
+
await inboxA.router.routeMessage({
|
|
313
|
+
from: "alice",
|
|
314
|
+
to: "bob@system-b",
|
|
315
|
+
payload: "traceability test",
|
|
316
|
+
threadTag: "trace-thread",
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
320
|
+
|
|
321
|
+
// message.created should have been emitted on system-b
|
|
322
|
+
expect(traceSpy).toHaveBeenCalledTimes(1);
|
|
323
|
+
const delivered = traceSpy.mock.calls[0][0] as Message;
|
|
324
|
+
expect(delivered.sender_id).toBe("alice");
|
|
325
|
+
|
|
326
|
+
// Traceability layer on system-b should have created a conversation
|
|
327
|
+
const conversations = inboxB.storage.listConversations("default");
|
|
328
|
+
expect(conversations.length).toBeGreaterThan(0);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
// -----------------------------------------------------------------------
|
|
332
|
+
// Event content types
|
|
333
|
+
// -----------------------------------------------------------------------
|
|
334
|
+
|
|
335
|
+
it("should deliver event-type content across federation", async () => {
|
|
336
|
+
await inboxA.router.routeMessage({
|
|
337
|
+
from: "alice",
|
|
338
|
+
to: "bob@system-b",
|
|
339
|
+
payload: { type: "event", event: "deployment.started", data: { version: "1.2.3" } },
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
343
|
+
|
|
344
|
+
const bobInbox = inboxB.storage.getInbox("bob");
|
|
345
|
+
expect(bobInbox).toHaveLength(1);
|
|
346
|
+
expect(bobInbox[0].content).toEqual({
|
|
347
|
+
type: "event",
|
|
348
|
+
event: "deployment.started",
|
|
349
|
+
data: { version: "1.2.3" },
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
it("should deliver reference-type content across federation", async () => {
|
|
354
|
+
await inboxA.router.routeMessage({
|
|
355
|
+
from: "alice",
|
|
356
|
+
to: "bob@system-b",
|
|
357
|
+
payload: { type: "reference", uri: "https://github.com/org/repo/pull/42", label: "PR #42" },
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
361
|
+
|
|
362
|
+
const bobInbox = inboxB.storage.getInbox("bob");
|
|
363
|
+
expect(bobInbox).toHaveLength(1);
|
|
364
|
+
expect(bobInbox[0].content).toEqual({
|
|
365
|
+
type: "reference",
|
|
366
|
+
uri: "https://github.com/org/repo/pull/42",
|
|
367
|
+
label: "PR #42",
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
// -----------------------------------------------------------------------
|
|
372
|
+
// Local messages should not go through federation
|
|
373
|
+
// -----------------------------------------------------------------------
|
|
374
|
+
|
|
375
|
+
it("should deliver local messages without federation", async () => {
|
|
376
|
+
const routeSpy = vi.fn();
|
|
377
|
+
inboxA.events.on("federation.route", routeSpy);
|
|
378
|
+
|
|
379
|
+
// Alice sends to Carol (both on system-a)
|
|
380
|
+
inboxA.storage.putAgent({
|
|
381
|
+
agent_id: "carol",
|
|
382
|
+
scope: "default",
|
|
383
|
+
status: "active",
|
|
384
|
+
metadata: {},
|
|
385
|
+
registered_at: new Date().toISOString(),
|
|
386
|
+
last_active_at: new Date().toISOString(),
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
await inboxA.router.routeMessage({
|
|
390
|
+
from: "alice",
|
|
391
|
+
to: "carol",
|
|
392
|
+
payload: "local message",
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
const carolInbox = inboxA.storage.getInbox("carol");
|
|
396
|
+
expect(carolInbox).toHaveLength(1);
|
|
397
|
+
expect(carolInbox[0].sender_id).toBe("alice");
|
|
398
|
+
|
|
399
|
+
// Federation should not have been invoked
|
|
400
|
+
expect(routeSpy).not.toHaveBeenCalled();
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
// -----------------------------------------------------------------------
|
|
404
|
+
// Message with metadata passthrough
|
|
405
|
+
// -----------------------------------------------------------------------
|
|
406
|
+
|
|
407
|
+
it("should preserve custom metadata through federation", async () => {
|
|
408
|
+
await inboxA.router.routeMessage({
|
|
409
|
+
from: "alice",
|
|
410
|
+
to: "bob@system-b",
|
|
411
|
+
payload: "metadata test",
|
|
412
|
+
metadata: {
|
|
413
|
+
customTag: "important",
|
|
414
|
+
priority_override: true,
|
|
415
|
+
nested: { key: "value" },
|
|
416
|
+
},
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
420
|
+
|
|
421
|
+
const bobInbox = inboxB.storage.getInbox("bob");
|
|
422
|
+
expect(bobInbox).toHaveLength(1);
|
|
423
|
+
// Custom metadata is encoded in _meta and carried through
|
|
424
|
+
const meta = bobInbox[0].metadata;
|
|
425
|
+
expect(meta.customTag).toBe("important");
|
|
426
|
+
expect(meta.priority_override).toBe(true);
|
|
427
|
+
expect(meta.nested).toEqual({ key: "value" });
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
// -----------------------------------------------------------------------
|
|
431
|
+
// Rapid-fire: many messages in sequence
|
|
432
|
+
// -----------------------------------------------------------------------
|
|
433
|
+
|
|
434
|
+
it("should handle rapid sequential messages", async () => {
|
|
435
|
+
const count = 20;
|
|
436
|
+
for (let i = 0; i < count; i++) {
|
|
437
|
+
await inboxA.router.routeMessage({
|
|
438
|
+
from: "alice",
|
|
439
|
+
to: "bob@system-b",
|
|
440
|
+
payload: `message ${i}`,
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
445
|
+
|
|
446
|
+
const bobInbox = inboxB.storage.getInbox("bob");
|
|
447
|
+
expect(bobInbox).toHaveLength(count);
|
|
448
|
+
|
|
449
|
+
// Verify all messages arrived (order may vary)
|
|
450
|
+
const texts = bobInbox.map((m) =>
|
|
451
|
+
m.content.type === "text" ? m.content.text : ""
|
|
452
|
+
).sort();
|
|
453
|
+
for (let i = 0; i < count; i++) {
|
|
454
|
+
expect(texts).toContain(`message ${i}`);
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
// -----------------------------------------------------------------------
|
|
459
|
+
// Bidirectional concurrent messaging
|
|
460
|
+
// -----------------------------------------------------------------------
|
|
461
|
+
|
|
462
|
+
it("should handle concurrent bidirectional messaging", async () => {
|
|
463
|
+
// Both sides send simultaneously
|
|
464
|
+
const sends = [
|
|
465
|
+
inboxA.router.routeMessage({ from: "alice", to: "bob@system-b", payload: "from A" }),
|
|
466
|
+
inboxB.router.routeMessage({ from: "bob", to: "alice@system-a", payload: "from B" }),
|
|
467
|
+
];
|
|
468
|
+
await Promise.all(sends);
|
|
469
|
+
|
|
470
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
471
|
+
|
|
472
|
+
const bobInbox = inboxB.storage.getInbox("bob");
|
|
473
|
+
expect(bobInbox).toHaveLength(1);
|
|
474
|
+
expect(bobInbox[0].content).toEqual({ type: "text", text: "from A" });
|
|
475
|
+
|
|
476
|
+
const aliceInbox = inboxA.storage.getInbox("alice");
|
|
477
|
+
expect(aliceInbox).toHaveLength(1);
|
|
478
|
+
expect(aliceInbox[0].content).toEqual({ type: "text", text: "from B" });
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
// -----------------------------------------------------------------------
|
|
482
|
+
// String payload normalization
|
|
483
|
+
// -----------------------------------------------------------------------
|
|
484
|
+
|
|
485
|
+
it("should normalize string payloads on both sides", async () => {
|
|
486
|
+
await inboxA.router.routeMessage({
|
|
487
|
+
from: "alice",
|
|
488
|
+
to: "bob@system-b",
|
|
489
|
+
payload: "plain string",
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
493
|
+
|
|
494
|
+
const bobInbox = inboxB.storage.getInbox("bob");
|
|
495
|
+
expect(bobInbox[0].content).toEqual({ type: "text", text: "plain string" });
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
// -----------------------------------------------------------------------
|
|
499
|
+
// Importance levels
|
|
500
|
+
// -----------------------------------------------------------------------
|
|
501
|
+
|
|
502
|
+
it("should preserve all importance levels across federation", async () => {
|
|
503
|
+
const levels = ["low", "normal", "high", "urgent"] as const;
|
|
504
|
+
|
|
505
|
+
for (const level of levels) {
|
|
506
|
+
await inboxA.router.routeMessage({
|
|
507
|
+
from: "alice",
|
|
508
|
+
to: "bob@system-b",
|
|
509
|
+
payload: `${level} message`,
|
|
510
|
+
importance: level,
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
515
|
+
|
|
516
|
+
const bobInbox = inboxB.storage.getInbox("bob");
|
|
517
|
+
expect(bobInbox).toHaveLength(4);
|
|
518
|
+
|
|
519
|
+
for (const level of levels) {
|
|
520
|
+
const msg = bobInbox.find(
|
|
521
|
+
(m) => m.content.type === "text" && m.content.text === `${level} message`
|
|
522
|
+
);
|
|
523
|
+
expect(msg).toBeDefined();
|
|
524
|
+
expect(msg!.importance).toBe(level);
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
});
|