network-ai 3.7.0 → 3.8.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/README.md +2 -2
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -4
- package/dist/index.js.map +1 -1
- package/dist/lib/consistency.d.ts +194 -0
- package/dist/lib/consistency.d.ts.map +1 -0
- package/dist/lib/consistency.js +274 -0
- package/dist/lib/consistency.js.map +1 -0
- package/dist/lib/federated-budget.d.ts +205 -0
- package/dist/lib/federated-budget.d.ts.map +1 -0
- package/dist/lib/federated-budget.js +275 -0
- package/dist/lib/federated-budget.js.map +1 -0
- package/dist/run-tests.d.ts +9 -0
- package/dist/run-tests.d.ts.map +1 -0
- package/dist/run-tests.js +76 -0
- package/dist/run-tests.js.map +1 -0
- package/package.json +4 -2
|
@@ -0,0 +1,275 @@
|
|
|
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
|
+
* Restore budget state from the blackboard backend.
|
|
224
|
+
*
|
|
225
|
+
* Reads the entry stored under `budgetKey`, deserializes the snapshot, and
|
|
226
|
+
* replaces the current in-memory state. Useful when a node restarts and
|
|
227
|
+
* needs to recover its prior accumulated spend.
|
|
228
|
+
*
|
|
229
|
+
* No-op (returns `false`) when no blackboard is configured or no entry exists.
|
|
230
|
+
*
|
|
231
|
+
* @returns `true` if state was successfully loaded, `false` otherwise.
|
|
232
|
+
*/
|
|
233
|
+
loadFromBlackboard() {
|
|
234
|
+
if (!this._blackboard)
|
|
235
|
+
return false;
|
|
236
|
+
const entry = this._blackboard.read(this._budgetKey);
|
|
237
|
+
if (!entry)
|
|
238
|
+
return false;
|
|
239
|
+
try {
|
|
240
|
+
const snapshot = JSON.parse(entry.value);
|
|
241
|
+
this._spent.clear();
|
|
242
|
+
for (const [id, total] of Object.entries(snapshot.spent)) {
|
|
243
|
+
this._spent.set(id, total);
|
|
244
|
+
}
|
|
245
|
+
this._totalSpent = snapshot.totalSpent;
|
|
246
|
+
this._dynamicCeiling = snapshot.ceiling;
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
catch {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
// --------------------------------------------------------------------------
|
|
254
|
+
// PRIVATE HELPERS
|
|
255
|
+
// --------------------------------------------------------------------------
|
|
256
|
+
/** Serialize current state to the blackboard, if one is configured. */
|
|
257
|
+
_persist() {
|
|
258
|
+
if (!this._blackboard)
|
|
259
|
+
return;
|
|
260
|
+
const snapshot = {
|
|
261
|
+
ceiling: this._dynamicCeiling,
|
|
262
|
+
perAgentCeiling: this._perAgentCeiling,
|
|
263
|
+
spent: this.getSpendLog(),
|
|
264
|
+
totalSpent: this._totalSpent,
|
|
265
|
+
};
|
|
266
|
+
try {
|
|
267
|
+
this._blackboard.write(this._budgetKey, JSON.stringify(snapshot), this._bbAgent);
|
|
268
|
+
}
|
|
269
|
+
catch {
|
|
270
|
+
// Swallow persistence errors — the in-memory state remains authoritative.
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
exports.FederatedBudget = FederatedBudget;
|
|
275
|
+
//# 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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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test runner — executes all test suites sequentially as isolated child
|
|
3
|
+
* processes. Each suite is fully cleaned up before the next one starts,
|
|
4
|
+
* preventing memory accumulation that crashes VS Code's integrated terminal.
|
|
5
|
+
*
|
|
6
|
+
* Usage: npx ts-node run-tests.ts
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=run-tests.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-tests.d.ts","sourceRoot":"","sources":["../run-tests.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Test runner — executes all test suites sequentially as isolated child
|
|
4
|
+
* processes. Each suite is fully cleaned up before the next one starts,
|
|
5
|
+
* preventing memory accumulation that crashes VS Code's integrated terminal.
|
|
6
|
+
*
|
|
7
|
+
* Usage: npx ts-node run-tests.ts
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
const child_process_1 = require("child_process");
|
|
11
|
+
const path_1 = require("path");
|
|
12
|
+
const SUITES = [
|
|
13
|
+
'test-standalone.ts',
|
|
14
|
+
'test-security.ts',
|
|
15
|
+
'test-adapters.ts',
|
|
16
|
+
'test-priority.ts',
|
|
17
|
+
'test-phase4.ts',
|
|
18
|
+
'test-phase5.ts',
|
|
19
|
+
'test-phase5b.ts',
|
|
20
|
+
'test-phase5c.ts',
|
|
21
|
+
'test-phase5d.ts',
|
|
22
|
+
'test-phase5e.ts',
|
|
23
|
+
'test-phase5f.ts',
|
|
24
|
+
];
|
|
25
|
+
const WIDTH = 60;
|
|
26
|
+
const cwd = (0, path_1.resolve)(__dirname);
|
|
27
|
+
let totalPassed = 0;
|
|
28
|
+
let totalFailed = 0;
|
|
29
|
+
const results = [];
|
|
30
|
+
console.log('='.repeat(WIDTH));
|
|
31
|
+
console.log(' Network-AI Full Test Suite');
|
|
32
|
+
console.log('='.repeat(WIDTH));
|
|
33
|
+
for (const suite of SUITES) {
|
|
34
|
+
process.stdout.write(`\nRunning ${suite} ... `);
|
|
35
|
+
const result = (0, child_process_1.spawnSync)(process.execPath, // node
|
|
36
|
+
['-e', `require('ts-node/register'); require('./${suite}')`], {
|
|
37
|
+
cwd,
|
|
38
|
+
encoding: 'utf8',
|
|
39
|
+
maxBuffer: 10 * 1024 * 1024, // 10 MB output buffer
|
|
40
|
+
env: {
|
|
41
|
+
...process.env,
|
|
42
|
+
NODE_OPTIONS: '--max-old-space-size=512', // cap per-process RAM
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
// Count pass/fail markers — suites use either [PASS]/[FAIL] or [v]/[x]
|
|
46
|
+
const stdout = result.stdout ?? '';
|
|
47
|
+
const stderr = result.stderr ?? '';
|
|
48
|
+
const passed = (stdout.match(/\[PASS\]|\[v\]/g) ?? []).length;
|
|
49
|
+
const failed = (stdout.match(/\[FAIL\]|\[x\]/g) ?? []).length;
|
|
50
|
+
const ok = result.status === 0 && failed === 0;
|
|
51
|
+
totalPassed += passed;
|
|
52
|
+
totalFailed += failed;
|
|
53
|
+
results.push({ suite, passed, failed, ok });
|
|
54
|
+
if (ok) {
|
|
55
|
+
console.log(`OK (${passed} passed)`);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
console.log(`FAIL (${passed} passed, ${failed} failed)`);
|
|
59
|
+
// Print the last 20 lines of output for quick diagnosis
|
|
60
|
+
const lines = (stdout + stderr).split('\n').filter(Boolean);
|
|
61
|
+
lines.slice(-20).forEach(l => console.log(' ' + l));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
console.log('\n' + '='.repeat(WIDTH));
|
|
65
|
+
console.log(` Results: ${totalPassed} passed, ${totalFailed} failed`);
|
|
66
|
+
console.log('='.repeat(WIDTH));
|
|
67
|
+
const allOk = results.every(r => r.ok);
|
|
68
|
+
if (allOk) {
|
|
69
|
+
console.log(`\nALL ${totalPassed} TESTS PASSED\n`);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
console.log('\nFAILED SUITES:');
|
|
73
|
+
results.filter(r => !r.ok).forEach(r => console.log(` - ${r.suite} (${r.failed} failed)`));
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=run-tests.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-tests.js","sourceRoot":"","sources":["../run-tests.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "network-ai",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.8.0",
|
|
4
4
|
"description": "AI agent orchestration framework for TypeScript/Node.js - plug-and-play multi-agent coordination with 12 frameworks (LangChain, AutoGen, CrewAI, OpenAI Assistants, LlamaIndex, Semantic Kernel, Haystack, DSPy, Agno, MCP, OpenClaw). Built-in security, swarm intelligence, and agentic workflow patterns.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -18,7 +18,9 @@
|
|
|
18
18
|
"test:phase5b": "npx ts-node test-phase5b.ts",
|
|
19
19
|
"test:phase5c": "npx ts-node test-phase5c.ts",
|
|
20
20
|
"test:phase5d": "npx ts-node test-phase5d.ts",
|
|
21
|
-
"test:
|
|
21
|
+
"test:phase5e": "npx ts-node test-phase5e.ts",
|
|
22
|
+
"test:phase5f": "npx ts-node test-phase5f.ts",
|
|
23
|
+
"test:all": "npx ts-node run-tests.ts",
|
|
22
24
|
"setup": "npx ts-node setup.ts",
|
|
23
25
|
"setup:check": "npx ts-node setup.ts --check",
|
|
24
26
|
"setup:example": "npx ts-node setup.ts --example",
|