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.
- package/INTEGRATION_GUIDE.md +2 -2
- package/README.md +5 -3
- package/SKILL.md +3 -3
- package/dist/esm/adapters/a2a-adapter.js +235 -0
- package/dist/esm/adapters/a2a-adapter.js.map +1 -0
- package/dist/esm/adapters/adapter-registry.js +613 -0
- package/dist/esm/adapters/adapter-registry.js.map +1 -0
- package/dist/esm/adapters/agno-adapter.js +140 -0
- package/dist/esm/adapters/agno-adapter.js.map +1 -0
- package/dist/esm/adapters/anthropic-computer-use-adapter.js +180 -0
- package/dist/esm/adapters/anthropic-computer-use-adapter.js.map +1 -0
- package/dist/esm/adapters/aps-adapter.js +289 -0
- package/dist/esm/adapters/aps-adapter.js.map +1 -0
- package/dist/esm/adapters/autogen-adapter.js +141 -0
- package/dist/esm/adapters/autogen-adapter.js.map +1 -0
- package/dist/esm/adapters/base-adapter.js +104 -0
- package/dist/esm/adapters/base-adapter.js.map +1 -0
- package/dist/esm/adapters/browser-agent-adapter.js +219 -0
- package/dist/esm/adapters/browser-agent-adapter.js.map +1 -0
- package/dist/esm/adapters/codex-adapter.js +318 -0
- package/dist/esm/adapters/codex-adapter.js.map +1 -0
- package/dist/esm/adapters/copilot-adapter.js +132 -0
- package/dist/esm/adapters/copilot-adapter.js.map +1 -0
- package/dist/esm/adapters/crewai-adapter.js +148 -0
- package/dist/esm/adapters/crewai-adapter.js.map +1 -0
- package/dist/esm/adapters/custom-adapter.js +142 -0
- package/dist/esm/adapters/custom-adapter.js.map +1 -0
- package/dist/esm/adapters/custom-streaming-adapter.js +181 -0
- package/dist/esm/adapters/custom-streaming-adapter.js.map +1 -0
- package/dist/esm/adapters/dspy-adapter.js +127 -0
- package/dist/esm/adapters/dspy-adapter.js.map +1 -0
- package/dist/esm/adapters/haystack-adapter.js +149 -0
- package/dist/esm/adapters/haystack-adapter.js.map +1 -0
- package/dist/esm/adapters/hermes-adapter.js +217 -0
- package/dist/esm/adapters/hermes-adapter.js.map +1 -0
- package/dist/esm/adapters/index.js +109 -0
- package/dist/esm/adapters/index.js.map +1 -0
- package/dist/esm/adapters/langchain-adapter.js +134 -0
- package/dist/esm/adapters/langchain-adapter.js.map +1 -0
- package/dist/esm/adapters/langchain-streaming-adapter.js +161 -0
- package/dist/esm/adapters/langchain-streaming-adapter.js.map +1 -0
- package/dist/esm/adapters/langgraph-adapter.js +119 -0
- package/dist/esm/adapters/langgraph-adapter.js.map +1 -0
- package/dist/esm/adapters/llamaindex-adapter.js +135 -0
- package/dist/esm/adapters/llamaindex-adapter.js.map +1 -0
- package/dist/esm/adapters/mcp-adapter.js +200 -0
- package/dist/esm/adapters/mcp-adapter.js.map +1 -0
- package/dist/esm/adapters/minimax-adapter.js +233 -0
- package/dist/esm/adapters/minimax-adapter.js.map +1 -0
- package/dist/esm/adapters/nemoclaw-adapter.js +465 -0
- package/dist/esm/adapters/nemoclaw-adapter.js.map +1 -0
- package/dist/esm/adapters/openai-agents-adapter.js +118 -0
- package/dist/esm/adapters/openai-agents-adapter.js.map +1 -0
- package/dist/esm/adapters/openai-assistants-adapter.js +130 -0
- package/dist/esm/adapters/openai-assistants-adapter.js.map +1 -0
- package/dist/esm/adapters/openclaw-adapter.js +107 -0
- package/dist/esm/adapters/openclaw-adapter.js.map +1 -0
- package/dist/esm/adapters/orchestrator-adapter.js +218 -0
- package/dist/esm/adapters/orchestrator-adapter.js.map +1 -0
- package/dist/esm/adapters/pydantic-ai-adapter.js +163 -0
- package/dist/esm/adapters/pydantic-ai-adapter.js.map +1 -0
- package/dist/esm/adapters/rlm-adapter.js +167 -0
- package/dist/esm/adapters/rlm-adapter.js.map +1 -0
- package/dist/esm/adapters/semantic-kernel-adapter.js +123 -0
- package/dist/esm/adapters/semantic-kernel-adapter.js.map +1 -0
- package/dist/esm/adapters/streaming-base-adapter.js +74 -0
- package/dist/esm/adapters/streaming-base-adapter.js.map +1 -0
- package/dist/esm/adapters/vertex-ai-adapter.js +166 -0
- package/dist/esm/adapters/vertex-ai-adapter.js.map +1 -0
- package/dist/esm/demo-control-plane.js +147 -0
- package/dist/esm/demo-control-plane.js.map +1 -0
- package/dist/esm/demo-worktree-dashboard.js +131 -0
- package/dist/esm/demo-worktree-dashboard.js.map +1 -0
- package/dist/esm/examples/01-hello-swarm.js +165 -0
- package/dist/esm/examples/01-hello-swarm.js.map +1 -0
- package/dist/esm/examples/02-fsm-pipeline.js +189 -0
- package/dist/esm/examples/02-fsm-pipeline.js.map +1 -0
- package/dist/esm/examples/03-parallel-agents.js +192 -0
- package/dist/esm/examples/03-parallel-agents.js.map +1 -0
- package/dist/esm/examples/05-code-review-swarm.js +1177 -0
- package/dist/esm/examples/05-code-review-swarm.js.map +1 -0
- package/dist/esm/examples/06-ai-pipeline-demo.js +263 -0
- package/dist/esm/examples/06-ai-pipeline-demo.js.map +1 -0
- package/dist/esm/examples/07-full-showcase.js +946 -0
- package/dist/esm/examples/07-full-showcase.js.map +1 -0
- package/dist/esm/examples/08-control-plane-stress-demo.js +186 -0
- package/dist/esm/examples/08-control-plane-stress-demo.js.map +1 -0
- package/dist/esm/examples/09-real-langchain.js +231 -0
- package/dist/esm/examples/09-real-langchain.js.map +1 -0
- package/dist/esm/examples/10-nemoclaw-sandbox-swarm.js +270 -0
- package/dist/esm/examples/10-nemoclaw-sandbox-swarm.js.map +1 -0
- package/dist/esm/examples/demo-runner.js +119 -0
- package/dist/esm/examples/demo-runner.js.map +1 -0
- package/dist/esm/index.js +1352 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/lib/adapter-hooks.js +216 -0
- package/dist/esm/lib/adapter-hooks.js.map +1 -0
- package/dist/esm/lib/adapter-test-harness.js +118 -0
- package/dist/esm/lib/adapter-test-harness.js.map +1 -0
- package/dist/esm/lib/agent-conversation.js +155 -0
- package/dist/esm/lib/agent-conversation.js.map +1 -0
- package/dist/esm/lib/agent-debate.js +146 -0
- package/dist/esm/lib/agent-debate.js.map +1 -0
- package/dist/esm/lib/agent-memory.js +336 -0
- package/dist/esm/lib/agent-memory.js.map +1 -0
- package/dist/esm/lib/agent-runtime.js +818 -0
- package/dist/esm/lib/agent-runtime.js.map +1 -0
- package/dist/esm/lib/agent-vcr.js +218 -0
- package/dist/esm/lib/agent-vcr.js.map +1 -0
- package/dist/esm/lib/anomaly-detector.js +178 -0
- package/dist/esm/lib/anomaly-detector.js.map +1 -0
- package/dist/esm/lib/approval-inbox.js +385 -0
- package/dist/esm/lib/approval-inbox.js.map +1 -0
- package/dist/esm/lib/auth-guardian.js +692 -0
- package/dist/esm/lib/auth-guardian.js.map +1 -0
- package/dist/esm/lib/auth-validator.js +32 -0
- package/dist/esm/lib/auth-validator.js.map +1 -0
- package/dist/esm/lib/blackboard-backend-crdt.js +251 -0
- package/dist/esm/lib/blackboard-backend-crdt.js.map +1 -0
- package/dist/esm/lib/blackboard-backend-redis.js +244 -0
- package/dist/esm/lib/blackboard-backend-redis.js.map +1 -0
- package/dist/esm/lib/blackboard-backend.js +141 -0
- package/dist/esm/lib/blackboard-backend.js.map +1 -0
- package/dist/esm/lib/blackboard-validator.js +985 -0
- package/dist/esm/lib/blackboard-validator.js.map +1 -0
- package/dist/esm/lib/circuit-breaker.js +164 -0
- package/dist/esm/lib/circuit-breaker.js.map +1 -0
- package/dist/esm/lib/claim-verifier.js +173 -0
- package/dist/esm/lib/claim-verifier.js.map +1 -0
- package/dist/esm/lib/comparison-runner.js +138 -0
- package/dist/esm/lib/comparison-runner.js.map +1 -0
- package/dist/esm/lib/compliance-monitor.js +261 -0
- package/dist/esm/lib/compliance-monitor.js.map +1 -0
- package/dist/esm/lib/confidence-filter.js +210 -0
- package/dist/esm/lib/confidence-filter.js.map +1 -0
- package/dist/esm/lib/config-watcher.js +215 -0
- package/dist/esm/lib/config-watcher.js.map +1 -0
- package/dist/esm/lib/consistency.js +274 -0
- package/dist/esm/lib/consistency.js.map +1 -0
- package/dist/esm/lib/console-ui.js +276 -0
- package/dist/esm/lib/console-ui.js.map +1 -0
- package/dist/esm/lib/context-throttler.js +171 -0
- package/dist/esm/lib/context-throttler.js.map +1 -0
- package/dist/esm/lib/control-plane.js +527 -0
- package/dist/esm/lib/control-plane.js.map +1 -0
- package/dist/esm/lib/cost-governor.js +128 -0
- package/dist/esm/lib/cost-governor.js.map +1 -0
- package/dist/esm/lib/cost-heatmap.js +161 -0
- package/dist/esm/lib/cost-heatmap.js.map +1 -0
- package/dist/esm/lib/coverage-gate.js +213 -0
- package/dist/esm/lib/coverage-gate.js.map +1 -0
- package/dist/esm/lib/coverage-reporter.js +177 -0
- package/dist/esm/lib/coverage-reporter.js.map +1 -0
- package/dist/esm/lib/crdt.js +141 -0
- package/dist/esm/lib/crdt.js.map +1 -0
- package/dist/esm/lib/dashboard-server.js +403 -0
- package/dist/esm/lib/dashboard-server.js.map +1 -0
- package/dist/esm/lib/dry-run.js +130 -0
- package/dist/esm/lib/dry-run.js.map +1 -0
- package/dist/esm/lib/env-manager.js +518 -0
- package/dist/esm/lib/env-manager.js.map +1 -0
- package/dist/esm/lib/errors.js +201 -0
- package/dist/esm/lib/errors.js.map +1 -0
- package/dist/esm/lib/event-bus.js +229 -0
- package/dist/esm/lib/event-bus.js.map +1 -0
- package/dist/esm/lib/explainability.js +102 -0
- package/dist/esm/lib/explainability.js.map +1 -0
- package/dist/esm/lib/fan-out.js +237 -0
- package/dist/esm/lib/fan-out.js.map +1 -0
- package/dist/esm/lib/federated-budget.js +322 -0
- package/dist/esm/lib/federated-budget.js.map +1 -0
- package/dist/esm/lib/fsm-journey.js +478 -0
- package/dist/esm/lib/fsm-journey.js.map +1 -0
- package/dist/esm/lib/goal-decomposer.js +698 -0
- package/dist/esm/lib/goal-decomposer.js.map +1 -0
- package/dist/esm/lib/goal-dsl.js +391 -0
- package/dist/esm/lib/goal-dsl.js.map +1 -0
- package/dist/esm/lib/job-queue.js +310 -0
- package/dist/esm/lib/job-queue.js.map +1 -0
- package/dist/esm/lib/landscape-agent.js +134 -0
- package/dist/esm/lib/landscape-agent.js.map +1 -0
- package/dist/esm/lib/learning-loop.js +181 -0
- package/dist/esm/lib/learning-loop.js.map +1 -0
- package/dist/esm/lib/lifecycle-hooks.js +148 -0
- package/dist/esm/lib/lifecycle-hooks.js.map +1 -0
- package/dist/esm/lib/locked-blackboard.js +1295 -0
- package/dist/esm/lib/locked-blackboard.js.map +1 -0
- package/dist/esm/lib/logger.js +150 -0
- package/dist/esm/lib/logger.js.map +1 -0
- package/dist/esm/lib/mcp-blackboard-tools.js +298 -0
- package/dist/esm/lib/mcp-blackboard-tools.js.map +1 -0
- package/dist/esm/lib/mcp-bridge.js +357 -0
- package/dist/esm/lib/mcp-bridge.js.map +1 -0
- package/dist/esm/lib/mcp-tool-consumer.js +287 -0
- package/dist/esm/lib/mcp-tool-consumer.js.map +1 -0
- package/dist/esm/lib/mcp-tools-control.js +392 -0
- package/dist/esm/lib/mcp-tools-control.js.map +1 -0
- package/dist/esm/lib/mcp-tools-extended.js +371 -0
- package/dist/esm/lib/mcp-tools-extended.js.map +1 -0
- package/dist/esm/lib/mcp-transport-http.js +528 -0
- package/dist/esm/lib/mcp-transport-http.js.map +1 -0
- package/dist/esm/lib/mcp-transport-sse.js +503 -0
- package/dist/esm/lib/mcp-transport-sse.js.map +1 -0
- package/dist/esm/lib/metrics.js +284 -0
- package/dist/esm/lib/metrics.js.map +1 -0
- package/dist/esm/lib/orchestrator-types.js +66 -0
- package/dist/esm/lib/orchestrator-types.js.map +1 -0
- package/dist/esm/lib/otel-bridge.js +167 -0
- package/dist/esm/lib/otel-bridge.js.map +1 -0
- package/dist/esm/lib/partition-planner.js +246 -0
- package/dist/esm/lib/partition-planner.js.map +1 -0
- package/dist/esm/lib/phase-pipeline.js +367 -0
- package/dist/esm/lib/phase-pipeline.js.map +1 -0
- package/dist/esm/lib/playground.js +224 -0
- package/dist/esm/lib/playground.js.map +1 -0
- package/dist/esm/lib/qa-orchestrator.js +296 -0
- package/dist/esm/lib/qa-orchestrator.js.map +1 -0
- package/dist/esm/lib/quadtree.js +259 -0
- package/dist/esm/lib/quadtree.js.map +1 -0
- package/dist/esm/lib/route-classifier.js +217 -0
- package/dist/esm/lib/route-classifier.js.map +1 -0
- package/dist/esm/lib/semantic-search.js +235 -0
- package/dist/esm/lib/semantic-search.js.map +1 -0
- package/dist/esm/lib/shared-blackboard.js +249 -0
- package/dist/esm/lib/shared-blackboard.js.map +1 -0
- package/dist/esm/lib/skill-composer.js +190 -0
- package/dist/esm/lib/skill-composer.js.map +1 -0
- package/dist/esm/lib/speculative-executor.js +107 -0
- package/dist/esm/lib/speculative-executor.js.map +1 -0
- package/dist/esm/lib/strategy-agent.js +626 -0
- package/dist/esm/lib/strategy-agent.js.map +1 -0
- package/dist/esm/lib/swarm-transport.js +307 -0
- package/dist/esm/lib/swarm-transport.js.map +1 -0
- package/dist/esm/lib/swarm-utils.js +510 -0
- package/dist/esm/lib/swarm-utils.js.map +1 -0
- package/dist/esm/lib/task-decomposer.js +272 -0
- package/dist/esm/lib/task-decomposer.js.map +1 -0
- package/dist/esm/lib/telemetry-provider.js +207 -0
- package/dist/esm/lib/telemetry-provider.js.map +1 -0
- package/dist/esm/lib/timeline-scrubber.js +173 -0
- package/dist/esm/lib/timeline-scrubber.js.map +1 -0
- package/dist/esm/lib/topology.js +591 -0
- package/dist/esm/lib/topology.js.map +1 -0
- package/dist/esm/lib/transport-agent.js +366 -0
- package/dist/esm/lib/transport-agent.js.map +1 -0
- package/dist/esm/lib/work-tree-dashboard.js +583 -0
- package/dist/esm/lib/work-tree-dashboard.js.map +1 -0
- package/dist/esm/lib/work-tree-ui.js +333 -0
- package/dist/esm/lib/work-tree-ui.js.map +1 -0
- package/dist/esm/lib/work-tree.js +480 -0
- package/dist/esm/lib/work-tree.js.map +1 -0
- package/dist/esm/run.js +144 -0
- package/dist/esm/run.js.map +1 -0
- package/dist/esm/security.js +1122 -0
- package/dist/esm/security.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/mcp-transport-http.d.ts +203 -0
- package/dist/lib/mcp-transport-http.d.ts.map +1 -0
- package/dist/lib/mcp-transport-http.js +528 -0
- package/dist/lib/mcp-transport-http.js.map +1 -0
- package/dist/lib/phase-pipeline.d.ts +31 -0
- package/dist/lib/phase-pipeline.d.ts.map +1 -1
- package/dist/lib/phase-pipeline.js +93 -1
- package/dist/lib/phase-pipeline.js.map +1 -1
- package/dist/lib/semantic-search.d.ts +42 -6
- package/dist/lib/semantic-search.d.ts.map +1 -1
- package/dist/lib/semantic-search.js +87 -6
- package/dist/lib/semantic-search.js.map +1 -1
- 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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"}
|