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,222 @@
1
+ import { EventEmitter } from "node:events";
2
+ import { ulid } from "ulid";
3
+ import type { Message, DeliveryQueueConfig, QueuedMessage } from "../types.js";
4
+
5
+ const DEFAULT_CONFIG: DeliveryQueueConfig = {
6
+ persistence: "memory",
7
+ maxTTL: 86_400_000, // 24h
8
+ maxQueueSize: 10_000,
9
+ retryStrategy: "exponential",
10
+ retryBaseInterval: 1_000,
11
+ retryMaxAttempts: 0, // unlimited until TTL
12
+ flushOnReconnect: true,
13
+ overflow: "drop-oldest",
14
+ };
15
+
16
+ /**
17
+ * Delivery queue for messages to offline or unreachable federation peers.
18
+ *
19
+ * Supports configurable TTL, overflow policy, and retry strategy.
20
+ * Currently memory-only; SQLite persistence is a future enhancement.
21
+ */
22
+ export class DeliveryQueue {
23
+ private queues = new Map<string, QueuedMessage[]>();
24
+ private config: DeliveryQueueConfig;
25
+ private tickTimer?: ReturnType<typeof setInterval>;
26
+
27
+ constructor(
28
+ private events: EventEmitter,
29
+ config?: Partial<DeliveryQueueConfig>
30
+ ) {
31
+ this.config = { ...DEFAULT_CONFIG, ...config };
32
+ }
33
+
34
+ /**
35
+ * Enqueue a message for a peer. Returns the queued message ID, or null if rejected.
36
+ */
37
+ enqueue(peerId: string, message: Message): string | null {
38
+ let queue = this.queues.get(peerId);
39
+ if (!queue) {
40
+ queue = [];
41
+ this.queues.set(peerId, queue);
42
+ }
43
+
44
+ // Check queue size limit
45
+ if (queue.length >= this.config.maxQueueSize) {
46
+ switch (this.config.overflow) {
47
+ case "drop-oldest":
48
+ queue.shift();
49
+ break;
50
+ case "drop-newest":
51
+ return null; // Don't enqueue
52
+ case "reject-new":
53
+ return null;
54
+ }
55
+ }
56
+
57
+ const now = new Date().toISOString();
58
+ const entry: QueuedMessage = {
59
+ id: ulid(),
60
+ peerId,
61
+ message,
62
+ enqueuedAt: now,
63
+ attempts: 0,
64
+ nextRetry: now, // Ready to send immediately
65
+ };
66
+
67
+ queue.push(entry);
68
+ this.events.emit("queue.enqueued", { peerId, messageId: entry.id });
69
+ return entry.id;
70
+ }
71
+
72
+ /**
73
+ * Flush all queued messages for a peer (on reconnect).
74
+ * Returns the messages and removes them from the queue.
75
+ */
76
+ flush(peerId: string): QueuedMessage[] {
77
+ const queue = this.queues.get(peerId);
78
+ if (!queue || queue.length === 0) return [];
79
+
80
+ const messages = [...queue];
81
+ this.queues.delete(peerId);
82
+ this.events.emit("queue.flushed", { peerId, count: messages.length });
83
+ return messages;
84
+ }
85
+
86
+ /**
87
+ * Get messages ready for retry. Does not remove them from the queue.
88
+ */
89
+ getRetryable(peerId: string): QueuedMessage[] {
90
+ const queue = this.queues.get(peerId);
91
+ if (!queue) return [];
92
+
93
+ const now = Date.now();
94
+ return queue.filter((entry) => {
95
+ if (!entry.nextRetry) return true;
96
+ return new Date(entry.nextRetry).getTime() <= now;
97
+ });
98
+ }
99
+
100
+ /**
101
+ * Record a retry attempt for a message. Updates attempt count and next retry time.
102
+ * Returns false if max attempts exceeded (message should be dropped).
103
+ */
104
+ recordAttempt(peerId: string, messageId: string): boolean {
105
+ const queue = this.queues.get(peerId);
106
+ if (!queue) return false;
107
+
108
+ const entry = queue.find((e) => e.id === messageId);
109
+ if (!entry) return false;
110
+
111
+ entry.attempts++;
112
+ entry.lastAttempt = new Date().toISOString();
113
+
114
+ // Check max attempts
115
+ if (this.config.retryMaxAttempts > 0 && entry.attempts >= this.config.retryMaxAttempts) {
116
+ this.removeEntry(peerId, messageId);
117
+ return false;
118
+ }
119
+
120
+ // Calculate next retry
121
+ const delay =
122
+ this.config.retryStrategy === "exponential"
123
+ ? this.config.retryBaseInterval * Math.pow(2, entry.attempts - 1)
124
+ : this.config.retryBaseInterval;
125
+ entry.nextRetry = new Date(Date.now() + delay).toISOString();
126
+ return true;
127
+ }
128
+
129
+ /**
130
+ * Remove a specific entry (e.g., after successful delivery).
131
+ */
132
+ removeEntry(peerId: string, messageId: string): boolean {
133
+ const queue = this.queues.get(peerId);
134
+ if (!queue) return false;
135
+
136
+ const idx = queue.findIndex((e) => e.id === messageId);
137
+ if (idx === -1) return false;
138
+
139
+ queue.splice(idx, 1);
140
+ if (queue.length === 0) this.queues.delete(peerId);
141
+ return true;
142
+ }
143
+
144
+ /**
145
+ * Process tick: expire old messages past TTL.
146
+ */
147
+ tick(): number {
148
+ const now = Date.now();
149
+ let expired = 0;
150
+
151
+ for (const [peerId, queue] of this.queues.entries()) {
152
+ const before = queue.length;
153
+ const remaining = queue.filter((entry) => {
154
+ const age = now - new Date(entry.enqueuedAt).getTime();
155
+ return age < this.config.maxTTL;
156
+ });
157
+ expired += before - remaining.length;
158
+
159
+ if (remaining.length === 0) {
160
+ this.queues.delete(peerId);
161
+ } else {
162
+ this.queues.set(peerId, remaining);
163
+ }
164
+ }
165
+
166
+ if (expired > 0) {
167
+ this.events.emit("queue.expired", { count: expired });
168
+ }
169
+ return expired;
170
+ }
171
+
172
+ /**
173
+ * Start periodic tick timer for TTL expiry.
174
+ */
175
+ startTicking(intervalMs: number = 60_000): void {
176
+ this.stopTicking();
177
+ this.tickTimer = setInterval(() => this.tick(), intervalMs);
178
+ }
179
+
180
+ /**
181
+ * Stop periodic tick timer.
182
+ */
183
+ stopTicking(): void {
184
+ if (this.tickTimer) {
185
+ clearInterval(this.tickTimer);
186
+ this.tickTimer = undefined;
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Get queue depth for a peer.
192
+ */
193
+ size(peerId: string): number {
194
+ return this.queues.get(peerId)?.length ?? 0;
195
+ }
196
+
197
+ /**
198
+ * Get total queue depth across all peers.
199
+ */
200
+ totalSize(): number {
201
+ let total = 0;
202
+ for (const queue of this.queues.values()) {
203
+ total += queue.length;
204
+ }
205
+ return total;
206
+ }
207
+
208
+ /**
209
+ * List peers with queued messages.
210
+ */
211
+ peers(): string[] {
212
+ return Array.from(this.queues.keys());
213
+ }
214
+
215
+ /**
216
+ * Clean up all state. Call on shutdown.
217
+ */
218
+ destroy(): void {
219
+ this.stopTicking();
220
+ this.queues.clear();
221
+ }
222
+ }
@@ -0,0 +1,6 @@
1
+ export { parseAddress, formatAddress, isRemoteAddress, isBroadcastAddress } from "./address.js";
2
+ export { ConnectionManager } from "./connection-manager.js";
3
+ export type { DeliveryResult } from "./connection-manager.js";
4
+ export { DeliveryQueue } from "./delivery-queue.js";
5
+ export { RoutingEngine } from "./routing-engine.js";
6
+ export { TrustManager } from "./trust.js";
@@ -0,0 +1,188 @@
1
+ import { EventEmitter } from "node:events";
2
+ import type {
3
+ FederatedAddress,
4
+ FederationRoutingConfig,
5
+ RoutingEntry,
6
+ WarmAgentStatus,
7
+ } from "../types.js";
8
+
9
+ const DEFAULT_CONFIG: FederationRoutingConfig = {
10
+ strategy: "table",
11
+ tableTTL: 300_000, // 5 minutes
12
+ refreshOnMiss: true,
13
+ broadcastTimeout: 5_000,
14
+ };
15
+
16
+ /**
17
+ * Federation routing engine with configurable strategies.
18
+ *
19
+ * - table: In-memory routing table populated from federation exposure data
20
+ * - broadcast: Forward to all peers, first responder wins
21
+ * - hierarchical: Local table first, then delegate to upstream hubs
22
+ */
23
+ export class RoutingEngine {
24
+ private table = new Map<string, RoutingEntry>();
25
+ private config: FederationRoutingConfig;
26
+
27
+ constructor(
28
+ private events: EventEmitter,
29
+ config?: Partial<FederationRoutingConfig>
30
+ ) {
31
+ this.config = { ...DEFAULT_CONFIG, ...config };
32
+ }
33
+
34
+ /**
35
+ * Resolve a federated address to a peer system ID.
36
+ * Returns null if no route found.
37
+ */
38
+ resolveRoute(addr: FederatedAddress): string | null {
39
+ // If the address specifies a system directly, use it
40
+ if (addr.system) {
41
+ return addr.system;
42
+ }
43
+
44
+ // Otherwise, look up the agent in the routing table
45
+ if (addr.agent) {
46
+ return this.lookupAgent(addr.agent);
47
+ }
48
+
49
+ return null;
50
+ }
51
+
52
+ /**
53
+ * Look up an agent in the routing table.
54
+ * Respects TTL — expired entries are not returned.
55
+ */
56
+ lookupAgent(agentId: string): string | null {
57
+ const entry = this.table.get(agentId);
58
+ if (!entry) return null;
59
+
60
+ // Check TTL
61
+ const age = Date.now() - new Date(entry.lastSeen).getTime();
62
+ if (age > (this.config.tableTTL ?? DEFAULT_CONFIG.tableTTL!)) {
63
+ this.table.delete(agentId);
64
+ return null;
65
+ }
66
+
67
+ // Only route to active or away agents
68
+ if (entry.status === "expired") return null;
69
+
70
+ return entry.peerId;
71
+ }
72
+
73
+ /**
74
+ * Update the routing table with exposure data from a peer.
75
+ * Called when federation/connect completes or peer sends updates.
76
+ */
77
+ updateFromExposure(
78
+ peerId: string,
79
+ agents: Array<{ agentId: string; status?: WarmAgentStatus }>
80
+ ): void {
81
+ const now = new Date().toISOString();
82
+ for (const agent of agents) {
83
+ this.table.set(agent.agentId, {
84
+ agentId: agent.agentId,
85
+ peerId,
86
+ lastSeen: now,
87
+ status: agent.status ?? "active",
88
+ });
89
+ }
90
+ this.events.emit("routing.updated", { peerId, agentCount: agents.length });
91
+ }
92
+
93
+ /**
94
+ * Update status for a specific agent in the routing table.
95
+ */
96
+ updateAgentStatus(agentId: string, status: WarmAgentStatus): void {
97
+ const entry = this.table.get(agentId);
98
+ if (entry) {
99
+ entry.status = status;
100
+ entry.lastSeen = new Date().toISOString();
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Remove all routing entries for a peer (e.g., on disconnect).
106
+ */
107
+ removePeer(peerId: string): number {
108
+ let removed = 0;
109
+ for (const [agentId, entry] of this.table.entries()) {
110
+ if (entry.peerId === peerId) {
111
+ this.table.delete(agentId);
112
+ removed++;
113
+ }
114
+ }
115
+ return removed;
116
+ }
117
+
118
+ /**
119
+ * Get all known peers from the routing table.
120
+ */
121
+ knownPeers(): string[] {
122
+ const peers = new Set<string>();
123
+ for (const entry of this.table.values()) {
124
+ peers.add(entry.peerId);
125
+ }
126
+ return Array.from(peers);
127
+ }
128
+
129
+ /**
130
+ * Get all routing entries (for debugging/inspection).
131
+ */
132
+ getTable(): RoutingEntry[] {
133
+ return Array.from(this.table.values());
134
+ }
135
+
136
+ /**
137
+ * Get the routing strategy in use.
138
+ */
139
+ getStrategy(): FederationRoutingConfig["strategy"] {
140
+ return this.config.strategy;
141
+ }
142
+
143
+ /**
144
+ * Get configured upstream hubs (for hierarchical strategy).
145
+ */
146
+ getUpstream(): string[] {
147
+ return this.config.upstream ?? [];
148
+ }
149
+
150
+ /**
151
+ * Whether to query peers on cache miss (for table strategy).
152
+ */
153
+ shouldRefreshOnMiss(): boolean {
154
+ return this.config.refreshOnMiss ?? true;
155
+ }
156
+
157
+ /**
158
+ * Get broadcast timeout (for broadcast strategy).
159
+ */
160
+ getBroadcastTimeout(): number {
161
+ return this.config.broadcastTimeout ?? 5_000;
162
+ }
163
+
164
+ /**
165
+ * Clean expired entries from the routing table.
166
+ */
167
+ cleanup(): number {
168
+ const ttl = this.config.tableTTL ?? DEFAULT_CONFIG.tableTTL!;
169
+ const now = Date.now();
170
+ let removed = 0;
171
+
172
+ for (const [agentId, entry] of this.table.entries()) {
173
+ const age = now - new Date(entry.lastSeen).getTime();
174
+ if (age > ttl) {
175
+ this.table.delete(agentId);
176
+ removed++;
177
+ }
178
+ }
179
+ return removed;
180
+ }
181
+
182
+ /**
183
+ * Clear all state.
184
+ */
185
+ destroy(): void {
186
+ this.table.clear();
187
+ }
188
+ }
@@ -0,0 +1,71 @@
1
+ import type { FederationTrustPolicy } from "../types.js";
2
+
3
+ const DEFAULT_POLICY: FederationTrustPolicy = {
4
+ allowedServers: [],
5
+ scopePermissions: {},
6
+ requireAuth: false,
7
+ };
8
+
9
+ /**
10
+ * Federation trust enforcement.
11
+ *
12
+ * Layer 1 (implemented): Server allow-list
13
+ * Layer 2 (stub): Scope-based permissions
14
+ * Layer 3 (stub): Token-based auth
15
+ */
16
+ export class TrustManager {
17
+ private policy: FederationTrustPolicy;
18
+
19
+ constructor(policy?: Partial<FederationTrustPolicy>) {
20
+ this.policy = { ...DEFAULT_POLICY, ...policy };
21
+ }
22
+
23
+ /**
24
+ * Check if a system is allowed to connect (Layer 1).
25
+ * If allowedServers is empty, all servers are allowed (open federation).
26
+ */
27
+ canConnect(systemId: string): boolean {
28
+ if (this.policy.allowedServers.length === 0) return true;
29
+ return this.policy.allowedServers.includes(systemId);
30
+ }
31
+
32
+ /**
33
+ * Check if routing from a remote system to a local scope is allowed (Layer 2 — stub).
34
+ * Currently always returns true.
35
+ */
36
+ canRoute(fromSystem: string, toScope: string): boolean {
37
+ // Stub: check scope permissions when implemented
38
+ const allowed = this.policy.scopePermissions[toScope];
39
+ if (!allowed) return true; // No restrictions defined = allow all
40
+ return allowed.includes(fromSystem);
41
+ }
42
+
43
+ /**
44
+ * Validate authentication credentials for a system (Layer 3 — stub).
45
+ * Currently always returns true unless requireAuth is explicitly set.
46
+ */
47
+ validateAuth(
48
+ systemId: string,
49
+ _credentials?: { method?: string; token?: string }
50
+ ): boolean {
51
+ if (!this.policy.requireAuth) return true;
52
+ // Stub: when implemented, validate credentials against authTokens
53
+ const expectedToken = this.policy.authTokens?.[systemId];
54
+ if (!expectedToken) return true; // No token configured = allow
55
+ return _credentials?.token === expectedToken;
56
+ }
57
+
58
+ /**
59
+ * Update the trust policy at runtime.
60
+ */
61
+ updatePolicy(updates: Partial<FederationTrustPolicy>): void {
62
+ Object.assign(this.policy, updates);
63
+ }
64
+
65
+ /**
66
+ * Get current policy (for inspection/debugging).
67
+ */
68
+ getPolicy(): Readonly<FederationTrustPolicy> {
69
+ return this.policy;
70
+ }
71
+ }