network-ai 4.15.2 → 5.0.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 +11 -4
- package/QUICKSTART.md +31 -4
- package/README.md +37 -15
- package/bin/dashboard.ts +146 -0
- package/bin/mcp-server.ts +3 -2
- package/dist/adapters/adapter-registry.d.ts +33 -1
- package/dist/adapters/adapter-registry.d.ts.map +1 -1
- package/dist/adapters/adapter-registry.js +49 -0
- package/dist/adapters/adapter-registry.js.map +1 -1
- package/dist/adapters/anthropic-computer-use-adapter.d.ts +132 -0
- package/dist/adapters/anthropic-computer-use-adapter.d.ts.map +1 -0
- package/dist/adapters/anthropic-computer-use-adapter.js +180 -0
- package/dist/adapters/anthropic-computer-use-adapter.js.map +1 -0
- package/dist/adapters/browser-agent-adapter.d.ts +121 -0
- package/dist/adapters/browser-agent-adapter.d.ts.map +1 -0
- package/dist/adapters/browser-agent-adapter.js +219 -0
- package/dist/adapters/browser-agent-adapter.js.map +1 -0
- package/dist/adapters/copilot-adapter.d.ts +59 -0
- package/dist/adapters/copilot-adapter.d.ts.map +1 -0
- package/dist/adapters/copilot-adapter.js +132 -0
- package/dist/adapters/copilot-adapter.js.map +1 -0
- package/dist/adapters/index.d.ts +15 -1
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +22 -1
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/langgraph-adapter.d.ts +70 -0
- package/dist/adapters/langgraph-adapter.d.ts.map +1 -0
- package/dist/adapters/langgraph-adapter.js +119 -0
- package/dist/adapters/langgraph-adapter.js.map +1 -0
- package/dist/adapters/openai-agents-adapter.d.ts +100 -0
- package/dist/adapters/openai-agents-adapter.d.ts.map +1 -0
- package/dist/adapters/openai-agents-adapter.js +118 -0
- package/dist/adapters/openai-agents-adapter.js.map +1 -0
- package/dist/adapters/pydantic-ai-adapter.d.ts +104 -0
- package/dist/adapters/pydantic-ai-adapter.d.ts.map +1 -0
- package/dist/adapters/pydantic-ai-adapter.js +163 -0
- package/dist/adapters/pydantic-ai-adapter.js.map +1 -0
- package/dist/adapters/vertex-ai-adapter.d.ts +122 -0
- package/dist/adapters/vertex-ai-adapter.d.ts.map +1 -0
- package/dist/adapters/vertex-ai-adapter.js +166 -0
- package/dist/adapters/vertex-ai-adapter.js.map +1 -0
- package/dist/bin/dashboard.d.ts +11 -0
- package/dist/bin/dashboard.d.ts.map +1 -0
- package/dist/bin/dashboard.js +135 -0
- package/dist/bin/dashboard.js.map +1 -0
- package/dist/bin/mcp-server.js +3 -2
- package/dist/bin/mcp-server.js.map +1 -1
- package/dist/index.d.ts +103 -559
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +295 -1074
- package/dist/index.js.map +1 -1
- package/dist/lib/adapter-test-harness.d.ts +88 -0
- package/dist/lib/adapter-test-harness.d.ts.map +1 -0
- package/dist/lib/adapter-test-harness.js +118 -0
- package/dist/lib/adapter-test-harness.js.map +1 -0
- package/dist/lib/agent-conversation.d.ts +115 -0
- package/dist/lib/agent-conversation.d.ts.map +1 -0
- package/dist/lib/agent-conversation.js +155 -0
- package/dist/lib/agent-conversation.js.map +1 -0
- package/dist/lib/agent-debate.d.ts +115 -0
- package/dist/lib/agent-debate.d.ts.map +1 -0
- package/dist/lib/agent-debate.js +146 -0
- package/dist/lib/agent-debate.js.map +1 -0
- package/dist/lib/agent-memory.d.ts +157 -0
- package/dist/lib/agent-memory.d.ts.map +1 -0
- package/dist/lib/agent-memory.js +336 -0
- package/dist/lib/agent-memory.js.map +1 -0
- package/dist/lib/agent-vcr.d.ts +133 -0
- package/dist/lib/agent-vcr.d.ts.map +1 -0
- package/dist/lib/agent-vcr.js +218 -0
- package/dist/lib/agent-vcr.js.map +1 -0
- package/dist/lib/anomaly-detector.d.ts +112 -0
- package/dist/lib/anomaly-detector.d.ts.map +1 -0
- package/dist/lib/anomaly-detector.js +178 -0
- package/dist/lib/anomaly-detector.js.map +1 -0
- package/dist/lib/approval-inbox.d.ts +147 -0
- package/dist/lib/approval-inbox.d.ts.map +1 -0
- package/dist/lib/approval-inbox.js +385 -0
- package/dist/lib/approval-inbox.js.map +1 -0
- package/dist/lib/auth-guardian.d.ts +170 -0
- package/dist/lib/auth-guardian.d.ts.map +1 -0
- package/dist/lib/auth-guardian.js +604 -0
- package/dist/lib/auth-guardian.js.map +1 -0
- package/dist/lib/auth-validator.d.ts +70 -0
- package/dist/lib/auth-validator.d.ts.map +1 -0
- package/dist/lib/auth-validator.js +32 -0
- package/dist/lib/auth-validator.js.map +1 -0
- package/dist/lib/blackboard-validator.d.ts +56 -0
- package/dist/lib/blackboard-validator.d.ts.map +1 -1
- package/dist/lib/blackboard-validator.js +181 -4
- package/dist/lib/blackboard-validator.js.map +1 -1
- package/dist/lib/comparison-runner.d.ts +99 -0
- package/dist/lib/comparison-runner.d.ts.map +1 -0
- package/dist/lib/comparison-runner.js +138 -0
- package/dist/lib/comparison-runner.js.map +1 -0
- package/dist/lib/config-watcher.d.ts +109 -0
- package/dist/lib/config-watcher.d.ts.map +1 -0
- package/dist/lib/config-watcher.js +215 -0
- package/dist/lib/config-watcher.js.map +1 -0
- package/dist/lib/cost-governor.d.ts +105 -0
- package/dist/lib/cost-governor.d.ts.map +1 -0
- package/dist/lib/cost-governor.js +128 -0
- package/dist/lib/cost-governor.js.map +1 -0
- package/dist/lib/cost-heatmap.d.ts +104 -0
- package/dist/lib/cost-heatmap.d.ts.map +1 -0
- package/dist/lib/cost-heatmap.js +161 -0
- package/dist/lib/cost-heatmap.js.map +1 -0
- package/dist/lib/coverage-reporter.d.ts +92 -0
- package/dist/lib/coverage-reporter.d.ts.map +1 -0
- package/dist/lib/coverage-reporter.js +177 -0
- package/dist/lib/coverage-reporter.js.map +1 -0
- package/dist/lib/dashboard-server.d.ts +71 -0
- package/dist/lib/dashboard-server.d.ts.map +1 -0
- package/dist/lib/dashboard-server.js +403 -0
- package/dist/lib/dashboard-server.js.map +1 -0
- package/dist/lib/dry-run.d.ts +73 -0
- package/dist/lib/dry-run.d.ts.map +1 -0
- package/dist/lib/dry-run.js +130 -0
- package/dist/lib/dry-run.js.map +1 -0
- package/dist/lib/errors.d.ts +15 -0
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +38 -0
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/event-bus.d.ts +167 -0
- package/dist/lib/event-bus.d.ts.map +1 -0
- package/dist/lib/event-bus.js +229 -0
- package/dist/lib/event-bus.js.map +1 -0
- package/dist/lib/explainability.d.ts +85 -0
- package/dist/lib/explainability.d.ts.map +1 -0
- package/dist/lib/explainability.js +102 -0
- package/dist/lib/explainability.js.map +1 -0
- package/dist/lib/goal-dsl.d.ts +157 -0
- package/dist/lib/goal-dsl.d.ts.map +1 -0
- package/dist/lib/goal-dsl.js +392 -0
- package/dist/lib/goal-dsl.js.map +1 -0
- package/dist/lib/job-queue.d.ts +183 -0
- package/dist/lib/job-queue.d.ts.map +1 -0
- package/dist/lib/job-queue.js +310 -0
- package/dist/lib/job-queue.js.map +1 -0
- package/dist/lib/learning-loop.d.ts +113 -0
- package/dist/lib/learning-loop.d.ts.map +1 -0
- package/dist/lib/learning-loop.js +181 -0
- package/dist/lib/learning-loop.js.map +1 -0
- package/dist/lib/lifecycle-hooks.d.ts +116 -0
- package/dist/lib/lifecycle-hooks.d.ts.map +1 -0
- package/dist/lib/lifecycle-hooks.js +148 -0
- package/dist/lib/lifecycle-hooks.js.map +1 -0
- package/dist/lib/locked-blackboard.d.ts.map +1 -1
- package/dist/lib/locked-blackboard.js +9 -5
- package/dist/lib/locked-blackboard.js.map +1 -1
- package/dist/lib/mcp-tool-consumer.d.ts +153 -0
- package/dist/lib/mcp-tool-consumer.d.ts.map +1 -0
- package/dist/lib/mcp-tool-consumer.js +320 -0
- package/dist/lib/mcp-tool-consumer.js.map +1 -0
- package/dist/lib/metrics.d.ts +119 -0
- package/dist/lib/metrics.d.ts.map +1 -0
- package/dist/lib/metrics.js +284 -0
- package/dist/lib/metrics.js.map +1 -0
- package/dist/lib/orchestrator-types.d.ts +309 -0
- package/dist/lib/orchestrator-types.d.ts.map +1 -0
- package/dist/lib/orchestrator-types.js +61 -0
- package/dist/lib/orchestrator-types.js.map +1 -0
- package/dist/lib/otel-bridge.d.ts +74 -0
- package/dist/lib/otel-bridge.d.ts.map +1 -0
- package/dist/lib/otel-bridge.js +167 -0
- package/dist/lib/otel-bridge.js.map +1 -0
- package/dist/lib/playground.d.ts +76 -0
- package/dist/lib/playground.d.ts.map +1 -0
- package/dist/lib/playground.js +224 -0
- package/dist/lib/playground.js.map +1 -0
- package/dist/lib/quadtree.d.ts +114 -0
- package/dist/lib/quadtree.d.ts.map +1 -0
- package/dist/lib/quadtree.js +259 -0
- package/dist/lib/quadtree.js.map +1 -0
- package/dist/lib/shared-blackboard.d.ts +101 -0
- package/dist/lib/shared-blackboard.d.ts.map +1 -0
- package/dist/lib/shared-blackboard.js +249 -0
- package/dist/lib/shared-blackboard.js.map +1 -0
- package/dist/lib/speculative-executor.d.ts +89 -0
- package/dist/lib/speculative-executor.d.ts.map +1 -0
- package/dist/lib/speculative-executor.js +107 -0
- package/dist/lib/speculative-executor.js.map +1 -0
- package/dist/lib/swarm-transport.d.ts +150 -0
- package/dist/lib/swarm-transport.d.ts.map +1 -0
- package/dist/lib/swarm-transport.js +307 -0
- package/dist/lib/swarm-transport.js.map +1 -0
- package/dist/lib/task-decomposer.d.ts +41 -0
- package/dist/lib/task-decomposer.d.ts.map +1 -0
- package/dist/lib/task-decomposer.js +272 -0
- package/dist/lib/task-decomposer.js.map +1 -0
- package/dist/lib/timeline-scrubber.d.ts +84 -0
- package/dist/lib/timeline-scrubber.d.ts.map +1 -0
- package/dist/lib/timeline-scrubber.js +173 -0
- package/dist/lib/timeline-scrubber.js.map +1 -0
- package/dist/lib/topology.d.ts +361 -0
- package/dist/lib/topology.d.ts.map +1 -0
- package/dist/lib/topology.js +591 -0
- package/dist/lib/topology.js.map +1 -0
- package/dist/security.d.ts +95 -0
- package/dist/security.d.ts.map +1 -1
- package/dist/security.js +267 -5
- package/dist/security.js.map +1 -1
- package/package.json +7 -5
- package/types/agent-adapter.d.ts +5 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* VCR — Record and replay LLM/agent interactions for testing
|
|
4
|
+
*
|
|
5
|
+
* Provides a VCR (Video Cassette Recorder) pattern for capturing
|
|
6
|
+
* agent execution calls and replaying them deterministically in tests.
|
|
7
|
+
*
|
|
8
|
+
* Modes:
|
|
9
|
+
* - `record` — Intercepts calls, forwards to real handler, saves cassette
|
|
10
|
+
* - `replay` — Matches calls to recorded cassettes, returns saved results
|
|
11
|
+
* - `passthrough` — Disabled, all calls go to real handler
|
|
12
|
+
*
|
|
13
|
+
* Features:
|
|
14
|
+
* - Cassettes stored as JSON files
|
|
15
|
+
* - Request matching by agent ID + action + params hash
|
|
16
|
+
* - Configurable matching strictness
|
|
17
|
+
* - Call ordering enforcement (optional)
|
|
18
|
+
* - Missing cassette detection with helpful errors
|
|
19
|
+
*
|
|
20
|
+
* Usage:
|
|
21
|
+
* const vcr = new AgentVCR({ mode: 'record', cassettePath: './fixtures' });
|
|
22
|
+
* const result = await vcr.execute('agent-1', payload, realHandler);
|
|
23
|
+
* await vcr.save('my-test'); // Saves cassette to ./fixtures/my-test.json
|
|
24
|
+
*
|
|
25
|
+
* // Later in tests:
|
|
26
|
+
* const vcr2 = new AgentVCR({ mode: 'replay', cassettePath: './fixtures' });
|
|
27
|
+
* await vcr2.load('my-test');
|
|
28
|
+
* const result2 = await vcr2.execute('agent-1', payload); // Returns recorded result
|
|
29
|
+
*
|
|
30
|
+
* @module AgentVCR
|
|
31
|
+
* @version 1.0.0
|
|
32
|
+
*/
|
|
33
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
34
|
+
exports.AgentVCR = void 0;
|
|
35
|
+
const promises_1 = require("fs/promises");
|
|
36
|
+
const path_1 = require("path");
|
|
37
|
+
const crypto_1 = require("crypto");
|
|
38
|
+
// ============================================================================
|
|
39
|
+
// VCR
|
|
40
|
+
// ============================================================================
|
|
41
|
+
/**
|
|
42
|
+
* Agent VCR — Record and replay agent execution calls.
|
|
43
|
+
*
|
|
44
|
+
* In `record` mode, calls are forwarded to the real handler and saved.
|
|
45
|
+
* In `replay` mode, calls are matched to saved cassettes.
|
|
46
|
+
*/
|
|
47
|
+
class AgentVCR {
|
|
48
|
+
cassette = null;
|
|
49
|
+
replayIndex = 0;
|
|
50
|
+
config;
|
|
51
|
+
constructor(config) {
|
|
52
|
+
this.config = {
|
|
53
|
+
mode: config.mode,
|
|
54
|
+
cassettePath: config.cassettePath,
|
|
55
|
+
matchMode: config.matchMode ?? 'exact',
|
|
56
|
+
ordered: config.ordered ?? false,
|
|
57
|
+
throwOnMissing: config.throwOnMissing ?? true,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/** Get current mode */
|
|
61
|
+
get mode() {
|
|
62
|
+
return this.config.mode;
|
|
63
|
+
}
|
|
64
|
+
/** Set mode at runtime */
|
|
65
|
+
setMode(mode) {
|
|
66
|
+
this.config.mode = mode;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Execute an agent call through the VCR.
|
|
70
|
+
*
|
|
71
|
+
* @param agentId - The agent to call
|
|
72
|
+
* @param payload - The request payload
|
|
73
|
+
* @param handler - Real handler (required in record/passthrough mode)
|
|
74
|
+
*/
|
|
75
|
+
async execute(agentId, payload, handler) {
|
|
76
|
+
switch (this.config.mode) {
|
|
77
|
+
case 'passthrough':
|
|
78
|
+
if (!handler)
|
|
79
|
+
throw new Error('VCR passthrough mode requires a handler');
|
|
80
|
+
return handler(agentId, payload);
|
|
81
|
+
case 'record':
|
|
82
|
+
return this.recordExecution(agentId, payload, handler);
|
|
83
|
+
case 'replay':
|
|
84
|
+
return this.replayExecution(agentId, payload);
|
|
85
|
+
default:
|
|
86
|
+
throw new Error(`Unknown VCR mode: ${this.config.mode}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Load a cassette from disk.
|
|
91
|
+
*/
|
|
92
|
+
async load(name) {
|
|
93
|
+
const filePath = (0, path_1.join)(this.config.cassettePath, `${name}.json`);
|
|
94
|
+
const raw = await (0, promises_1.readFile)(filePath, 'utf-8');
|
|
95
|
+
this.cassette = JSON.parse(raw);
|
|
96
|
+
this.replayIndex = 0;
|
|
97
|
+
return this.cassette;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Save the current recording as a named cassette.
|
|
101
|
+
*/
|
|
102
|
+
async save(name) {
|
|
103
|
+
if (!this.cassette) {
|
|
104
|
+
this.cassette = { name, createdAt: Date.now(), interactions: [] };
|
|
105
|
+
}
|
|
106
|
+
this.cassette.name = name;
|
|
107
|
+
try {
|
|
108
|
+
await (0, promises_1.stat)(this.config.cassettePath);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
await (0, promises_1.mkdir)(this.config.cassettePath, { recursive: true });
|
|
112
|
+
}
|
|
113
|
+
const filePath = (0, path_1.join)(this.config.cassettePath, `${name}.json`);
|
|
114
|
+
await (0, promises_1.writeFile)(filePath, JSON.stringify(this.cassette, null, 2), 'utf-8');
|
|
115
|
+
}
|
|
116
|
+
/** Get the current cassette */
|
|
117
|
+
getCassette() {
|
|
118
|
+
return this.cassette;
|
|
119
|
+
}
|
|
120
|
+
/** Reset the VCR (clear cassette and replay index) */
|
|
121
|
+
reset() {
|
|
122
|
+
this.cassette = null;
|
|
123
|
+
this.replayIndex = 0;
|
|
124
|
+
}
|
|
125
|
+
/** Get number of recorded interactions */
|
|
126
|
+
get interactionCount() {
|
|
127
|
+
return this.cassette?.interactions.length ?? 0;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Try to match a request to a recorded interaction.
|
|
131
|
+
* Useful for debugging match failures.
|
|
132
|
+
*/
|
|
133
|
+
findMatch(agentId, payload) {
|
|
134
|
+
const fingerprint = this.computeFingerprint(agentId, payload);
|
|
135
|
+
if (!this.cassette) {
|
|
136
|
+
return { matched: false, fingerprint, candidates: 0 };
|
|
137
|
+
}
|
|
138
|
+
const candidates = this.cassette.interactions.filter((i) => {
|
|
139
|
+
if (this.config.matchMode === 'fuzzy') {
|
|
140
|
+
return i.agentId === agentId;
|
|
141
|
+
}
|
|
142
|
+
return i.fingerprint === fingerprint;
|
|
143
|
+
});
|
|
144
|
+
return {
|
|
145
|
+
matched: candidates.length > 0,
|
|
146
|
+
fingerprint,
|
|
147
|
+
candidates: candidates.length,
|
|
148
|
+
bestMatch: candidates[0],
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
// --------------------------------------------------------------------------
|
|
152
|
+
// Internal
|
|
153
|
+
// --------------------------------------------------------------------------
|
|
154
|
+
async recordExecution(agentId, payload, handler) {
|
|
155
|
+
if (!handler)
|
|
156
|
+
throw new Error('VCR record mode requires a handler');
|
|
157
|
+
if (!this.cassette) {
|
|
158
|
+
this.cassette = { name: 'recording', createdAt: Date.now(), interactions: [] };
|
|
159
|
+
}
|
|
160
|
+
const start = Date.now();
|
|
161
|
+
const response = await handler(agentId, payload);
|
|
162
|
+
const durationMs = Date.now() - start;
|
|
163
|
+
const action = payload['action'] ?? '';
|
|
164
|
+
const paramsHash = this.hashParams(payload);
|
|
165
|
+
const fingerprint = this.computeFingerprint(agentId, payload);
|
|
166
|
+
this.cassette.interactions.push({
|
|
167
|
+
fingerprint,
|
|
168
|
+
agentId,
|
|
169
|
+
action,
|
|
170
|
+
paramsHash,
|
|
171
|
+
request: payload,
|
|
172
|
+
response,
|
|
173
|
+
recordedAt: Date.now(),
|
|
174
|
+
durationMs,
|
|
175
|
+
});
|
|
176
|
+
return response;
|
|
177
|
+
}
|
|
178
|
+
replayExecution(agentId, payload) {
|
|
179
|
+
if (!this.cassette || this.cassette.interactions.length === 0) {
|
|
180
|
+
throw new Error('No cassette loaded for replay. Call load() first.');
|
|
181
|
+
}
|
|
182
|
+
// Ordered mode: use sequential index
|
|
183
|
+
if (this.config.ordered) {
|
|
184
|
+
if (this.replayIndex >= this.cassette.interactions.length) {
|
|
185
|
+
throw new Error(`VCR replay exhausted: ${this.replayIndex} calls made but only ${this.cassette.interactions.length} recorded`);
|
|
186
|
+
}
|
|
187
|
+
const interaction = this.cassette.interactions[this.replayIndex++];
|
|
188
|
+
return interaction.response;
|
|
189
|
+
}
|
|
190
|
+
// Unordered: match by fingerprint
|
|
191
|
+
const fingerprint = this.computeFingerprint(agentId, payload);
|
|
192
|
+
const match = this.cassette.interactions.find((i) => {
|
|
193
|
+
if (this.config.matchMode === 'fuzzy') {
|
|
194
|
+
return i.agentId === agentId;
|
|
195
|
+
}
|
|
196
|
+
return i.fingerprint === fingerprint;
|
|
197
|
+
});
|
|
198
|
+
if (match) {
|
|
199
|
+
return match.response;
|
|
200
|
+
}
|
|
201
|
+
if (this.config.throwOnMissing) {
|
|
202
|
+
throw new Error(`VCR replay: no matching interaction for agent='${agentId}' fingerprint='${fingerprint}'. ` +
|
|
203
|
+
`Cassette has ${this.cassette.interactions.length} interactions.`);
|
|
204
|
+
}
|
|
205
|
+
return { error: 'VCR: no matching interaction', agentId };
|
|
206
|
+
}
|
|
207
|
+
computeFingerprint(agentId, payload) {
|
|
208
|
+
const action = payload['action'] ?? '';
|
|
209
|
+
const paramsHash = this.hashParams(payload);
|
|
210
|
+
return `${agentId}:${action}:${paramsHash}`;
|
|
211
|
+
}
|
|
212
|
+
hashParams(payload) {
|
|
213
|
+
const sorted = JSON.stringify(payload, Object.keys(payload).sort());
|
|
214
|
+
return (0, crypto_1.createHash)('sha256').update(sorted).digest('hex').slice(0, 12);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
exports.AgentVCR = AgentVCR;
|
|
218
|
+
//# sourceMappingURL=agent-vcr.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-vcr.js","sourceRoot":"","sources":["../../lib/agent-vcr.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AnomalyDetector — Baseline agent behavior and flag statistical outliers.
|
|
3
|
+
*
|
|
4
|
+
* Maintains rolling statistics (mean, stddev) per agent for latency, token
|
|
5
|
+
* usage, and error rate. New observations are checked against the baseline
|
|
6
|
+
* and flagged as anomalies when they exceed a configurable z-score threshold.
|
|
7
|
+
*
|
|
8
|
+
* Uses Welford's online algorithm for numerically stable incremental
|
|
9
|
+
* mean/variance — no batch recomputation needed.
|
|
10
|
+
*
|
|
11
|
+
* @module AnomalyDetector
|
|
12
|
+
*/
|
|
13
|
+
/** A detected anomaly */
|
|
14
|
+
export interface Anomaly {
|
|
15
|
+
/** ISO 8601 timestamp */
|
|
16
|
+
timestamp: string;
|
|
17
|
+
/** Agent that triggered the anomaly */
|
|
18
|
+
agentId: string;
|
|
19
|
+
/** Which metric is anomalous */
|
|
20
|
+
metric: AnomalyMetric;
|
|
21
|
+
/** Observed value */
|
|
22
|
+
observed: number;
|
|
23
|
+
/** Baseline mean for this agent + metric */
|
|
24
|
+
baselineMean: number;
|
|
25
|
+
/** Baseline standard deviation */
|
|
26
|
+
baselineStddev: number;
|
|
27
|
+
/** Z-score of the observation */
|
|
28
|
+
zScore: number;
|
|
29
|
+
/** Severity based on z-score magnitude */
|
|
30
|
+
severity: 'warning' | 'critical';
|
|
31
|
+
}
|
|
32
|
+
/** Metrics that are monitored for anomalies */
|
|
33
|
+
export type AnomalyMetric = 'latency' | 'tokens' | 'errorRate';
|
|
34
|
+
/** Summary of an agent's baseline */
|
|
35
|
+
export interface BaselineSummary {
|
|
36
|
+
agentId: string;
|
|
37
|
+
latency: {
|
|
38
|
+
mean: number;
|
|
39
|
+
stddev: number;
|
|
40
|
+
samples: number;
|
|
41
|
+
};
|
|
42
|
+
tokens: {
|
|
43
|
+
mean: number;
|
|
44
|
+
stddev: number;
|
|
45
|
+
samples: number;
|
|
46
|
+
};
|
|
47
|
+
errorRate: {
|
|
48
|
+
mean: number;
|
|
49
|
+
stddev: number;
|
|
50
|
+
samples: number;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Monitors agent behavior and flags statistical anomalies.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* const detector = new AnomalyDetector();
|
|
59
|
+
*
|
|
60
|
+
* // Feed observations as they occur:
|
|
61
|
+
* detector.observe('agent-1', { latencyMs: 200, tokens: 500, success: true });
|
|
62
|
+
* detector.observe('agent-1', { latencyMs: 15000, tokens: 500, success: true });
|
|
63
|
+
* // ^ second call may flag latency anomaly if baseline is ~200ms
|
|
64
|
+
*
|
|
65
|
+
* const anomalies = detector.getAnomalies();
|
|
66
|
+
* const baseline = detector.getBaseline('agent-1');
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export declare class AnomalyDetector {
|
|
70
|
+
private baselines;
|
|
71
|
+
private anomalies;
|
|
72
|
+
private maxAnomalies;
|
|
73
|
+
private warningThreshold;
|
|
74
|
+
private criticalThreshold;
|
|
75
|
+
private minSamples;
|
|
76
|
+
private errorWindow;
|
|
77
|
+
constructor(options?: {
|
|
78
|
+
/** Z-score threshold for 'warning' level. Default 2.0 */
|
|
79
|
+
warningThreshold?: number;
|
|
80
|
+
/** Z-score threshold for 'critical' level. Default 3.0 */
|
|
81
|
+
criticalThreshold?: number;
|
|
82
|
+
/** Minimum samples before anomaly detection activates. Default 10 */
|
|
83
|
+
minSamples?: number;
|
|
84
|
+
/** Max anomalies to retain. Default 1000 */
|
|
85
|
+
maxAnomalies?: number;
|
|
86
|
+
});
|
|
87
|
+
/**
|
|
88
|
+
* Record an observation for an agent and check for anomalies.
|
|
89
|
+
* Returns any anomalies detected from this observation.
|
|
90
|
+
*/
|
|
91
|
+
observe(agentId: string, obs: {
|
|
92
|
+
latencyMs: number;
|
|
93
|
+
tokens: number;
|
|
94
|
+
success: boolean;
|
|
95
|
+
}): Anomaly[];
|
|
96
|
+
/** Get all recorded anomalies, optionally filtered by agent */
|
|
97
|
+
getAnomalies(agentId?: string): Anomaly[];
|
|
98
|
+
/** Get recent anomalies (last N) */
|
|
99
|
+
getRecentAnomalies(count: number): Anomaly[];
|
|
100
|
+
/** Get the baseline summary for an agent */
|
|
101
|
+
getBaseline(agentId: string): BaselineSummary | null;
|
|
102
|
+
/** List all agents with baselines */
|
|
103
|
+
listAgents(): string[];
|
|
104
|
+
/** Total anomalies recorded */
|
|
105
|
+
get anomalyCount(): number;
|
|
106
|
+
/** Clear all baselines and anomalies */
|
|
107
|
+
clear(): void;
|
|
108
|
+
/** Clear data for a specific agent */
|
|
109
|
+
clearAgent(agentId: string): void;
|
|
110
|
+
private checkMetric;
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=anomaly-detector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anomaly-detector.d.ts","sourceRoot":"","sources":["../../lib/anomaly-detector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,yBAAyB;AACzB,MAAM,WAAW,OAAO;IACtB,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,gCAAgC;IAChC,MAAM,EAAE,aAAa,CAAC;IACtB,qBAAqB;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,YAAY,EAAE,MAAM,CAAC;IACrB,kCAAkC;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,QAAQ,EAAE,SAAS,GAAG,UAAU,CAAC;CAClC;AAED,+CAA+C;AAC/C,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,QAAQ,GAAG,WAAW,CAAC;AAgB/D,qCAAqC;AACrC,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1D,SAAS,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9D;AAiCD;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,SAAS,CAAyC;IAC1D,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,WAAW,CAA6D;gBAEpE,OAAO,CAAC,EAAE;QACpB,yDAAyD;QACzD,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,0DAA0D;QAC1D,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,qEAAqE;QACrE,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,4CAA4C;QAC5C,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB;IAOD;;;OAGG;IACH,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE;QAC5B,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,OAAO,CAAC;KAClB,GAAG,OAAO,EAAE;IA+Cb,+DAA+D;IAC/D,YAAY,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,EAAE;IAKzC,oCAAoC;IACpC,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,EAAE;IAI5C,4CAA4C;IAC5C,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI;IAWpD,qCAAqC;IACrC,UAAU,IAAI,MAAM,EAAE;IAItB,+BAA+B;IAC/B,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,wCAAwC;IACxC,KAAK,IAAI,IAAI;IAMb,sCAAsC;IACtC,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAMjC,OAAO,CAAC,WAAW;CA0BpB"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* AnomalyDetector — Baseline agent behavior and flag statistical outliers.
|
|
4
|
+
*
|
|
5
|
+
* Maintains rolling statistics (mean, stddev) per agent for latency, token
|
|
6
|
+
* usage, and error rate. New observations are checked against the baseline
|
|
7
|
+
* and flagged as anomalies when they exceed a configurable z-score threshold.
|
|
8
|
+
*
|
|
9
|
+
* Uses Welford's online algorithm for numerically stable incremental
|
|
10
|
+
* mean/variance — no batch recomputation needed.
|
|
11
|
+
*
|
|
12
|
+
* @module AnomalyDetector
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.AnomalyDetector = void 0;
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// WELFORD HELPERS
|
|
18
|
+
// ============================================================================
|
|
19
|
+
function welfordInit() {
|
|
20
|
+
return { count: 0, mean: 0, m2: 0 };
|
|
21
|
+
}
|
|
22
|
+
function welfordUpdate(state, value) {
|
|
23
|
+
state.count++;
|
|
24
|
+
const delta = value - state.mean;
|
|
25
|
+
state.mean += delta / state.count;
|
|
26
|
+
const delta2 = value - state.mean;
|
|
27
|
+
state.m2 += delta * delta2;
|
|
28
|
+
}
|
|
29
|
+
function welfordStddev(state) {
|
|
30
|
+
if (state.count < 2)
|
|
31
|
+
return 0;
|
|
32
|
+
return Math.sqrt(state.m2 / (state.count - 1));
|
|
33
|
+
}
|
|
34
|
+
function welfordZScore(state, value) {
|
|
35
|
+
const std = welfordStddev(state);
|
|
36
|
+
if (std === 0 || state.count < 5)
|
|
37
|
+
return 0; // Need minimum samples
|
|
38
|
+
return (value - state.mean) / std;
|
|
39
|
+
}
|
|
40
|
+
// ============================================================================
|
|
41
|
+
// DETECTOR
|
|
42
|
+
// ============================================================================
|
|
43
|
+
/**
|
|
44
|
+
* Monitors agent behavior and flags statistical anomalies.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* const detector = new AnomalyDetector();
|
|
49
|
+
*
|
|
50
|
+
* // Feed observations as they occur:
|
|
51
|
+
* detector.observe('agent-1', { latencyMs: 200, tokens: 500, success: true });
|
|
52
|
+
* detector.observe('agent-1', { latencyMs: 15000, tokens: 500, success: true });
|
|
53
|
+
* // ^ second call may flag latency anomaly if baseline is ~200ms
|
|
54
|
+
*
|
|
55
|
+
* const anomalies = detector.getAnomalies();
|
|
56
|
+
* const baseline = detector.getBaseline('agent-1');
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
class AnomalyDetector {
|
|
60
|
+
baselines = new Map();
|
|
61
|
+
anomalies = [];
|
|
62
|
+
maxAnomalies;
|
|
63
|
+
warningThreshold;
|
|
64
|
+
criticalThreshold;
|
|
65
|
+
minSamples;
|
|
66
|
+
errorWindow = new Map();
|
|
67
|
+
constructor(options) {
|
|
68
|
+
this.warningThreshold = options?.warningThreshold ?? 2.0;
|
|
69
|
+
this.criticalThreshold = options?.criticalThreshold ?? 3.0;
|
|
70
|
+
this.minSamples = options?.minSamples ?? 10;
|
|
71
|
+
this.maxAnomalies = options?.maxAnomalies ?? 1000;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Record an observation for an agent and check for anomalies.
|
|
75
|
+
* Returns any anomalies detected from this observation.
|
|
76
|
+
*/
|
|
77
|
+
observe(agentId, obs) {
|
|
78
|
+
let baseline = this.baselines.get(agentId);
|
|
79
|
+
if (!baseline) {
|
|
80
|
+
baseline = {
|
|
81
|
+
latency: welfordInit(),
|
|
82
|
+
tokens: welfordInit(),
|
|
83
|
+
errorRate: welfordInit(),
|
|
84
|
+
};
|
|
85
|
+
this.baselines.set(agentId, baseline);
|
|
86
|
+
}
|
|
87
|
+
// Update error rate tracking
|
|
88
|
+
let errWin = this.errorWindow.get(agentId);
|
|
89
|
+
if (!errWin) {
|
|
90
|
+
errWin = { total: 0, errors: 0 };
|
|
91
|
+
this.errorWindow.set(agentId, errWin);
|
|
92
|
+
}
|
|
93
|
+
errWin.total++;
|
|
94
|
+
if (!obs.success)
|
|
95
|
+
errWin.errors++;
|
|
96
|
+
const currentErrorRate = errWin.total > 0 ? errWin.errors / errWin.total : 0;
|
|
97
|
+
const detected = [];
|
|
98
|
+
// Check before updating baseline (compare against historical)
|
|
99
|
+
detected.push(...this.checkMetric(agentId, 'latency', baseline.latency, obs.latencyMs));
|
|
100
|
+
detected.push(...this.checkMetric(agentId, 'tokens', baseline.tokens, obs.tokens));
|
|
101
|
+
detected.push(...this.checkMetric(agentId, 'errorRate', baseline.errorRate, currentErrorRate));
|
|
102
|
+
// Now update baselines
|
|
103
|
+
welfordUpdate(baseline.latency, obs.latencyMs);
|
|
104
|
+
welfordUpdate(baseline.tokens, obs.tokens);
|
|
105
|
+
welfordUpdate(baseline.errorRate, currentErrorRate);
|
|
106
|
+
// Store detected anomalies
|
|
107
|
+
for (const a of detected) {
|
|
108
|
+
this.anomalies.push(a);
|
|
109
|
+
}
|
|
110
|
+
if (this.anomalies.length > this.maxAnomalies) {
|
|
111
|
+
this.anomalies.splice(0, this.anomalies.length - this.maxAnomalies);
|
|
112
|
+
}
|
|
113
|
+
return detected;
|
|
114
|
+
}
|
|
115
|
+
/** Get all recorded anomalies, optionally filtered by agent */
|
|
116
|
+
getAnomalies(agentId) {
|
|
117
|
+
if (agentId)
|
|
118
|
+
return this.anomalies.filter(a => a.agentId === agentId);
|
|
119
|
+
return [...this.anomalies];
|
|
120
|
+
}
|
|
121
|
+
/** Get recent anomalies (last N) */
|
|
122
|
+
getRecentAnomalies(count) {
|
|
123
|
+
return this.anomalies.slice(-count);
|
|
124
|
+
}
|
|
125
|
+
/** Get the baseline summary for an agent */
|
|
126
|
+
getBaseline(agentId) {
|
|
127
|
+
const b = this.baselines.get(agentId);
|
|
128
|
+
if (!b)
|
|
129
|
+
return null;
|
|
130
|
+
return {
|
|
131
|
+
agentId,
|
|
132
|
+
latency: { mean: b.latency.mean, stddev: welfordStddev(b.latency), samples: b.latency.count },
|
|
133
|
+
tokens: { mean: b.tokens.mean, stddev: welfordStddev(b.tokens), samples: b.tokens.count },
|
|
134
|
+
errorRate: { mean: b.errorRate.mean, stddev: welfordStddev(b.errorRate), samples: b.errorRate.count },
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/** List all agents with baselines */
|
|
138
|
+
listAgents() {
|
|
139
|
+
return [...this.baselines.keys()];
|
|
140
|
+
}
|
|
141
|
+
/** Total anomalies recorded */
|
|
142
|
+
get anomalyCount() {
|
|
143
|
+
return this.anomalies.length;
|
|
144
|
+
}
|
|
145
|
+
/** Clear all baselines and anomalies */
|
|
146
|
+
clear() {
|
|
147
|
+
this.baselines.clear();
|
|
148
|
+
this.anomalies.length = 0;
|
|
149
|
+
this.errorWindow.clear();
|
|
150
|
+
}
|
|
151
|
+
/** Clear data for a specific agent */
|
|
152
|
+
clearAgent(agentId) {
|
|
153
|
+
this.baselines.delete(agentId);
|
|
154
|
+
this.errorWindow.delete(agentId);
|
|
155
|
+
this.anomalies = this.anomalies.filter(a => a.agentId !== agentId);
|
|
156
|
+
}
|
|
157
|
+
checkMetric(agentId, metric, state, value) {
|
|
158
|
+
if (state.count < this.minSamples)
|
|
159
|
+
return [];
|
|
160
|
+
const z = welfordZScore(state, value);
|
|
161
|
+
const absZ = Math.abs(z);
|
|
162
|
+
if (absZ >= this.warningThreshold) {
|
|
163
|
+
return [{
|
|
164
|
+
timestamp: new Date().toISOString(),
|
|
165
|
+
agentId,
|
|
166
|
+
metric,
|
|
167
|
+
observed: value,
|
|
168
|
+
baselineMean: state.mean,
|
|
169
|
+
baselineStddev: welfordStddev(state),
|
|
170
|
+
zScore: z,
|
|
171
|
+
severity: absZ >= this.criticalThreshold ? 'critical' : 'warning',
|
|
172
|
+
}];
|
|
173
|
+
}
|
|
174
|
+
return [];
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
exports.AnomalyDetector = AnomalyDetector;
|
|
178
|
+
//# sourceMappingURL=anomaly-detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anomaly-detector.js","sourceRoot":"","sources":["../../lib/anomaly-detector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ApprovalInbox — Web-accessible human-in-the-loop approval system
|
|
3
|
+
*
|
|
4
|
+
* Provides an HTTP API and SSE streaming for managing approval requests
|
|
5
|
+
* from AI agents. Designed to work as an ApprovalCallback for ApprovalGate
|
|
6
|
+
* while also exposing a REST-like HTTP interface.
|
|
7
|
+
*
|
|
8
|
+
* Features:
|
|
9
|
+
* - Queues approval requests with unique IDs and optional timeouts
|
|
10
|
+
* - REST API: list, get, approve, deny, stats
|
|
11
|
+
* - SSE stream for real-time notifications
|
|
12
|
+
* - Auto-expiry for stale requests
|
|
13
|
+
* - Standalone HTTP server or mountable handler
|
|
14
|
+
*
|
|
15
|
+
* @module ApprovalInbox
|
|
16
|
+
* @version 1.0.0
|
|
17
|
+
*/
|
|
18
|
+
import { EventEmitter } from 'events';
|
|
19
|
+
import { IncomingMessage, ServerResponse, Server } from 'http';
|
|
20
|
+
import type { ApprovalRequest, ApprovalDecision, ApprovalCallback } from './agent-runtime';
|
|
21
|
+
/** Status of an approval entry */
|
|
22
|
+
export type ApprovalStatus = 'pending' | 'approved' | 'denied' | 'expired';
|
|
23
|
+
/** A queued approval entry */
|
|
24
|
+
export interface ApprovalEntry {
|
|
25
|
+
/** Unique identifier for this approval */
|
|
26
|
+
id: string;
|
|
27
|
+
/** The original approval request */
|
|
28
|
+
request: ApprovalRequest;
|
|
29
|
+
/** Current status */
|
|
30
|
+
status: ApprovalStatus;
|
|
31
|
+
/** Decision if resolved */
|
|
32
|
+
decision?: ApprovalDecision;
|
|
33
|
+
/** When the request was created */
|
|
34
|
+
createdAt: number;
|
|
35
|
+
/** When the request was resolved */
|
|
36
|
+
resolvedAt?: number;
|
|
37
|
+
/** Timeout for this specific request (ms) */
|
|
38
|
+
timeoutMs: number;
|
|
39
|
+
}
|
|
40
|
+
/** Options for the ApprovalInbox */
|
|
41
|
+
export interface ApprovalInboxOptions {
|
|
42
|
+
/** Default timeout for approval requests in ms (default: 300000 = 5 min) */
|
|
43
|
+
defaultTimeoutMs?: number;
|
|
44
|
+
/** Maximum number of pending approvals (default: 100) */
|
|
45
|
+
maxPending?: number;
|
|
46
|
+
/** Maximum history entries to keep (default: 1000) */
|
|
47
|
+
maxHistory?: number;
|
|
48
|
+
/** URL path prefix for HTTP handler (default: '/approvals') */
|
|
49
|
+
pathPrefix?: string;
|
|
50
|
+
}
|
|
51
|
+
/** SSE event types */
|
|
52
|
+
export type InboxEventType = 'new' | 'approved' | 'denied' | 'expired';
|
|
53
|
+
/** SSE event payload */
|
|
54
|
+
export interface InboxEvent {
|
|
55
|
+
type: InboxEventType;
|
|
56
|
+
entry: ApprovalEntry;
|
|
57
|
+
}
|
|
58
|
+
/** Stats snapshot */
|
|
59
|
+
export interface InboxStats {
|
|
60
|
+
pending: number;
|
|
61
|
+
approved: number;
|
|
62
|
+
denied: number;
|
|
63
|
+
expired: number;
|
|
64
|
+
total: number;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Web-accessible approval inbox for human-in-the-loop agent workflows.
|
|
68
|
+
*
|
|
69
|
+
* Use `inbox.callback()` to get an ApprovalCallback for ApprovalGate,
|
|
70
|
+
* and `inbox.httpHandler()` to mount the HTTP API on a server.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```ts
|
|
74
|
+
* const inbox = new ApprovalInbox();
|
|
75
|
+
* const gate = new ApprovalGate(inbox.callback());
|
|
76
|
+
* const server = inbox.createServer(3002);
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export declare class ApprovalInbox extends EventEmitter {
|
|
80
|
+
private readonly pending;
|
|
81
|
+
private readonly history;
|
|
82
|
+
private readonly sseClients;
|
|
83
|
+
private readonly defaultTimeoutMs;
|
|
84
|
+
private readonly maxPending;
|
|
85
|
+
private readonly maxHistory;
|
|
86
|
+
private readonly pathPrefix;
|
|
87
|
+
private approvedCount;
|
|
88
|
+
private deniedCount;
|
|
89
|
+
private expiredCount;
|
|
90
|
+
constructor(options?: ApprovalInboxOptions);
|
|
91
|
+
/**
|
|
92
|
+
* Returns an ApprovalCallback suitable for ApprovalGate.
|
|
93
|
+
* Each call enqueues a pending approval and waits for resolution.
|
|
94
|
+
*/
|
|
95
|
+
callback(): ApprovalCallback;
|
|
96
|
+
/**
|
|
97
|
+
* Enqueue an approval request. Returns a promise that resolves when
|
|
98
|
+
* the request is approved, denied, or expires.
|
|
99
|
+
*/
|
|
100
|
+
enqueue(request: ApprovalRequest, timeoutMs?: number): Promise<ApprovalDecision>;
|
|
101
|
+
/**
|
|
102
|
+
* Approve a pending request.
|
|
103
|
+
* @returns The resolved entry, or undefined if not found/already resolved.
|
|
104
|
+
*/
|
|
105
|
+
approve(id: string, approvedBy: string, reason?: string): ApprovalEntry | undefined;
|
|
106
|
+
/**
|
|
107
|
+
* Deny a pending request.
|
|
108
|
+
* @returns The resolved entry, or undefined if not found/already resolved.
|
|
109
|
+
*/
|
|
110
|
+
deny(id: string, deniedBy?: string, reason?: string): ApprovalEntry | undefined;
|
|
111
|
+
/** Get a single entry by ID (pending or historical) */
|
|
112
|
+
get(id: string): ApprovalEntry | undefined;
|
|
113
|
+
/** List entries by status (default: 'pending') */
|
|
114
|
+
list(status?: ApprovalStatus | 'all'): ApprovalEntry[];
|
|
115
|
+
/** Get aggregate stats */
|
|
116
|
+
stats(): InboxStats;
|
|
117
|
+
/** Number of pending approvals */
|
|
118
|
+
get pendingCount(): number;
|
|
119
|
+
/**
|
|
120
|
+
* Returns an HTTP request handler for the approval inbox API.
|
|
121
|
+
*
|
|
122
|
+
* Routes (relative to pathPrefix):
|
|
123
|
+
* GET / — List approvals (?status=pending|approved|denied|expired|all)
|
|
124
|
+
* GET /stats — Aggregate stats
|
|
125
|
+
* GET /sse — SSE event stream
|
|
126
|
+
* GET /:id — Get single entry
|
|
127
|
+
* POST /:id/approve — Approve (body: { approvedBy, reason? })
|
|
128
|
+
* POST /:id/deny — Deny (body: { deniedBy?, reason? })
|
|
129
|
+
*/
|
|
130
|
+
httpHandler(): (req: IncomingMessage, res: ServerResponse) => void;
|
|
131
|
+
/**
|
|
132
|
+
* Create a standalone HTTP server for the inbox.
|
|
133
|
+
* @returns The HTTP server instance (already listening).
|
|
134
|
+
*/
|
|
135
|
+
startServer(port: number, hostname?: string): Server;
|
|
136
|
+
/** Broadcast an event to all connected SSE clients */
|
|
137
|
+
private broadcastSSE;
|
|
138
|
+
/** Register an SSE client */
|
|
139
|
+
private addSSEClient;
|
|
140
|
+
private resolve;
|
|
141
|
+
private expire;
|
|
142
|
+
private addToHistory;
|
|
143
|
+
private routeRequest;
|
|
144
|
+
private sendJson;
|
|
145
|
+
private readBody;
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=approval-inbox.d.ts.map
|