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.
Files changed (273) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +22 -1
  3. package/.claude-plugin/run-agent-inbox-mcp.sh +76 -0
  4. package/.claude-plugin/run-minimem-mcp.sh +98 -0
  5. package/.claude-plugin/run-opentasks-mcp.sh +65 -0
  6. package/CLAUDE.md +200 -36
  7. package/README.md +65 -0
  8. package/e2e/helpers/cleanup.mjs +17 -3
  9. package/e2e/helpers/map-mock-server.mjs +201 -25
  10. package/e2e/helpers/sidecar.mjs +222 -0
  11. package/e2e/helpers/workspace.mjs +2 -1
  12. package/e2e/tier5-sidecar-inbox.test.mjs +900 -0
  13. package/e2e/tier6-inbox-mcp.test.mjs +173 -0
  14. package/e2e/tier6-live-agent.test.mjs +759 -0
  15. package/e2e/vitest.config.e2e.mjs +1 -1
  16. package/hooks/hooks.json +15 -8
  17. package/package.json +13 -1
  18. package/references/agent-inbox/CLAUDE.md +151 -0
  19. package/references/agent-inbox/README.md +238 -0
  20. package/references/agent-inbox/docs/CLAUDE-CODE-SWARM-PROPOSAL.md +137 -0
  21. package/references/agent-inbox/docs/DESIGN.md +1156 -0
  22. package/references/agent-inbox/hooks/inbox-hook.mjs +119 -0
  23. package/references/agent-inbox/hooks/register-hook.mjs +69 -0
  24. package/references/agent-inbox/package-lock.json +3347 -0
  25. package/references/agent-inbox/package.json +58 -0
  26. package/references/agent-inbox/rules/agent-inbox.md +78 -0
  27. package/references/agent-inbox/src/federation/address.ts +61 -0
  28. package/references/agent-inbox/src/federation/connection-manager.ts +573 -0
  29. package/references/agent-inbox/src/federation/delivery-queue.ts +222 -0
  30. package/references/agent-inbox/src/federation/index.ts +6 -0
  31. package/references/agent-inbox/src/federation/routing-engine.ts +188 -0
  32. package/references/agent-inbox/src/federation/trust.ts +71 -0
  33. package/references/agent-inbox/src/index.ts +390 -0
  34. package/references/agent-inbox/src/ipc/ipc-server.ts +207 -0
  35. package/references/agent-inbox/src/jsonrpc/mail-server.ts +382 -0
  36. package/references/agent-inbox/src/map/map-client.ts +414 -0
  37. package/references/agent-inbox/src/mcp/mcp-server.ts +272 -0
  38. package/references/agent-inbox/src/mesh/delivery-bridge.ts +110 -0
  39. package/references/agent-inbox/src/mesh/mesh-connector.ts +41 -0
  40. package/references/agent-inbox/src/mesh/mesh-transport.ts +157 -0
  41. package/references/agent-inbox/src/mesh/type-mapper.ts +239 -0
  42. package/references/agent-inbox/src/push/notifier.ts +233 -0
  43. package/references/agent-inbox/src/registry/warm-registry.ts +255 -0
  44. package/references/agent-inbox/src/router/message-router.ts +175 -0
  45. package/references/agent-inbox/src/storage/interface.ts +48 -0
  46. package/references/agent-inbox/src/storage/memory.ts +145 -0
  47. package/references/agent-inbox/src/storage/sqlite.ts +671 -0
  48. package/references/agent-inbox/src/traceability/traceability.ts +183 -0
  49. package/references/agent-inbox/src/types.ts +303 -0
  50. package/references/agent-inbox/test/federation/address.test.ts +101 -0
  51. package/references/agent-inbox/test/federation/connection-manager.test.ts +546 -0
  52. package/references/agent-inbox/test/federation/delivery-queue.test.ts +159 -0
  53. package/references/agent-inbox/test/federation/integration.test.ts +857 -0
  54. package/references/agent-inbox/test/federation/routing-engine.test.ts +117 -0
  55. package/references/agent-inbox/test/federation/sdk-integration.test.ts +744 -0
  56. package/references/agent-inbox/test/federation/trust.test.ts +89 -0
  57. package/references/agent-inbox/test/ipc-jsonrpc.test.ts +113 -0
  58. package/references/agent-inbox/test/ipc-server.test.ts +197 -0
  59. package/references/agent-inbox/test/mail-server.test.ts +285 -0
  60. package/references/agent-inbox/test/map-client.test.ts +408 -0
  61. package/references/agent-inbox/test/mesh/delivery-bridge.test.ts +178 -0
  62. package/references/agent-inbox/test/mesh/e2e-mesh.test.ts +527 -0
  63. package/references/agent-inbox/test/mesh/e2e-real-meshpeer.test.ts +629 -0
  64. package/references/agent-inbox/test/mesh/federation-mesh.test.ts +269 -0
  65. package/references/agent-inbox/test/mesh/mesh-connector.test.ts +66 -0
  66. package/references/agent-inbox/test/mesh/mesh-transport.test.ts +191 -0
  67. package/references/agent-inbox/test/mesh/meshpeer-integration.test.ts +442 -0
  68. package/references/agent-inbox/test/mesh/mock-mesh.ts +125 -0
  69. package/references/agent-inbox/test/mesh/mock-meshpeer.ts +266 -0
  70. package/references/agent-inbox/test/mesh/type-mapper.test.ts +226 -0
  71. package/references/agent-inbox/test/message-router.test.ts +184 -0
  72. package/references/agent-inbox/test/push-notifier.test.ts +139 -0
  73. package/references/agent-inbox/test/registry/warm-registry.test.ts +171 -0
  74. package/references/agent-inbox/test/sqlite-prefix.test.ts +192 -0
  75. package/references/agent-inbox/test/sqlite-storage.test.ts +243 -0
  76. package/references/agent-inbox/test/storage.test.ts +196 -0
  77. package/references/agent-inbox/test/traceability.test.ts +123 -0
  78. package/references/agent-inbox/test/wake.test.ts +330 -0
  79. package/references/agent-inbox/tsconfig.json +20 -0
  80. package/references/agent-inbox/tsup.config.ts +10 -0
  81. package/references/agent-inbox/vitest.config.ts +8 -0
  82. package/references/minimem/.claude/settings.json +7 -0
  83. package/references/minimem/.sudocode/issues.jsonl +18 -0
  84. package/references/minimem/.sudocode/specs.jsonl +1 -0
  85. package/references/minimem/CLAUDE.md +329 -0
  86. package/references/minimem/README.md +565 -0
  87. package/references/minimem/claude-plugin/.claude-plugin/plugin.json +10 -0
  88. package/references/minimem/claude-plugin/.mcp.json +7 -0
  89. package/references/minimem/claude-plugin/README.md +158 -0
  90. package/references/minimem/claude-plugin/commands/recall.md +47 -0
  91. package/references/minimem/claude-plugin/commands/remember.md +41 -0
  92. package/references/minimem/claude-plugin/hooks/__tests__/hooks.test.ts +272 -0
  93. package/references/minimem/claude-plugin/hooks/hooks.json +27 -0
  94. package/references/minimem/claude-plugin/hooks/session-end.sh +86 -0
  95. package/references/minimem/claude-plugin/hooks/session-start.sh +85 -0
  96. package/references/minimem/claude-plugin/skills/memory/SKILL.md +108 -0
  97. package/references/minimem/media/banner.png +0 -0
  98. package/references/minimem/package-lock.json +5373 -0
  99. package/references/minimem/package.json +76 -0
  100. package/references/minimem/scripts/postbuild.js +49 -0
  101. package/references/minimem/src/__tests__/edge-cases.test.ts +371 -0
  102. package/references/minimem/src/__tests__/errors.test.ts +265 -0
  103. package/references/minimem/src/__tests__/helpers.ts +199 -0
  104. package/references/minimem/src/__tests__/internal.test.ts +407 -0
  105. package/references/minimem/src/__tests__/knowledge-frontmatter.test.ts +148 -0
  106. package/references/minimem/src/__tests__/knowledge.test.ts +148 -0
  107. package/references/minimem/src/__tests__/minimem.integration.test.ts +1127 -0
  108. package/references/minimem/src/__tests__/session.test.ts +190 -0
  109. package/references/minimem/src/cli/__tests__/commands.test.ts +760 -0
  110. package/references/minimem/src/cli/__tests__/contained-layout.test.ts +286 -0
  111. package/references/minimem/src/cli/commands/__tests__/conflicts.test.ts +141 -0
  112. package/references/minimem/src/cli/commands/append.ts +76 -0
  113. package/references/minimem/src/cli/commands/config.ts +262 -0
  114. package/references/minimem/src/cli/commands/conflicts.ts +415 -0
  115. package/references/minimem/src/cli/commands/daemon.ts +169 -0
  116. package/references/minimem/src/cli/commands/index.ts +12 -0
  117. package/references/minimem/src/cli/commands/init.ts +166 -0
  118. package/references/minimem/src/cli/commands/mcp.ts +221 -0
  119. package/references/minimem/src/cli/commands/push-pull.ts +213 -0
  120. package/references/minimem/src/cli/commands/search.ts +223 -0
  121. package/references/minimem/src/cli/commands/status.ts +84 -0
  122. package/references/minimem/src/cli/commands/store.ts +189 -0
  123. package/references/minimem/src/cli/commands/sync-init.ts +290 -0
  124. package/references/minimem/src/cli/commands/sync.ts +70 -0
  125. package/references/minimem/src/cli/commands/upsert.ts +197 -0
  126. package/references/minimem/src/cli/config.ts +611 -0
  127. package/references/minimem/src/cli/index.ts +299 -0
  128. package/references/minimem/src/cli/shared.ts +189 -0
  129. package/references/minimem/src/cli/sync/__tests__/central.test.ts +152 -0
  130. package/references/minimem/src/cli/sync/__tests__/conflicts.test.ts +209 -0
  131. package/references/minimem/src/cli/sync/__tests__/daemon.test.ts +118 -0
  132. package/references/minimem/src/cli/sync/__tests__/detection.test.ts +207 -0
  133. package/references/minimem/src/cli/sync/__tests__/integration.test.ts +476 -0
  134. package/references/minimem/src/cli/sync/__tests__/registry.test.ts +363 -0
  135. package/references/minimem/src/cli/sync/__tests__/state.test.ts +255 -0
  136. package/references/minimem/src/cli/sync/__tests__/validation.test.ts +193 -0
  137. package/references/minimem/src/cli/sync/__tests__/watcher.test.ts +178 -0
  138. package/references/minimem/src/cli/sync/central.ts +292 -0
  139. package/references/minimem/src/cli/sync/conflicts.ts +205 -0
  140. package/references/minimem/src/cli/sync/daemon.ts +407 -0
  141. package/references/minimem/src/cli/sync/detection.ts +138 -0
  142. package/references/minimem/src/cli/sync/index.ts +107 -0
  143. package/references/minimem/src/cli/sync/operations.ts +373 -0
  144. package/references/minimem/src/cli/sync/registry.ts +279 -0
  145. package/references/minimem/src/cli/sync/state.ts +358 -0
  146. package/references/minimem/src/cli/sync/validation.ts +206 -0
  147. package/references/minimem/src/cli/sync/watcher.ts +237 -0
  148. package/references/minimem/src/cli/version.ts +34 -0
  149. package/references/minimem/src/core/index.ts +9 -0
  150. package/references/minimem/src/core/indexer.ts +628 -0
  151. package/references/minimem/src/core/searcher.ts +221 -0
  152. package/references/minimem/src/db/schema.ts +183 -0
  153. package/references/minimem/src/db/sqlite-vec.ts +24 -0
  154. package/references/minimem/src/embeddings/__tests__/embeddings.test.ts +431 -0
  155. package/references/minimem/src/embeddings/batch-gemini.ts +392 -0
  156. package/references/minimem/src/embeddings/batch-openai.ts +409 -0
  157. package/references/minimem/src/embeddings/embeddings.ts +434 -0
  158. package/references/minimem/src/index.ts +132 -0
  159. package/references/minimem/src/internal.ts +299 -0
  160. package/references/minimem/src/minimem.ts +1291 -0
  161. package/references/minimem/src/search/__tests__/hybrid.test.ts +247 -0
  162. package/references/minimem/src/search/graph.ts +234 -0
  163. package/references/minimem/src/search/hybrid.ts +151 -0
  164. package/references/minimem/src/search/search.ts +256 -0
  165. package/references/minimem/src/server/__tests__/mcp.test.ts +347 -0
  166. package/references/minimem/src/server/__tests__/tools.test.ts +364 -0
  167. package/references/minimem/src/server/mcp.ts +326 -0
  168. package/references/minimem/src/server/tools.ts +720 -0
  169. package/references/minimem/src/session.ts +460 -0
  170. package/references/minimem/src/store/__tests__/manifest.test.ts +177 -0
  171. package/references/minimem/src/store/__tests__/materialize.test.ts +52 -0
  172. package/references/minimem/src/store/__tests__/store-graph.test.ts +228 -0
  173. package/references/minimem/src/store/index.ts +27 -0
  174. package/references/minimem/src/store/manifest.ts +203 -0
  175. package/references/minimem/src/store/materialize.ts +185 -0
  176. package/references/minimem/src/store/store-graph.ts +252 -0
  177. package/references/minimem/tsconfig.json +19 -0
  178. package/references/minimem/tsup.config.ts +26 -0
  179. package/references/minimem/vitest.config.ts +29 -0
  180. package/references/openteams/src/cli/generate.ts +23 -1
  181. package/references/openteams/src/generators/agent-prompt-generator.test.ts +94 -0
  182. package/references/openteams/src/generators/agent-prompt-generator.ts +42 -13
  183. package/references/openteams/src/generators/package-generator.ts +9 -1
  184. package/references/openteams/src/generators/skill-generator.test.ts +28 -0
  185. package/references/openteams/src/generators/skill-generator.ts +10 -4
  186. package/references/skill-tree/.claude/settings.json +6 -0
  187. package/references/skill-tree/.sudocode/issues.jsonl +19 -0
  188. package/references/skill-tree/.sudocode/specs.jsonl +3 -0
  189. package/references/skill-tree/CLAUDE.md +132 -0
  190. package/references/skill-tree/README.md +396 -0
  191. package/references/skill-tree/docs/GAPS_v1.md +221 -0
  192. package/references/skill-tree/docs/INTEGRATION_PLAN.md +467 -0
  193. package/references/skill-tree/docs/TODOS.md +91 -0
  194. package/references/skill-tree/docs/anthropic_skill_guide.md +1364 -0
  195. package/references/skill-tree/docs/design/federated-skill-trees.md +524 -0
  196. package/references/skill-tree/docs/design/multi-agent-sync.md +759 -0
  197. package/references/skill-tree/docs/scraper/BRAINSTORM.md +583 -0
  198. package/references/skill-tree/docs/scraper/POC_PLAN.md +420 -0
  199. package/references/skill-tree/docs/scraper/README.md +170 -0
  200. package/references/skill-tree/examples/basic-usage.ts +157 -0
  201. package/references/skill-tree/package-lock.json +1852 -0
  202. package/references/skill-tree/package.json +66 -0
  203. package/references/skill-tree/plan.md +78 -0
  204. package/references/skill-tree/scraper/README.md +123 -0
  205. package/references/skill-tree/scraper/docs/DESIGN.md +683 -0
  206. package/references/skill-tree/scraper/docs/PLAN.md +336 -0
  207. package/references/skill-tree/scraper/drizzle.config.ts +10 -0
  208. package/references/skill-tree/scraper/package-lock.json +6329 -0
  209. package/references/skill-tree/scraper/package.json +68 -0
  210. package/references/skill-tree/scraper/test/fixtures/invalid-skill/missing-description.md +7 -0
  211. package/references/skill-tree/scraper/test/fixtures/invalid-skill/missing-name.md +7 -0
  212. package/references/skill-tree/scraper/test/fixtures/minimal-skill/SKILL.md +27 -0
  213. package/references/skill-tree/scraper/test/fixtures/skill-json/SKILL.json +21 -0
  214. package/references/skill-tree/scraper/test/fixtures/skill-with-meta/SKILL.md +54 -0
  215. package/references/skill-tree/scraper/test/fixtures/skill-with-meta/_meta.json +24 -0
  216. package/references/skill-tree/scraper/test/fixtures/valid-skill/SKILL.md +93 -0
  217. package/references/skill-tree/scraper/test/fixtures/valid-skill/_meta.json +22 -0
  218. package/references/skill-tree/scraper/tsup.config.ts +14 -0
  219. package/references/skill-tree/scraper/vitest.config.ts +17 -0
  220. package/references/skill-tree/scripts/convert-to-vitest.ts +166 -0
  221. package/references/skill-tree/skills/skill-writer/SKILL.md +339 -0
  222. package/references/skill-tree/skills/skill-writer/references/examples.md +326 -0
  223. package/references/skill-tree/skills/skill-writer/references/patterns.md +210 -0
  224. package/references/skill-tree/skills/skill-writer/references/quality-checklist.md +123 -0
  225. package/references/skill-tree/test/run-all.ts +106 -0
  226. package/references/skill-tree/test/utils.ts +128 -0
  227. package/references/skill-tree/vitest.config.ts +16 -0
  228. package/references/swarmkit/src/commands/init/phases/configure.ts +0 -22
  229. package/references/swarmkit/src/commands/init/phases/global-setup.ts +5 -3
  230. package/references/swarmkit/src/commands/init/wizard.ts +2 -2
  231. package/references/swarmkit/src/packages/setup.test.ts +53 -7
  232. package/references/swarmkit/src/packages/setup.ts +37 -1
  233. package/scripts/bootstrap.mjs +26 -1
  234. package/scripts/generate-agents.mjs +5 -1
  235. package/scripts/map-hook.mjs +97 -64
  236. package/scripts/map-sidecar.mjs +179 -25
  237. package/scripts/team-loader.mjs +12 -41
  238. package/skills/swarm/SKILL.md +89 -25
  239. package/src/__tests__/agent-generator.test.mjs +6 -13
  240. package/src/__tests__/bootstrap.test.mjs +124 -1
  241. package/src/__tests__/config.test.mjs +200 -27
  242. package/src/__tests__/e2e-live-map.test.mjs +536 -0
  243. package/src/__tests__/e2e-mesh-sidecar.test.mjs +570 -0
  244. package/src/__tests__/e2e-native-task-hooks.test.mjs +376 -0
  245. package/src/__tests__/e2e-sidecar-bridge.test.mjs +477 -0
  246. package/src/__tests__/helpers.mjs +13 -0
  247. package/src/__tests__/inbox.test.mjs +22 -89
  248. package/src/__tests__/index.test.mjs +35 -9
  249. package/src/__tests__/integration.test.mjs +513 -0
  250. package/src/__tests__/map-events.test.mjs +514 -150
  251. package/src/__tests__/mesh-connection.test.mjs +308 -0
  252. package/src/__tests__/opentasks-client.test.mjs +517 -0
  253. package/src/__tests__/paths.test.mjs +185 -41
  254. package/src/__tests__/sidecar-client.test.mjs +35 -0
  255. package/src/__tests__/sidecar-server.test.mjs +124 -0
  256. package/src/__tests__/skilltree-client.test.mjs +80 -0
  257. package/src/agent-generator.mjs +104 -33
  258. package/src/bootstrap.mjs +150 -10
  259. package/src/config.mjs +81 -17
  260. package/src/context-output.mjs +58 -8
  261. package/src/inbox.mjs +9 -54
  262. package/src/index.mjs +39 -8
  263. package/src/map-connection.mjs +4 -3
  264. package/src/map-events.mjs +350 -80
  265. package/src/mesh-connection.mjs +148 -0
  266. package/src/opentasks-client.mjs +269 -0
  267. package/src/paths.mjs +182 -27
  268. package/src/sessionlog.mjs +14 -9
  269. package/src/sidecar-client.mjs +81 -27
  270. package/src/sidecar-server.mjs +175 -16
  271. package/src/skilltree-client.mjs +173 -0
  272. package/src/template.mjs +68 -4
  273. 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
+ });