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,629 @@
1
+ /**
2
+ * End-to-end tests: Two real agent-inbox instances communicating
3
+ * through real agentic-mesh MeshPeer federation.
4
+ *
5
+ * Unlike the mock-based e2e tests, these use real agentic-mesh components:
6
+ * - MeshPeer.createEmbedded() for in-process MAP servers
7
+ * - Real FederationGateway with JSON-RPC handshake + envelope routing
8
+ * - Real MapServer agent registration + DeliveryHandler dispatch
9
+ * - InMemoryMapStream pairs simulate the transport layer
10
+ *
11
+ * The only mock is the transport: instead of real networking (Nebula/Tailscale),
12
+ * we use linked in-memory MapStream pairs so frames written on one side appear
13
+ * instantly on the other. Everything above the transport is real code.
14
+ *
15
+ * Message flow:
16
+ * Agent on System-A → router.routeMessage()
17
+ * → ConnectionManager.route() → inboxMessageToMap()
18
+ * → real FederationGateway.route() → federation envelope → MapStream
19
+ * → [in-memory linked stream] →
20
+ * → real FederationGateway.processIncomingEnvelope()
21
+ * → real MapServer.MessageRouter.send()
22
+ * → DeliveryBridge.deliverToAgent()
23
+ * → mapMessageToInbox() → storage.putMessage()
24
+ * → events.emit("message.created") → traceability
25
+ *
26
+ * Note: The real FederationGateway prefixes sender IDs with the source
27
+ * system, e.g. "alice" becomes "system-a:alice" when delivered to system-b.
28
+ * This is standard MAP federation behavior.
29
+ */
30
+
31
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
32
+ import { EventEmitter } from "node:events";
33
+ import { MeshPeer, type MapStream, type MapFrame } from "agentic-mesh";
34
+ import { InMemoryStorage } from "../../src/storage/memory.js";
35
+ import { MessageRouter } from "../../src/router/message-router.js";
36
+ import { TraceabilityLayer } from "../../src/traceability/traceability.js";
37
+ import { MapClient } from "../../src/map/map-client.js";
38
+ import type { MeshPeerLike } from "../../src/map/map-client.js";
39
+ import { ConnectionManager } from "../../src/federation/connection-manager.js";
40
+ import { DeliveryBridge } from "../../src/mesh/delivery-bridge.js";
41
+
42
+ // ---------------------------------------------------------------------------
43
+ // InMemoryMapStream: linked pair for in-process federation
44
+ // ---------------------------------------------------------------------------
45
+
46
+ class InMemoryMapStream extends EventEmitter implements MapStream {
47
+ readonly id: string;
48
+ private _isOpen = false;
49
+ private _state: "connecting" | "connected" | "disconnecting" | "disconnected" | "error" =
50
+ "disconnected";
51
+ private _frameQueue: MapFrame[] = [];
52
+ private _waitingReaders: Array<(frame: MapFrame | null) => void> = [];
53
+ private _peer: InMemoryMapStream | null = null;
54
+
55
+ constructor(id: string) {
56
+ super();
57
+ this.id = id;
58
+ }
59
+
60
+ get isOpen(): boolean {
61
+ return this._isOpen;
62
+ }
63
+ get state(): "connecting" | "connected" | "disconnecting" | "disconnected" | "error" {
64
+ return this._state;
65
+ }
66
+
67
+ link(peer: InMemoryMapStream): void {
68
+ this._peer = peer;
69
+ }
70
+
71
+ open(): void {
72
+ this._isOpen = true;
73
+ this._state = "connected";
74
+ }
75
+
76
+ async write(frame: MapFrame): Promise<void> {
77
+ if (!this._isOpen) throw new Error("Stream closed");
78
+ if (this._peer) {
79
+ this._peer._enqueue(frame);
80
+ }
81
+ }
82
+
83
+ _enqueue(frame: MapFrame): void {
84
+ if (this._waitingReaders.length > 0) {
85
+ const resolve = this._waitingReaders.shift()!;
86
+ resolve(frame);
87
+ } else {
88
+ this._frameQueue.push(frame);
89
+ }
90
+ }
91
+
92
+ async close(): Promise<void> {
93
+ this._isOpen = false;
94
+ this._state = "disconnected";
95
+ for (const resolve of this._waitingReaders) {
96
+ resolve(null);
97
+ }
98
+ this._waitingReaders = [];
99
+ this.emit("close");
100
+ }
101
+
102
+ [Symbol.asyncIterator](): AsyncIterator<MapFrame> {
103
+ const self = this;
104
+ return {
105
+ async next(): Promise<IteratorResult<MapFrame>> {
106
+ if (!self._isOpen && self._frameQueue.length === 0) {
107
+ return { done: true, value: undefined };
108
+ }
109
+ if (self._frameQueue.length > 0) {
110
+ return { done: false, value: self._frameQueue.shift()! };
111
+ }
112
+ const frame = await new Promise<MapFrame | null>((resolve) => {
113
+ self._waitingReaders.push(resolve);
114
+ });
115
+ if (frame === null) return { done: true, value: undefined };
116
+ return { done: false, value: frame };
117
+ },
118
+ };
119
+ }
120
+ }
121
+
122
+ function createLinkedMapStreamPair(): [InMemoryMapStream, InMemoryMapStream] {
123
+ const a = new InMemoryMapStream("stream-a");
124
+ const b = new InMemoryMapStream("stream-b");
125
+ a.link(b);
126
+ b.link(a);
127
+ a.open();
128
+ b.open();
129
+ return [a, b];
130
+ }
131
+
132
+ // ---------------------------------------------------------------------------
133
+ // Helper: wire up a full agent-inbox stack on a real MeshPeer
134
+ // ---------------------------------------------------------------------------
135
+
136
+ interface InboxStack {
137
+ peerId: string;
138
+ systemId: string;
139
+ storage: InMemoryStorage;
140
+ events: EventEmitter;
141
+ router: MessageRouter;
142
+ traceability: TraceabilityLayer;
143
+ mapClient: MapClient;
144
+ federation: ConnectionManager;
145
+ meshPeer: MeshPeer;
146
+ stop(): Promise<void>;
147
+ }
148
+
149
+ async function createInboxOnMeshPeer(
150
+ meshPeer: MeshPeer,
151
+ scope = "default"
152
+ ): Promise<InboxStack> {
153
+ const storage = new InMemoryStorage();
154
+ const events = new EventEmitter();
155
+ const router = new MessageRouter(storage, events, scope);
156
+ const traceability = new TraceabilityLayer(storage, events);
157
+
158
+ const mapClient = new MapClient(storage, router, events);
159
+ const meshSystemId = await mapClient.connectViaMesh(
160
+ meshPeer as unknown as MeshPeerLike
161
+ );
162
+
163
+ // Install DeliveryBridge on the real MapServer
164
+ const bridge = new DeliveryBridge(storage, events, scope);
165
+ meshPeer.server.setDeliveryHandler(bridge);
166
+
167
+ // Set up federation (ConnectionManager will use meshPeer.federateWith())
168
+ const federation = new ConnectionManager(
169
+ events,
170
+ {
171
+ systemId: meshSystemId,
172
+ trust: { allowedServers: [], scopePermissions: {}, requireAuth: false },
173
+ },
174
+ { meshPeer: meshPeer as unknown as MeshPeerLike }
175
+ );
176
+ router.setFederation(federation);
177
+
178
+ return {
179
+ peerId: meshPeer.peerId,
180
+ systemId: meshSystemId,
181
+ storage,
182
+ events,
183
+ router,
184
+ traceability,
185
+ mapClient,
186
+ federation,
187
+ meshPeer,
188
+ async stop() {
189
+ await federation.destroy();
190
+ await mapClient.disconnect();
191
+ },
192
+ };
193
+ }
194
+
195
+ // ---------------------------------------------------------------------------
196
+ // Tests
197
+ // ---------------------------------------------------------------------------
198
+
199
+ describe("E2E: Two agent-inboxes over real agentic-mesh MeshPeers", () => {
200
+ let peerA: MeshPeer;
201
+ let peerB: MeshPeer;
202
+ let streams: InMemoryMapStream[];
203
+ let inboxA: InboxStack;
204
+ let inboxB: InboxStack;
205
+
206
+ beforeEach(async () => {
207
+ // Create two real embedded MeshPeers (in-process, no networking)
208
+ peerA = MeshPeer.createEmbedded({ peerId: "system-a" });
209
+ peerB = MeshPeer.createEmbedded({ peerId: "system-b" });
210
+ await peerA.start();
211
+ await peerB.start();
212
+
213
+ // Create linked MapStream pairs and connect FederationGateways.
214
+ // This must happen BEFORE wiring agent-inbox, so that when
215
+ // ConnectionManager.federate() calls federateWith(), the gateway
216
+ // already exists and is connected.
217
+ const [streamAtoB, streamBtoA] = createLinkedMapStreamPair();
218
+ streams = [streamAtoB, streamBtoA];
219
+ const gwA = await peerA.federateWith("system-b");
220
+ const gwB = await peerB.federateWith("system-a");
221
+
222
+ // Connect concurrently — each gateway sends a JSON-RPC handshake
223
+ // that the other must respond to
224
+ await Promise.all([gwA.connect(streamAtoB), gwB.connect(streamBtoA)]);
225
+
226
+ // Wire up full agent-inbox stacks on each peer
227
+ inboxA = await createInboxOnMeshPeer(peerA);
228
+ inboxB = await createInboxOnMeshPeer(peerB);
229
+
230
+ // Register local agents on each system.
231
+ // Agents must be registered BOTH on the real MapServer (so the real
232
+ // MessageRouter can resolve addresses) AND in inbox storage (so
233
+ // agent-inbox knows they're local).
234
+ await peerA.createAgent({ agentId: "alice", name: "Alice" });
235
+ inboxA.storage.putAgent({
236
+ agent_id: "alice",
237
+ scope: "default",
238
+ status: "active",
239
+ metadata: {},
240
+ registered_at: new Date().toISOString(),
241
+ last_active_at: new Date().toISOString(),
242
+ });
243
+ await peerB.createAgent({ agentId: "bob", name: "Bob" });
244
+ inboxB.storage.putAgent({
245
+ agent_id: "bob",
246
+ scope: "default",
247
+ status: "active",
248
+ metadata: {},
249
+ registered_at: new Date().toISOString(),
250
+ last_active_at: new Date().toISOString(),
251
+ });
252
+
253
+ // Federate — ConnectionManager picks up the already-connected gateways
254
+ await inboxA.federation.federate({
255
+ systemId: "system-b",
256
+ meshPeerId: "system-b",
257
+ });
258
+ await inboxB.federation.federate({
259
+ systemId: "system-a",
260
+ meshPeerId: "system-a",
261
+ });
262
+ }, 15000);
263
+
264
+ afterEach(async () => {
265
+ await inboxA.stop();
266
+ await inboxB.stop();
267
+ // Close streams BEFORE stopping peers to unblock async iterators
268
+ for (const stream of streams) {
269
+ await stream.close();
270
+ }
271
+ await peerA.stop();
272
+ await peerB.stop();
273
+ }, 15000);
274
+
275
+ // -----------------------------------------------------------------------
276
+ // Basic message delivery
277
+ // -----------------------------------------------------------------------
278
+
279
+ it("should deliver a message from alice@system-a to bob@system-b", async () => {
280
+ await inboxA.router.routeMessage({
281
+ from: "alice",
282
+ to: "bob@system-b",
283
+ payload: "Hello Bob, this is Alice!",
284
+ importance: "normal",
285
+ });
286
+
287
+ await new Promise((r) => setTimeout(r, 500));
288
+
289
+ const bobInbox = inboxB.storage.getInbox("bob");
290
+ expect(bobInbox).toHaveLength(1);
291
+ expect(bobInbox[0].content).toEqual({
292
+ type: "text",
293
+ text: "Hello Bob, this is Alice!",
294
+ });
295
+ // Real FederationGateway prefixes sender with source system
296
+ expect(bobInbox[0].sender_id).toContain("alice");
297
+ });
298
+
299
+ it("should deliver a message from bob@system-b to alice@system-a", async () => {
300
+ await inboxB.router.routeMessage({
301
+ from: "bob",
302
+ to: "alice@system-a",
303
+ payload: "Hey Alice!",
304
+ });
305
+
306
+ await new Promise((r) => setTimeout(r, 200));
307
+
308
+ const aliceInbox = inboxA.storage.getInbox("alice");
309
+ expect(aliceInbox).toHaveLength(1);
310
+ expect(aliceInbox[0].content).toEqual({ type: "text", text: "Hey Alice!" });
311
+ expect(aliceInbox[0].sender_id).toContain("bob");
312
+ });
313
+
314
+ // -----------------------------------------------------------------------
315
+ // Reply chain
316
+ // -----------------------------------------------------------------------
317
+
318
+ it("should support a full reply chain: A→B→A", async () => {
319
+ // Alice sends to Bob
320
+ await inboxA.router.routeMessage({
321
+ from: "alice",
322
+ to: "bob@system-b",
323
+ payload: "Can you review PR #42?",
324
+ subject: "PR Review",
325
+ threadTag: "pr-42",
326
+ importance: "high",
327
+ });
328
+
329
+ await new Promise((r) => setTimeout(r, 200));
330
+
331
+ const bobInbox = inboxB.storage.getInbox("bob");
332
+ expect(bobInbox).toHaveLength(1);
333
+ expect(bobInbox[0].subject).toBe("PR Review");
334
+ expect(bobInbox[0].importance).toBe("high");
335
+
336
+ // Bob replies to Alice
337
+ await inboxB.router.routeMessage({
338
+ from: "bob",
339
+ to: "alice@system-a",
340
+ payload: "LGTM, approved!",
341
+ subject: "PR Review",
342
+ threadTag: "pr-42",
343
+ });
344
+
345
+ await new Promise((r) => setTimeout(r, 200));
346
+
347
+ const aliceInbox = inboxA.storage.getInbox("alice");
348
+ expect(aliceInbox).toHaveLength(1);
349
+ expect(aliceInbox[0].content).toEqual({
350
+ type: "text",
351
+ text: "LGTM, approved!",
352
+ });
353
+ expect(aliceInbox[0].subject).toBe("PR Review");
354
+ expect(aliceInbox[0].thread_tag).toBe("pr-42");
355
+ });
356
+
357
+ // -----------------------------------------------------------------------
358
+ // Subject / thread_tag / importance preservation
359
+ // -----------------------------------------------------------------------
360
+
361
+ it("should preserve subject, thread_tag, and importance through real federation", async () => {
362
+ await inboxA.router.routeMessage({
363
+ from: "alice",
364
+ to: "bob@system-b",
365
+ payload: {
366
+ type: "data",
367
+ schema: "review",
368
+ data: { pr: 42, status: "open" },
369
+ },
370
+ subject: "Urgent: production hotfix",
371
+ threadTag: "hotfix-2024",
372
+ importance: "urgent",
373
+ });
374
+
375
+ await new Promise((r) => setTimeout(r, 200));
376
+
377
+ const bobInbox = inboxB.storage.getInbox("bob");
378
+ expect(bobInbox).toHaveLength(1);
379
+ const msg = bobInbox[0];
380
+ expect(msg.subject).toBe("Urgent: production hotfix");
381
+ expect(msg.thread_tag).toBe("hotfix-2024");
382
+ expect(msg.importance).toBe("urgent");
383
+ expect(msg.content).toEqual({
384
+ type: "data",
385
+ schema: "review",
386
+ data: { pr: 42, status: "open" },
387
+ });
388
+ });
389
+
390
+ // -----------------------------------------------------------------------
391
+ // Multiple agents
392
+ // -----------------------------------------------------------------------
393
+
394
+ it("should handle multiple agents sending messages in both directions", async () => {
395
+ inboxA.storage.putAgent({
396
+ agent_id: "carol",
397
+ scope: "default",
398
+ status: "active",
399
+ metadata: {},
400
+ registered_at: new Date().toISOString(),
401
+ last_active_at: new Date().toISOString(),
402
+ });
403
+
404
+ await inboxA.router.routeMessage({
405
+ from: "alice",
406
+ to: "bob@system-b",
407
+ payload: "Message 1 from alice",
408
+ });
409
+ await inboxA.router.routeMessage({
410
+ from: "carol",
411
+ to: "bob@system-b",
412
+ payload: "Message 2 from carol",
413
+ });
414
+ await inboxB.router.routeMessage({
415
+ from: "bob",
416
+ to: "alice@system-a",
417
+ payload: "Reply from bob",
418
+ });
419
+
420
+ await new Promise((r) => setTimeout(r, 300));
421
+
422
+ const bobInbox = inboxB.storage.getInbox("bob");
423
+ expect(bobInbox).toHaveLength(2);
424
+
425
+ const aliceInbox = inboxA.storage.getInbox("alice");
426
+ expect(aliceInbox).toHaveLength(1);
427
+ expect(aliceInbox[0].sender_id).toContain("bob");
428
+ });
429
+
430
+ // -----------------------------------------------------------------------
431
+ // Traceability
432
+ // -----------------------------------------------------------------------
433
+
434
+ it("should trigger traceability on both sides via real federation", async () => {
435
+ const traceSpy = vi.fn();
436
+ inboxB.events.on("message.created", traceSpy);
437
+
438
+ await inboxA.router.routeMessage({
439
+ from: "alice",
440
+ to: "bob@system-b",
441
+ payload: "traceability test",
442
+ threadTag: "trace-thread",
443
+ });
444
+
445
+ await new Promise((r) => setTimeout(r, 200));
446
+
447
+ expect(traceSpy).toHaveBeenCalledTimes(1);
448
+ const delivered = traceSpy.mock.calls[0][0] as Message;
449
+ expect(delivered.sender_id).toContain("alice");
450
+
451
+ const conversations = inboxB.storage.listConversations("default");
452
+ expect(conversations.length).toBeGreaterThan(0);
453
+ });
454
+
455
+ // -----------------------------------------------------------------------
456
+ // Content types
457
+ // -----------------------------------------------------------------------
458
+
459
+ it("should deliver structured content types through real federation", async () => {
460
+ await inboxA.router.routeMessage({
461
+ from: "alice",
462
+ to: "bob@system-b",
463
+ payload: {
464
+ type: "event",
465
+ event: "deployment.started",
466
+ data: { version: "1.2.3" },
467
+ },
468
+ });
469
+
470
+ await new Promise((r) => setTimeout(r, 200));
471
+
472
+ const bobInbox = inboxB.storage.getInbox("bob");
473
+ expect(bobInbox).toHaveLength(1);
474
+ expect(bobInbox[0].content).toEqual({
475
+ type: "event",
476
+ event: "deployment.started",
477
+ data: { version: "1.2.3" },
478
+ });
479
+ });
480
+
481
+ // -----------------------------------------------------------------------
482
+ // Concurrent bidirectional messaging
483
+ // -----------------------------------------------------------------------
484
+
485
+ it("should handle concurrent bidirectional messaging", async () => {
486
+ await Promise.all([
487
+ inboxA.router.routeMessage({
488
+ from: "alice",
489
+ to: "bob@system-b",
490
+ payload: "from A",
491
+ }),
492
+ inboxB.router.routeMessage({
493
+ from: "bob",
494
+ to: "alice@system-a",
495
+ payload: "from B",
496
+ }),
497
+ ]);
498
+
499
+ await new Promise((r) => setTimeout(r, 300));
500
+
501
+ const bobInbox = inboxB.storage.getInbox("bob");
502
+ expect(bobInbox).toHaveLength(1);
503
+ expect(bobInbox[0].content).toEqual({ type: "text", text: "from A" });
504
+
505
+ const aliceInbox = inboxA.storage.getInbox("alice");
506
+ expect(aliceInbox).toHaveLength(1);
507
+ expect(aliceInbox[0].content).toEqual({ type: "text", text: "from B" });
508
+ });
509
+
510
+ // -----------------------------------------------------------------------
511
+ // Importance levels
512
+ // -----------------------------------------------------------------------
513
+
514
+ it("should preserve all importance levels through real federation", async () => {
515
+ const levels = ["low", "normal", "high", "urgent"] as const;
516
+
517
+ for (const level of levels) {
518
+ await inboxA.router.routeMessage({
519
+ from: "alice",
520
+ to: "bob@system-b",
521
+ payload: `${level} message`,
522
+ importance: level,
523
+ });
524
+ }
525
+
526
+ await new Promise((r) => setTimeout(r, 400));
527
+
528
+ const bobInbox = inboxB.storage.getInbox("bob");
529
+ expect(bobInbox).toHaveLength(4);
530
+
531
+ for (const level of levels) {
532
+ const msg = bobInbox.find(
533
+ (m) => m.content.type === "text" && m.content.text === `${level} message`
534
+ );
535
+ expect(msg).toBeDefined();
536
+ expect(msg!.importance).toBe(level);
537
+ }
538
+ });
539
+
540
+ // -----------------------------------------------------------------------
541
+ // Custom metadata
542
+ // -----------------------------------------------------------------------
543
+
544
+ it("should preserve custom metadata through real federation", async () => {
545
+ await inboxA.router.routeMessage({
546
+ from: "alice",
547
+ to: "bob@system-b",
548
+ payload: "metadata test",
549
+ metadata: {
550
+ customTag: "important",
551
+ priority_override: true,
552
+ nested: { key: "value" },
553
+ },
554
+ });
555
+
556
+ await new Promise((r) => setTimeout(r, 200));
557
+
558
+ const bobInbox = inboxB.storage.getInbox("bob");
559
+ expect(bobInbox).toHaveLength(1);
560
+ const meta = bobInbox[0].metadata;
561
+ expect(meta.customTag).toBe("important");
562
+ expect(meta.priority_override).toBe(true);
563
+ expect(meta.nested).toEqual({ key: "value" });
564
+ });
565
+
566
+ // -----------------------------------------------------------------------
567
+ // Rapid-fire messages
568
+ // -----------------------------------------------------------------------
569
+
570
+ it("should handle rapid sequential messages through real federation", async () => {
571
+ const count = 10;
572
+ for (let i = 0; i < count; i++) {
573
+ await inboxA.router.routeMessage({
574
+ from: "alice",
575
+ to: "bob@system-b",
576
+ payload: `message ${i}`,
577
+ });
578
+ }
579
+
580
+ await new Promise((r) => setTimeout(r, 500));
581
+
582
+ const bobInbox = inboxB.storage.getInbox("bob");
583
+ expect(bobInbox).toHaveLength(count);
584
+
585
+ const texts = bobInbox
586
+ .map((m) => (m.content.type === "text" ? m.content.text : ""))
587
+ .sort();
588
+ for (let i = 0; i < count; i++) {
589
+ expect(texts).toContain(`message ${i}`);
590
+ }
591
+ });
592
+
593
+ // -----------------------------------------------------------------------
594
+ // Gateway state
595
+ // -----------------------------------------------------------------------
596
+
597
+ it("should report gateways as connected on both sides", () => {
598
+ expect(inboxA.federation.hasGateway("system-b")).toBe(true);
599
+ expect(inboxB.federation.hasGateway("system-a")).toBe(true);
600
+ });
601
+
602
+ // -----------------------------------------------------------------------
603
+ // Local messages bypass federation
604
+ // -----------------------------------------------------------------------
605
+
606
+ it("should deliver local messages without federation", async () => {
607
+ inboxA.storage.putAgent({
608
+ agent_id: "carol",
609
+ scope: "default",
610
+ status: "active",
611
+ metadata: {},
612
+ registered_at: new Date().toISOString(),
613
+ last_active_at: new Date().toISOString(),
614
+ });
615
+
616
+ await inboxA.router.routeMessage({
617
+ from: "alice",
618
+ to: "carol",
619
+ payload: "local message",
620
+ });
621
+
622
+ const carolInbox = inboxA.storage.getInbox("carol");
623
+ expect(carolInbox).toHaveLength(1);
624
+ expect(carolInbox[0].sender_id).toBe("alice");
625
+
626
+ const bobInbox = inboxB.storage.getInbox("bob");
627
+ expect(bobInbox).toHaveLength(0);
628
+ });
629
+ });