network-ai 5.10.1 → 5.11.0

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 (272) hide show
  1. package/INTEGRATION_GUIDE.md +2 -2
  2. package/README.md +5 -3
  3. package/SKILL.md +3 -3
  4. package/dist/esm/adapters/a2a-adapter.js +235 -0
  5. package/dist/esm/adapters/a2a-adapter.js.map +1 -0
  6. package/dist/esm/adapters/adapter-registry.js +613 -0
  7. package/dist/esm/adapters/adapter-registry.js.map +1 -0
  8. package/dist/esm/adapters/agno-adapter.js +140 -0
  9. package/dist/esm/adapters/agno-adapter.js.map +1 -0
  10. package/dist/esm/adapters/anthropic-computer-use-adapter.js +180 -0
  11. package/dist/esm/adapters/anthropic-computer-use-adapter.js.map +1 -0
  12. package/dist/esm/adapters/aps-adapter.js +289 -0
  13. package/dist/esm/adapters/aps-adapter.js.map +1 -0
  14. package/dist/esm/adapters/autogen-adapter.js +141 -0
  15. package/dist/esm/adapters/autogen-adapter.js.map +1 -0
  16. package/dist/esm/adapters/base-adapter.js +104 -0
  17. package/dist/esm/adapters/base-adapter.js.map +1 -0
  18. package/dist/esm/adapters/browser-agent-adapter.js +219 -0
  19. package/dist/esm/adapters/browser-agent-adapter.js.map +1 -0
  20. package/dist/esm/adapters/codex-adapter.js +318 -0
  21. package/dist/esm/adapters/codex-adapter.js.map +1 -0
  22. package/dist/esm/adapters/copilot-adapter.js +132 -0
  23. package/dist/esm/adapters/copilot-adapter.js.map +1 -0
  24. package/dist/esm/adapters/crewai-adapter.js +148 -0
  25. package/dist/esm/adapters/crewai-adapter.js.map +1 -0
  26. package/dist/esm/adapters/custom-adapter.js +142 -0
  27. package/dist/esm/adapters/custom-adapter.js.map +1 -0
  28. package/dist/esm/adapters/custom-streaming-adapter.js +181 -0
  29. package/dist/esm/adapters/custom-streaming-adapter.js.map +1 -0
  30. package/dist/esm/adapters/dspy-adapter.js +127 -0
  31. package/dist/esm/adapters/dspy-adapter.js.map +1 -0
  32. package/dist/esm/adapters/haystack-adapter.js +149 -0
  33. package/dist/esm/adapters/haystack-adapter.js.map +1 -0
  34. package/dist/esm/adapters/hermes-adapter.js +217 -0
  35. package/dist/esm/adapters/hermes-adapter.js.map +1 -0
  36. package/dist/esm/adapters/index.js +109 -0
  37. package/dist/esm/adapters/index.js.map +1 -0
  38. package/dist/esm/adapters/langchain-adapter.js +134 -0
  39. package/dist/esm/adapters/langchain-adapter.js.map +1 -0
  40. package/dist/esm/adapters/langchain-streaming-adapter.js +161 -0
  41. package/dist/esm/adapters/langchain-streaming-adapter.js.map +1 -0
  42. package/dist/esm/adapters/langgraph-adapter.js +119 -0
  43. package/dist/esm/adapters/langgraph-adapter.js.map +1 -0
  44. package/dist/esm/adapters/llamaindex-adapter.js +135 -0
  45. package/dist/esm/adapters/llamaindex-adapter.js.map +1 -0
  46. package/dist/esm/adapters/mcp-adapter.js +200 -0
  47. package/dist/esm/adapters/mcp-adapter.js.map +1 -0
  48. package/dist/esm/adapters/minimax-adapter.js +233 -0
  49. package/dist/esm/adapters/minimax-adapter.js.map +1 -0
  50. package/dist/esm/adapters/nemoclaw-adapter.js +465 -0
  51. package/dist/esm/adapters/nemoclaw-adapter.js.map +1 -0
  52. package/dist/esm/adapters/openai-agents-adapter.js +118 -0
  53. package/dist/esm/adapters/openai-agents-adapter.js.map +1 -0
  54. package/dist/esm/adapters/openai-assistants-adapter.js +130 -0
  55. package/dist/esm/adapters/openai-assistants-adapter.js.map +1 -0
  56. package/dist/esm/adapters/openclaw-adapter.js +107 -0
  57. package/dist/esm/adapters/openclaw-adapter.js.map +1 -0
  58. package/dist/esm/adapters/orchestrator-adapter.js +218 -0
  59. package/dist/esm/adapters/orchestrator-adapter.js.map +1 -0
  60. package/dist/esm/adapters/pydantic-ai-adapter.js +163 -0
  61. package/dist/esm/adapters/pydantic-ai-adapter.js.map +1 -0
  62. package/dist/esm/adapters/rlm-adapter.js +167 -0
  63. package/dist/esm/adapters/rlm-adapter.js.map +1 -0
  64. package/dist/esm/adapters/semantic-kernel-adapter.js +123 -0
  65. package/dist/esm/adapters/semantic-kernel-adapter.js.map +1 -0
  66. package/dist/esm/adapters/streaming-base-adapter.js +74 -0
  67. package/dist/esm/adapters/streaming-base-adapter.js.map +1 -0
  68. package/dist/esm/adapters/vertex-ai-adapter.js +166 -0
  69. package/dist/esm/adapters/vertex-ai-adapter.js.map +1 -0
  70. package/dist/esm/demo-control-plane.js +147 -0
  71. package/dist/esm/demo-control-plane.js.map +1 -0
  72. package/dist/esm/demo-worktree-dashboard.js +131 -0
  73. package/dist/esm/demo-worktree-dashboard.js.map +1 -0
  74. package/dist/esm/examples/01-hello-swarm.js +165 -0
  75. package/dist/esm/examples/01-hello-swarm.js.map +1 -0
  76. package/dist/esm/examples/02-fsm-pipeline.js +189 -0
  77. package/dist/esm/examples/02-fsm-pipeline.js.map +1 -0
  78. package/dist/esm/examples/03-parallel-agents.js +192 -0
  79. package/dist/esm/examples/03-parallel-agents.js.map +1 -0
  80. package/dist/esm/examples/05-code-review-swarm.js +1177 -0
  81. package/dist/esm/examples/05-code-review-swarm.js.map +1 -0
  82. package/dist/esm/examples/06-ai-pipeline-demo.js +263 -0
  83. package/dist/esm/examples/06-ai-pipeline-demo.js.map +1 -0
  84. package/dist/esm/examples/07-full-showcase.js +946 -0
  85. package/dist/esm/examples/07-full-showcase.js.map +1 -0
  86. package/dist/esm/examples/08-control-plane-stress-demo.js +186 -0
  87. package/dist/esm/examples/08-control-plane-stress-demo.js.map +1 -0
  88. package/dist/esm/examples/09-real-langchain.js +231 -0
  89. package/dist/esm/examples/09-real-langchain.js.map +1 -0
  90. package/dist/esm/examples/10-nemoclaw-sandbox-swarm.js +270 -0
  91. package/dist/esm/examples/10-nemoclaw-sandbox-swarm.js.map +1 -0
  92. package/dist/esm/examples/demo-runner.js +119 -0
  93. package/dist/esm/examples/demo-runner.js.map +1 -0
  94. package/dist/esm/index.js +1352 -0
  95. package/dist/esm/index.js.map +1 -0
  96. package/dist/esm/lib/adapter-hooks.js +216 -0
  97. package/dist/esm/lib/adapter-hooks.js.map +1 -0
  98. package/dist/esm/lib/adapter-test-harness.js +118 -0
  99. package/dist/esm/lib/adapter-test-harness.js.map +1 -0
  100. package/dist/esm/lib/agent-conversation.js +155 -0
  101. package/dist/esm/lib/agent-conversation.js.map +1 -0
  102. package/dist/esm/lib/agent-debate.js +146 -0
  103. package/dist/esm/lib/agent-debate.js.map +1 -0
  104. package/dist/esm/lib/agent-memory.js +336 -0
  105. package/dist/esm/lib/agent-memory.js.map +1 -0
  106. package/dist/esm/lib/agent-runtime.js +818 -0
  107. package/dist/esm/lib/agent-runtime.js.map +1 -0
  108. package/dist/esm/lib/agent-vcr.js +218 -0
  109. package/dist/esm/lib/agent-vcr.js.map +1 -0
  110. package/dist/esm/lib/anomaly-detector.js +178 -0
  111. package/dist/esm/lib/anomaly-detector.js.map +1 -0
  112. package/dist/esm/lib/approval-inbox.js +385 -0
  113. package/dist/esm/lib/approval-inbox.js.map +1 -0
  114. package/dist/esm/lib/auth-guardian.js +692 -0
  115. package/dist/esm/lib/auth-guardian.js.map +1 -0
  116. package/dist/esm/lib/auth-validator.js +32 -0
  117. package/dist/esm/lib/auth-validator.js.map +1 -0
  118. package/dist/esm/lib/blackboard-backend-crdt.js +251 -0
  119. package/dist/esm/lib/blackboard-backend-crdt.js.map +1 -0
  120. package/dist/esm/lib/blackboard-backend-redis.js +244 -0
  121. package/dist/esm/lib/blackboard-backend-redis.js.map +1 -0
  122. package/dist/esm/lib/blackboard-backend.js +141 -0
  123. package/dist/esm/lib/blackboard-backend.js.map +1 -0
  124. package/dist/esm/lib/blackboard-validator.js +985 -0
  125. package/dist/esm/lib/blackboard-validator.js.map +1 -0
  126. package/dist/esm/lib/circuit-breaker.js +164 -0
  127. package/dist/esm/lib/circuit-breaker.js.map +1 -0
  128. package/dist/esm/lib/claim-verifier.js +173 -0
  129. package/dist/esm/lib/claim-verifier.js.map +1 -0
  130. package/dist/esm/lib/comparison-runner.js +138 -0
  131. package/dist/esm/lib/comparison-runner.js.map +1 -0
  132. package/dist/esm/lib/compliance-monitor.js +261 -0
  133. package/dist/esm/lib/compliance-monitor.js.map +1 -0
  134. package/dist/esm/lib/confidence-filter.js +210 -0
  135. package/dist/esm/lib/confidence-filter.js.map +1 -0
  136. package/dist/esm/lib/config-watcher.js +215 -0
  137. package/dist/esm/lib/config-watcher.js.map +1 -0
  138. package/dist/esm/lib/consistency.js +274 -0
  139. package/dist/esm/lib/consistency.js.map +1 -0
  140. package/dist/esm/lib/console-ui.js +276 -0
  141. package/dist/esm/lib/console-ui.js.map +1 -0
  142. package/dist/esm/lib/context-throttler.js +171 -0
  143. package/dist/esm/lib/context-throttler.js.map +1 -0
  144. package/dist/esm/lib/control-plane.js +527 -0
  145. package/dist/esm/lib/control-plane.js.map +1 -0
  146. package/dist/esm/lib/cost-governor.js +128 -0
  147. package/dist/esm/lib/cost-governor.js.map +1 -0
  148. package/dist/esm/lib/cost-heatmap.js +161 -0
  149. package/dist/esm/lib/cost-heatmap.js.map +1 -0
  150. package/dist/esm/lib/coverage-gate.js +213 -0
  151. package/dist/esm/lib/coverage-gate.js.map +1 -0
  152. package/dist/esm/lib/coverage-reporter.js +177 -0
  153. package/dist/esm/lib/coverage-reporter.js.map +1 -0
  154. package/dist/esm/lib/crdt.js +141 -0
  155. package/dist/esm/lib/crdt.js.map +1 -0
  156. package/dist/esm/lib/dashboard-server.js +403 -0
  157. package/dist/esm/lib/dashboard-server.js.map +1 -0
  158. package/dist/esm/lib/dry-run.js +130 -0
  159. package/dist/esm/lib/dry-run.js.map +1 -0
  160. package/dist/esm/lib/env-manager.js +518 -0
  161. package/dist/esm/lib/env-manager.js.map +1 -0
  162. package/dist/esm/lib/errors.js +201 -0
  163. package/dist/esm/lib/errors.js.map +1 -0
  164. package/dist/esm/lib/event-bus.js +229 -0
  165. package/dist/esm/lib/event-bus.js.map +1 -0
  166. package/dist/esm/lib/explainability.js +102 -0
  167. package/dist/esm/lib/explainability.js.map +1 -0
  168. package/dist/esm/lib/fan-out.js +237 -0
  169. package/dist/esm/lib/fan-out.js.map +1 -0
  170. package/dist/esm/lib/federated-budget.js +322 -0
  171. package/dist/esm/lib/federated-budget.js.map +1 -0
  172. package/dist/esm/lib/fsm-journey.js +478 -0
  173. package/dist/esm/lib/fsm-journey.js.map +1 -0
  174. package/dist/esm/lib/goal-decomposer.js +698 -0
  175. package/dist/esm/lib/goal-decomposer.js.map +1 -0
  176. package/dist/esm/lib/goal-dsl.js +391 -0
  177. package/dist/esm/lib/goal-dsl.js.map +1 -0
  178. package/dist/esm/lib/job-queue.js +310 -0
  179. package/dist/esm/lib/job-queue.js.map +1 -0
  180. package/dist/esm/lib/landscape-agent.js +134 -0
  181. package/dist/esm/lib/landscape-agent.js.map +1 -0
  182. package/dist/esm/lib/learning-loop.js +181 -0
  183. package/dist/esm/lib/learning-loop.js.map +1 -0
  184. package/dist/esm/lib/lifecycle-hooks.js +148 -0
  185. package/dist/esm/lib/lifecycle-hooks.js.map +1 -0
  186. package/dist/esm/lib/locked-blackboard.js +1295 -0
  187. package/dist/esm/lib/locked-blackboard.js.map +1 -0
  188. package/dist/esm/lib/logger.js +150 -0
  189. package/dist/esm/lib/logger.js.map +1 -0
  190. package/dist/esm/lib/mcp-blackboard-tools.js +298 -0
  191. package/dist/esm/lib/mcp-blackboard-tools.js.map +1 -0
  192. package/dist/esm/lib/mcp-bridge.js +357 -0
  193. package/dist/esm/lib/mcp-bridge.js.map +1 -0
  194. package/dist/esm/lib/mcp-tool-consumer.js +287 -0
  195. package/dist/esm/lib/mcp-tool-consumer.js.map +1 -0
  196. package/dist/esm/lib/mcp-tools-control.js +392 -0
  197. package/dist/esm/lib/mcp-tools-control.js.map +1 -0
  198. package/dist/esm/lib/mcp-tools-extended.js +371 -0
  199. package/dist/esm/lib/mcp-tools-extended.js.map +1 -0
  200. package/dist/esm/lib/mcp-transport-http.js +528 -0
  201. package/dist/esm/lib/mcp-transport-http.js.map +1 -0
  202. package/dist/esm/lib/mcp-transport-sse.js +503 -0
  203. package/dist/esm/lib/mcp-transport-sse.js.map +1 -0
  204. package/dist/esm/lib/metrics.js +284 -0
  205. package/dist/esm/lib/metrics.js.map +1 -0
  206. package/dist/esm/lib/orchestrator-types.js +66 -0
  207. package/dist/esm/lib/orchestrator-types.js.map +1 -0
  208. package/dist/esm/lib/otel-bridge.js +167 -0
  209. package/dist/esm/lib/otel-bridge.js.map +1 -0
  210. package/dist/esm/lib/partition-planner.js +246 -0
  211. package/dist/esm/lib/partition-planner.js.map +1 -0
  212. package/dist/esm/lib/phase-pipeline.js +367 -0
  213. package/dist/esm/lib/phase-pipeline.js.map +1 -0
  214. package/dist/esm/lib/playground.js +224 -0
  215. package/dist/esm/lib/playground.js.map +1 -0
  216. package/dist/esm/lib/qa-orchestrator.js +296 -0
  217. package/dist/esm/lib/qa-orchestrator.js.map +1 -0
  218. package/dist/esm/lib/quadtree.js +259 -0
  219. package/dist/esm/lib/quadtree.js.map +1 -0
  220. package/dist/esm/lib/route-classifier.js +217 -0
  221. package/dist/esm/lib/route-classifier.js.map +1 -0
  222. package/dist/esm/lib/semantic-search.js +235 -0
  223. package/dist/esm/lib/semantic-search.js.map +1 -0
  224. package/dist/esm/lib/shared-blackboard.js +249 -0
  225. package/dist/esm/lib/shared-blackboard.js.map +1 -0
  226. package/dist/esm/lib/skill-composer.js +190 -0
  227. package/dist/esm/lib/skill-composer.js.map +1 -0
  228. package/dist/esm/lib/speculative-executor.js +107 -0
  229. package/dist/esm/lib/speculative-executor.js.map +1 -0
  230. package/dist/esm/lib/strategy-agent.js +626 -0
  231. package/dist/esm/lib/strategy-agent.js.map +1 -0
  232. package/dist/esm/lib/swarm-transport.js +307 -0
  233. package/dist/esm/lib/swarm-transport.js.map +1 -0
  234. package/dist/esm/lib/swarm-utils.js +510 -0
  235. package/dist/esm/lib/swarm-utils.js.map +1 -0
  236. package/dist/esm/lib/task-decomposer.js +272 -0
  237. package/dist/esm/lib/task-decomposer.js.map +1 -0
  238. package/dist/esm/lib/telemetry-provider.js +207 -0
  239. package/dist/esm/lib/telemetry-provider.js.map +1 -0
  240. package/dist/esm/lib/timeline-scrubber.js +173 -0
  241. package/dist/esm/lib/timeline-scrubber.js.map +1 -0
  242. package/dist/esm/lib/topology.js +591 -0
  243. package/dist/esm/lib/topology.js.map +1 -0
  244. package/dist/esm/lib/transport-agent.js +366 -0
  245. package/dist/esm/lib/transport-agent.js.map +1 -0
  246. package/dist/esm/lib/work-tree-dashboard.js +583 -0
  247. package/dist/esm/lib/work-tree-dashboard.js.map +1 -0
  248. package/dist/esm/lib/work-tree-ui.js +333 -0
  249. package/dist/esm/lib/work-tree-ui.js.map +1 -0
  250. package/dist/esm/lib/work-tree.js +480 -0
  251. package/dist/esm/lib/work-tree.js.map +1 -0
  252. package/dist/esm/run.js +144 -0
  253. package/dist/esm/run.js.map +1 -0
  254. package/dist/esm/security.js +1122 -0
  255. package/dist/esm/security.js.map +1 -0
  256. package/dist/index.d.ts +2 -0
  257. package/dist/index.d.ts.map +1 -1
  258. package/dist/index.js +6 -1
  259. package/dist/index.js.map +1 -1
  260. package/dist/lib/mcp-transport-http.d.ts +203 -0
  261. package/dist/lib/mcp-transport-http.d.ts.map +1 -0
  262. package/dist/lib/mcp-transport-http.js +528 -0
  263. package/dist/lib/mcp-transport-http.js.map +1 -0
  264. package/dist/lib/phase-pipeline.d.ts +31 -0
  265. package/dist/lib/phase-pipeline.d.ts.map +1 -1
  266. package/dist/lib/phase-pipeline.js +93 -1
  267. package/dist/lib/phase-pipeline.js.map +1 -1
  268. package/dist/lib/semantic-search.d.ts +42 -6
  269. package/dist/lib/semantic-search.d.ts.map +1 -1
  270. package/dist/lib/semantic-search.js +87 -6
  271. package/dist/lib/semantic-search.js.map +1 -1
  272. package/package.json +24 -4
@@ -0,0 +1,237 @@
1
+ "use strict";
2
+ /**
3
+ * Fan-Out / Fan-In — Parallel agent spawning and result aggregation
4
+ *
5
+ * Launches multiple agents in parallel (fan-out) and combines their results
6
+ * using pluggable strategies (fan-in). Supports concurrency limits, timeouts,
7
+ * and custom aggregation. Inspired by Claude Code's parallel agent patterns.
8
+ *
9
+ * @module FanOutFanIn
10
+ * @version 1.0.0
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.FanOutFanIn = void 0;
14
+ // ============================================================================
15
+ // FAN-OUT / FAN-IN
16
+ // ============================================================================
17
+ /**
18
+ * Parallel agent execution with pluggable result aggregation.
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * const fanout = new FanOutFanIn(registry, { agentId: 'orchestrator' });
23
+ *
24
+ * const steps: FanOutStep[] = [
25
+ * { agentId: 'researcher-a', payload: { action: 'search', params: { q: 'AI safety' } }, label: 'web' },
26
+ * { agentId: 'researcher-b', payload: { action: 'search', params: { q: 'AI safety' } }, label: 'papers' },
27
+ * { agentId: 'researcher-c', payload: { action: 'search', params: { q: 'AI safety' } }, label: 'news' },
28
+ * ];
29
+ *
30
+ * const results = await fanout.fanOut(steps, { concurrency: 2 });
31
+ * const aggregated = fanout.fanIn(results, 'merge');
32
+ * ```
33
+ */
34
+ class FanOutFanIn {
35
+ registry;
36
+ baseContext;
37
+ /**
38
+ * @param registry Adapter registry for agent execution
39
+ * @param baseContext Default execution context (merged with step-level overrides)
40
+ */
41
+ constructor(registry, baseContext) {
42
+ this.registry = registry;
43
+ this.baseContext = baseContext;
44
+ }
45
+ /**
46
+ * Execute agents in parallel (fan-out phase).
47
+ *
48
+ * Uses a true semaphore queue — as soon as one slot frees up the next
49
+ * step starts, rather than waiting for an entire chunk to drain.
50
+ *
51
+ * @param steps Steps to execute
52
+ * @param options Concurrency and timeout settings
53
+ */
54
+ async fanOut(steps, options = {}) {
55
+ if (steps.length === 0)
56
+ return [];
57
+ const maxConcurrency = Math.max(1, options.concurrency ?? steps.length);
58
+ const continueOnError = options.continueOnError ?? true;
59
+ const results = new Array(steps.length);
60
+ return new Promise((resolve) => {
61
+ let activeCount = 0;
62
+ let nextToStart = 0;
63
+ let completedCount = 0;
64
+ let shouldAbort = false;
65
+ const checkDone = () => {
66
+ if (completedCount === steps.length) {
67
+ resolve(results);
68
+ }
69
+ };
70
+ /** Mark all not-yet-started (and not-yet-filled) slots as SKIPPED. */
71
+ const markRemainingSkipped = (fromIdx) => {
72
+ for (let k = fromIdx; k < steps.length; k++) {
73
+ if (results[k] === undefined) {
74
+ results[k] = {
75
+ agentId: steps[k].agentId,
76
+ label: steps[k].label,
77
+ index: k,
78
+ result: {
79
+ success: false,
80
+ error: { code: 'FANOUT_SKIPPED', message: 'Skipped due to earlier failure', recoverable: false },
81
+ },
82
+ durationMs: 0,
83
+ };
84
+ completedCount++;
85
+ }
86
+ }
87
+ };
88
+ const onTaskDone = (tagged, isFailure) => {
89
+ results[tagged.index] = tagged;
90
+ activeCount--;
91
+ completedCount++;
92
+ if (!continueOnError && isFailure && !shouldAbort) {
93
+ shouldAbort = true;
94
+ // Skip all tasks that haven't been picked up yet
95
+ markRemainingSkipped(nextToStart);
96
+ }
97
+ checkDone();
98
+ if (!shouldAbort) {
99
+ scheduleNext();
100
+ }
101
+ };
102
+ const scheduleNext = () => {
103
+ while (activeCount < maxConcurrency && nextToStart < steps.length && !shouldAbort) {
104
+ const idx = nextToStart++;
105
+ activeCount++;
106
+ this.executeStep(steps[idx], idx, options.timeoutMs).then((tagged) => onTaskDone(tagged, !tagged.result.success), (reason) => {
107
+ const tagged = {
108
+ agentId: steps[idx].agentId,
109
+ label: steps[idx].label,
110
+ index: idx,
111
+ result: {
112
+ success: false,
113
+ error: { code: 'FANOUT_ERROR', message: String(reason), recoverable: false },
114
+ },
115
+ durationMs: 0,
116
+ };
117
+ onTaskDone(tagged, true);
118
+ });
119
+ }
120
+ };
121
+ scheduleNext();
122
+ });
123
+ }
124
+ /**
125
+ * Aggregate results (fan-in phase).
126
+ *
127
+ * @param results Tagged results from fan-out
128
+ * @param strategy Aggregation strategy to apply
129
+ * @param customReducer Required when strategy is 'custom'
130
+ */
131
+ fanIn(results, strategy = 'merge', customReducer) {
132
+ const totalMs = results.reduce((sum, r) => sum + r.durationMs, 0);
133
+ const successCount = results.filter(r => r.result.success).length;
134
+ const failureCount = results.length - successCount;
135
+ let success;
136
+ let data;
137
+ switch (strategy) {
138
+ case 'merge': {
139
+ success = successCount > 0;
140
+ data = results.map(r => ({
141
+ agentId: r.agentId,
142
+ label: r.label,
143
+ success: r.result.success,
144
+ data: r.result.data,
145
+ }));
146
+ break;
147
+ }
148
+ case 'firstSuccess': {
149
+ const first = results.find(r => r.result.success);
150
+ success = !!first;
151
+ data = first?.result.data ?? null;
152
+ break;
153
+ }
154
+ case 'vote': {
155
+ const votes = new Map();
156
+ for (const r of results) {
157
+ if (!r.result.success)
158
+ continue;
159
+ const key = JSON.stringify(r.result.data);
160
+ const existing = votes.get(key);
161
+ if (existing) {
162
+ existing.count++;
163
+ }
164
+ else {
165
+ votes.set(key, { count: 1, data: r.result.data });
166
+ }
167
+ }
168
+ let best;
169
+ for (const v of votes.values()) {
170
+ if (!best || v.count > best.count)
171
+ best = v;
172
+ }
173
+ success = !!best;
174
+ data = best?.data ?? null;
175
+ break;
176
+ }
177
+ case 'consensus': {
178
+ const successResults = results.filter(r => r.result.success);
179
+ if (successResults.length === 0) {
180
+ success = false;
181
+ data = null;
182
+ }
183
+ else {
184
+ const first = JSON.stringify(successResults[0].result.data);
185
+ const allAgree = successResults.every(r => JSON.stringify(r.result.data) === first);
186
+ success = allAgree;
187
+ data = allAgree ? successResults[0].result.data : null;
188
+ }
189
+ break;
190
+ }
191
+ case 'custom': {
192
+ if (!customReducer) {
193
+ throw new Error('customReducer is required when strategy is "custom"');
194
+ }
195
+ const reduced = customReducer(results);
196
+ success = reduced.success;
197
+ data = reduced.data;
198
+ break;
199
+ }
200
+ }
201
+ return { success, strategy, data, results, totalMs, successCount, failureCount };
202
+ }
203
+ /**
204
+ * Convenience: fan-out + fan-in in a single call.
205
+ */
206
+ async run(steps, strategy = 'merge', options, customReducer) {
207
+ const results = await this.fanOut(steps, options);
208
+ return this.fanIn(results, strategy, customReducer);
209
+ }
210
+ // --------------------------------------------------------------------------
211
+ // Private helpers
212
+ // --------------------------------------------------------------------------
213
+ async executeStep(step, index, globalTimeout) {
214
+ const ctx = { ...this.baseContext, ...step.context };
215
+ const timeout = step.timeoutMs ?? globalTimeout;
216
+ const start = Date.now();
217
+ let result;
218
+ if (timeout) {
219
+ result = await Promise.race([
220
+ this.registry.executeAgent(step.agentId, step.payload, ctx),
221
+ new Promise((_, reject) => setTimeout(() => reject(new Error(`Fan-out timeout: agent "${step.agentId}" exceeded ${timeout}ms`)), timeout)),
222
+ ]);
223
+ }
224
+ else {
225
+ result = await this.registry.executeAgent(step.agentId, step.payload, ctx);
226
+ }
227
+ return {
228
+ agentId: step.agentId,
229
+ label: step.label,
230
+ index,
231
+ result,
232
+ durationMs: Date.now() - start,
233
+ };
234
+ }
235
+ }
236
+ exports.FanOutFanIn = FanOutFanIn;
237
+ //# sourceMappingURL=fan-out.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fan-out.js","sourceRoot":"","sources":["../../../lib/fan-out.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,322 @@
1
+ "use strict";
2
+ /**
3
+ * Federated Budget Tracking
4
+ *
5
+ * Tracks token spending across distributed agent swarms. Each `FederatedBudget`
6
+ * instance enforces a global ceiling shared among all agents that call `spend()`.
7
+ *
8
+ * When an optional `BlackboardBackend` is supplied, the budget state is written
9
+ * to the blackboard after every mutation. Wiring a `CrdtBackend` or `RedisBackend`
10
+ * as the underlying backend therefore gives automatic cross-node synchronization
11
+ * with no extra configuration.
12
+ *
13
+ * Architecture:
14
+ * - In-memory `spent` map keyed by `agentId` holds per-agent cumulative totals.
15
+ * - `spend()` is synchronous and enforces both the global ceiling and an optional
16
+ * per-agent ceiling in a single check.
17
+ * - A `blackboard` backend (if supplied) stores a JSON snapshot under `budgetKey`
18
+ * after every `spend()` / `reset()` / `setCeiling()` call so distributed nodes
19
+ * can read the latest state.
20
+ * - `loadFromBlackboard()` deserializes a previously saved snapshot so a
21
+ * restarted node can recover its prior accumulated spend.
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * import { FederatedBudget } from 'network-ai';
26
+ *
27
+ * const budget = new FederatedBudget({ ceiling: 10_000 });
28
+ *
29
+ * budget.spend('agent-1', 3000); // { allowed: true, remaining: 7000 }
30
+ * budget.spend('agent-2', 8000); // { allowed: false, remaining: 7000 }
31
+ * budget.remaining(); // 7000
32
+ * budget.getSpendLog(); // { 'agent-1': 3000 }
33
+ * ```
34
+ *
35
+ * @example With blackboard persistence
36
+ * ```typescript
37
+ * import { CrdtBackend } from 'network-ai';
38
+ * import { FederatedBudget } from 'network-ai';
39
+ *
40
+ * const node = new CrdtBackend('node-a');
41
+ * const budget = new FederatedBudget({ ceiling: 50_000, blackboard: node });
42
+ *
43
+ * budget.spend('agent-1', 1000);
44
+ * // State is now stored in node under 'federated-budget'
45
+ * // Sync node to other CrdtBackend nodes to propagate the spend.
46
+ * ```
47
+ *
48
+ * @module FederatedBudget
49
+ * @version 1.0.0
50
+ * @license MIT
51
+ */
52
+ Object.defineProperty(exports, "__esModule", { value: true });
53
+ exports.FederatedBudget = void 0;
54
+ // ============================================================================
55
+ // FEDERATED BUDGET
56
+ // ============================================================================
57
+ /**
58
+ * Federated token-budget tracker for distributed agent swarms.
59
+ *
60
+ * Enforces a shared global ceiling across all agents and optionally an
61
+ * individual per-agent ceiling. State can be persisted to any
62
+ * `BlackboardBackend` for cross-node visibility.
63
+ */
64
+ class FederatedBudget {
65
+ _ceiling;
66
+ _dynamicCeiling;
67
+ _perAgentCeiling;
68
+ _spent = new Map();
69
+ _totalSpent = 0;
70
+ _log = [];
71
+ _blackboard;
72
+ _budgetKey;
73
+ _bbAgent;
74
+ constructor(options) {
75
+ if (typeof options.ceiling !== 'number' || options.ceiling <= 0 || !Number.isFinite(options.ceiling)) {
76
+ throw new RangeError('FederatedBudget: ceiling must be a positive finite number');
77
+ }
78
+ if (options.perAgentCeiling !== undefined &&
79
+ (typeof options.perAgentCeiling !== 'number' ||
80
+ options.perAgentCeiling <= 0 ||
81
+ !Number.isFinite(options.perAgentCeiling))) {
82
+ throw new RangeError('FederatedBudget: perAgentCeiling must be a positive finite number when provided');
83
+ }
84
+ this._ceiling = options.ceiling;
85
+ this._dynamicCeiling = options.ceiling;
86
+ this._perAgentCeiling = options.perAgentCeiling;
87
+ this._blackboard = options.blackboard;
88
+ this._budgetKey = options.budgetKey ?? 'federated-budget';
89
+ this._bbAgent = options.blackboardAgent ?? 'federated-budget-tracker';
90
+ }
91
+ // --------------------------------------------------------------------------
92
+ // PUBLIC API
93
+ // --------------------------------------------------------------------------
94
+ /**
95
+ * Attempt to spend `tokens` on behalf of `agentId`.
96
+ *
97
+ * The spend is allowed only when:
98
+ * 1. `totalSpent + tokens <= ceiling` (global ceiling not breached), AND
99
+ * 2. `agentSpent + tokens <= perAgentCeiling` if a per-agent ceiling is set.
100
+ *
101
+ * When allowed the internal counters are updated and the state is persisted
102
+ * to the blackboard (if configured).
103
+ *
104
+ * @param agentId Identifier of the spending agent. Must be a non-empty string.
105
+ * @param tokens Number of tokens to spend. Must be a positive integer.
106
+ * @returns `SpendResult` with `allowed`, `remaining`, and optional `deniedReason`.
107
+ */
108
+ spend(agentId, tokens) {
109
+ if (!agentId || typeof agentId !== 'string') {
110
+ throw new TypeError('FederatedBudget.spend: agentId must be a non-empty string');
111
+ }
112
+ if (typeof tokens !== 'number' || tokens <= 0 || !Number.isFinite(tokens)) {
113
+ throw new RangeError('FederatedBudget.spend: tokens must be a positive finite number');
114
+ }
115
+ const agentSpent = this._spent.get(agentId) ?? 0;
116
+ // Check per-agent ceiling first (lower ceiling wins)
117
+ if (this._perAgentCeiling !== undefined && agentSpent + tokens > this._perAgentCeiling) {
118
+ return {
119
+ allowed: false,
120
+ remaining: this.remaining(),
121
+ deniedReason: 'per_agent_ceiling',
122
+ };
123
+ }
124
+ // Check global ceiling
125
+ if (this._totalSpent + tokens > this._dynamicCeiling) {
126
+ return {
127
+ allowed: false,
128
+ remaining: this.remaining(),
129
+ deniedReason: 'global_ceiling',
130
+ };
131
+ }
132
+ // Commit
133
+ this._spent.set(agentId, agentSpent + tokens);
134
+ this._totalSpent += tokens;
135
+ this._log.push({
136
+ agentId,
137
+ tokens,
138
+ timestamp: new Date().toISOString(),
139
+ });
140
+ this._persist();
141
+ return {
142
+ allowed: true,
143
+ remaining: this.remaining(),
144
+ };
145
+ }
146
+ /**
147
+ * Remaining tokens in the global pool.
148
+ */
149
+ remaining() {
150
+ return Math.max(0, this._dynamicCeiling - this._totalSpent);
151
+ }
152
+ /**
153
+ * Total tokens spent across all agents.
154
+ */
155
+ getTotalSpent() {
156
+ return this._totalSpent;
157
+ }
158
+ /**
159
+ * Tokens spent by a specific agent. Returns `0` if the agent has no spend.
160
+ */
161
+ getAgentSpent(agentId) {
162
+ return this._spent.get(agentId) ?? 0;
163
+ }
164
+ /**
165
+ * Per-agent spend totals as a plain object: `{ agentId: totalTokens }`.
166
+ */
167
+ getSpendLog() {
168
+ const result = {};
169
+ for (const [id, total] of this._spent) {
170
+ result[id] = total;
171
+ }
172
+ return result;
173
+ }
174
+ /**
175
+ * Detailed transaction log — every individual `spend()` call in order.
176
+ */
177
+ getTransactionLog() {
178
+ return this._log.slice();
179
+ }
180
+ /**
181
+ * Current global ceiling (may differ from the original if `setCeiling()` was called).
182
+ */
183
+ getCeiling() {
184
+ return this._dynamicCeiling;
185
+ }
186
+ /**
187
+ * Per-agent ceiling, or `undefined` if none was configured.
188
+ */
189
+ getPerAgentCeiling() {
190
+ return this._perAgentCeiling;
191
+ }
192
+ /**
193
+ * Dynamically adjust the global ceiling.
194
+ *
195
+ * The new ceiling must be a positive finite number. It may be set below
196
+ * the current `totalSpent` (no previously approved spends are reversed, but
197
+ * future spends will be denied until tokens are freed by a `reset()`).
198
+ *
199
+ * @param ceiling New global ceiling.
200
+ */
201
+ setCeiling(ceiling) {
202
+ if (typeof ceiling !== 'number' || ceiling <= 0 || !Number.isFinite(ceiling)) {
203
+ throw new RangeError('FederatedBudget.setCeiling: ceiling must be a positive finite number');
204
+ }
205
+ this._dynamicCeiling = ceiling;
206
+ this._persist();
207
+ }
208
+ /**
209
+ * Reset all spend counters and clear the transaction log.
210
+ *
211
+ * The global ceiling is preserved (its current value after any `setCeiling()`
212
+ * calls). After a reset `remaining() === getCeiling()`.
213
+ *
214
+ * The blackboard entry (if configured) is updated with the reset state.
215
+ */
216
+ reset() {
217
+ this._spent.clear();
218
+ this._totalSpent = 0;
219
+ this._log.length = 0;
220
+ this._persist();
221
+ }
222
+ /**
223
+ * Spawn a child budget whose ceiling is drawn from this budget's remaining pool.
224
+ *
225
+ * RLM-style recursive budget propagation: the child gets an isolated spend
226
+ * tracker capped at `childCeiling` (or the full remaining amount if omitted).
227
+ * Call `commit()` on the returned handle to propagate the child's total spend
228
+ * back to the parent as a single atomic `spend()` call.
229
+ *
230
+ * This enables recursive sub-agent trees where each level can only consume
231
+ * what its parent has left, and costs bubble back up the chain automatically.
232
+ *
233
+ * @example
234
+ * ```typescript
235
+ * const root = new FederatedBudget({ ceiling: 10_000 });
236
+ * const { budget: child, commit } = root.spawnChild('sub-agent-A', 3000);
237
+ * child.spend('worker-1', 800);
238
+ * child.spend('worker-2', 600);
239
+ * commit(); // propagates 1400 tokens back to root
240
+ * root.getTotalSpent(); // 1400
241
+ * ```
242
+ *
243
+ * @param spenderId agentId recorded on the parent when `commit()` is called.
244
+ * @param childCeiling Optional upper bound for the child (defaults to `remaining()`).
245
+ * @returns Object with the child `FederatedBudget` and a `commit` function.
246
+ */
247
+ spawnChild(spenderId, childCeiling) {
248
+ if (!spenderId || typeof spenderId !== 'string') {
249
+ throw new TypeError('FederatedBudget.spawnChild: spenderId must be a non-empty string');
250
+ }
251
+ const available = this.remaining();
252
+ const effectiveCeiling = Math.min(childCeiling !== undefined ? childCeiling : available, available);
253
+ // Guarantee a valid (positive) ceiling even when the parent is exhausted.
254
+ const safeCeiling = effectiveCeiling > 0 ? effectiveCeiling : 1;
255
+ const child = new FederatedBudget({
256
+ ceiling: safeCeiling,
257
+ perAgentCeiling: this._perAgentCeiling,
258
+ });
259
+ const commit = () => {
260
+ const childSpent = child.getTotalSpent();
261
+ if (childSpent <= 0) {
262
+ return { allowed: true, remaining: this.remaining() };
263
+ }
264
+ return this.spend(spenderId, childSpent);
265
+ };
266
+ return { budget: child, commit };
267
+ }
268
+ /**
269
+ * Restore budget state from the blackboard backend.
270
+ *
271
+ * Reads the entry stored under `budgetKey`, deserializes the snapshot, and
272
+ * replaces the current in-memory state. Useful when a node restarts and
273
+ * needs to recover its prior accumulated spend.
274
+ *
275
+ * No-op (returns `false`) when no blackboard is configured or no entry exists.
276
+ *
277
+ * @returns `true` if state was successfully loaded, `false` otherwise.
278
+ */
279
+ loadFromBlackboard() {
280
+ if (!this._blackboard)
281
+ return false;
282
+ const entry = this._blackboard.read(this._budgetKey);
283
+ if (!entry)
284
+ return false;
285
+ try {
286
+ const snapshot = JSON.parse(entry.value);
287
+ this._spent.clear();
288
+ for (const [id, total] of Object.entries(snapshot.spent)) {
289
+ this._spent.set(id, total);
290
+ }
291
+ this._totalSpent = snapshot.totalSpent;
292
+ this._dynamicCeiling = snapshot.ceiling;
293
+ return true;
294
+ }
295
+ catch {
296
+ return false;
297
+ }
298
+ }
299
+ // --------------------------------------------------------------------------
300
+ // PRIVATE HELPERS
301
+ // --------------------------------------------------------------------------
302
+ /** Serialize current state to the blackboard, if one is configured. */
303
+ _persist() {
304
+ if (!this._blackboard)
305
+ return;
306
+ const snapshot = {
307
+ ceiling: this._dynamicCeiling,
308
+ perAgentCeiling: this._perAgentCeiling,
309
+ spent: this.getSpendLog(),
310
+ totalSpent: this._totalSpent,
311
+ };
312
+ try {
313
+ this._blackboard.write(this._budgetKey, JSON.stringify(snapshot), this._bbAgent);
314
+ }
315
+ catch (err) {
316
+ const msg = err instanceof Error ? err.message : String(err);
317
+ console.warn(`[FederatedBudget] Blackboard persist failed — in-memory state is ahead of disk: ${msg}`);
318
+ }
319
+ }
320
+ }
321
+ exports.FederatedBudget = FederatedBudget;
322
+ //# sourceMappingURL=federated-budget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"federated-budget.js","sourceRoot":"","sources":["../../../lib/federated-budget.ts"],"names":[],"mappings":"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"}