@advicenxt/sbp-server 0.1.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/benchmarks/bench.ts +272 -0
- package/dist/auth.d.ts +20 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +69 -0
- package/dist/auth.js.map +1 -0
- package/dist/blackboard.d.ts +84 -0
- package/dist/blackboard.d.ts.map +1 -0
- package/dist/blackboard.js +502 -0
- package/dist/blackboard.js.map +1 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +102 -0
- package/dist/cli.js.map +1 -0
- package/dist/conditions.d.ts +27 -0
- package/dist/conditions.d.ts.map +1 -0
- package/dist/conditions.js +240 -0
- package/dist/conditions.js.map +1 -0
- package/dist/decay.d.ts +21 -0
- package/dist/decay.d.ts.map +1 -0
- package/dist/decay.js +88 -0
- package/dist/decay.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/rate-limiter.d.ts +21 -0
- package/dist/rate-limiter.d.ts.map +1 -0
- package/dist/rate-limiter.js +75 -0
- package/dist/rate-limiter.js.map +1 -0
- package/dist/server.d.ts +63 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +401 -0
- package/dist/server.js.map +1 -0
- package/dist/store.d.ts +54 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +55 -0
- package/dist/store.js.map +1 -0
- package/dist/types.d.ts +247 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +26 -0
- package/dist/types.js.map +1 -0
- package/dist/validation.d.ts +296 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +205 -0
- package/dist/validation.js.map +1 -0
- package/eslint.config.js +26 -0
- package/package.json +66 -0
- package/src/auth.ts +89 -0
- package/src/blackboard.test.ts +287 -0
- package/src/blackboard.ts +651 -0
- package/src/cli.ts +116 -0
- package/src/conditions.ts +305 -0
- package/src/conformance.test.ts +686 -0
- package/src/decay.ts +103 -0
- package/src/index.ts +24 -0
- package/src/rate-limiter.ts +104 -0
- package/src/server.integration.test.ts +436 -0
- package/src/server.ts +500 -0
- package/src/store.ts +108 -0
- package/src/types.ts +314 -0
- package/src/validation.ts +251 -0
- package/tsconfig.eslint.json +5 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SBP Benchmark Suite
|
|
3
|
+
*
|
|
4
|
+
* Measures performance characteristics of the core blackboard operations.
|
|
5
|
+
* Run: npx tsx benchmarks/bench.ts
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Blackboard } from "../src/blackboard.js";
|
|
9
|
+
import { MemoryStore } from "../src/store.js";
|
|
10
|
+
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// HELPERS
|
|
13
|
+
// ============================================================================
|
|
14
|
+
|
|
15
|
+
function formatOps(count: number, durationMs: number): string {
|
|
16
|
+
const opsPerSec = Math.round((count / durationMs) * 1000);
|
|
17
|
+
return `${opsPerSec.toLocaleString()} ops/sec`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function formatDuration(ms: number): string {
|
|
21
|
+
if (ms < 1) return `${(ms * 1000).toFixed(0)}µs`;
|
|
22
|
+
if (ms < 1000) return `${ms.toFixed(2)}ms`;
|
|
23
|
+
return `${(ms / 1000).toFixed(2)}s`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function runBench(name: string, fn: () => void, iterations: number = 100_000): void {
|
|
27
|
+
// Warmup
|
|
28
|
+
for (let i = 0; i < Math.min(1000, iterations / 10); i++) fn();
|
|
29
|
+
|
|
30
|
+
const start = performance.now();
|
|
31
|
+
for (let i = 0; i < iterations; i++) fn();
|
|
32
|
+
const elapsed = performance.now() - start;
|
|
33
|
+
|
|
34
|
+
const avg = elapsed / iterations;
|
|
35
|
+
console.log(
|
|
36
|
+
` ${name.padEnd(45)} ${formatOps(iterations, elapsed).padStart(15)} (avg ${formatDuration(avg)})`
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ============================================================================
|
|
41
|
+
// BENCHMARKS
|
|
42
|
+
// ============================================================================
|
|
43
|
+
|
|
44
|
+
function benchEmitNew(): void {
|
|
45
|
+
const bb = new Blackboard();
|
|
46
|
+
|
|
47
|
+
console.log("\n📤 EMIT (new pheromone, no merge)");
|
|
48
|
+
let i = 0;
|
|
49
|
+
runBench("emit new pheromone", () => {
|
|
50
|
+
bb.emit({
|
|
51
|
+
trail: "bench.signals",
|
|
52
|
+
type: `type-${i++}`,
|
|
53
|
+
intensity: 0.8,
|
|
54
|
+
merge_strategy: "new",
|
|
55
|
+
});
|
|
56
|
+
}, 50_000);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function benchEmitReinforce(): void {
|
|
60
|
+
const bb = new Blackboard();
|
|
61
|
+
bb.emit({
|
|
62
|
+
trail: "bench.signals",
|
|
63
|
+
type: "target",
|
|
64
|
+
intensity: 0.5,
|
|
65
|
+
payload: { key: "value" },
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
console.log("\n🔁 EMIT (reinforce existing)");
|
|
69
|
+
runBench("emit reinforce", () => {
|
|
70
|
+
bb.emit({
|
|
71
|
+
trail: "bench.signals",
|
|
72
|
+
type: "target",
|
|
73
|
+
intensity: 0.7,
|
|
74
|
+
payload: { key: "value" },
|
|
75
|
+
merge_strategy: "reinforce",
|
|
76
|
+
});
|
|
77
|
+
}, 50_000);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function benchSniff(pheromoneCount: number): void {
|
|
81
|
+
const bb = new Blackboard();
|
|
82
|
+
for (let i = 0; i < pheromoneCount; i++) {
|
|
83
|
+
bb.emit({
|
|
84
|
+
trail: `bench.trail-${i % 10}`,
|
|
85
|
+
type: `type-${i}`,
|
|
86
|
+
intensity: Math.random(),
|
|
87
|
+
merge_strategy: "new",
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
console.log(`\n👃 SNIFF (${pheromoneCount.toLocaleString()} pheromones in store)`);
|
|
92
|
+
|
|
93
|
+
runBench("sniff all (no filters)", () => {
|
|
94
|
+
bb.sniff({ limit: 100 });
|
|
95
|
+
}, 10_000);
|
|
96
|
+
|
|
97
|
+
runBench("sniff single trail", () => {
|
|
98
|
+
bb.sniff({ trails: ["bench.trail-0"], limit: 100 });
|
|
99
|
+
}, 10_000);
|
|
100
|
+
|
|
101
|
+
runBench("sniff with min_intensity 0.5", () => {
|
|
102
|
+
bb.sniff({ min_intensity: 0.5, limit: 100 });
|
|
103
|
+
}, 10_000);
|
|
104
|
+
|
|
105
|
+
runBench("sniff with tags filter", () => {
|
|
106
|
+
bb.sniff({ tags: { any: ["tag-a"] }, limit: 100 });
|
|
107
|
+
}, 10_000);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function benchConditionEvaluation(): void {
|
|
111
|
+
const bb = new Blackboard();
|
|
112
|
+
for (let i = 0; i < 1000; i++) {
|
|
113
|
+
bb.emit({
|
|
114
|
+
trail: "bench.eval",
|
|
115
|
+
type: `signal-${i % 5}`,
|
|
116
|
+
intensity: 0.3 + Math.random() * 0.7,
|
|
117
|
+
merge_strategy: "new",
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Register a composite scent
|
|
122
|
+
bb.registerScent({
|
|
123
|
+
scent_id: "bench-scent",
|
|
124
|
+
agent_endpoint: "http://localhost:9999",
|
|
125
|
+
condition: {
|
|
126
|
+
type: "composite",
|
|
127
|
+
operator: "and",
|
|
128
|
+
conditions: [
|
|
129
|
+
{
|
|
130
|
+
type: "threshold",
|
|
131
|
+
trail: "bench.eval",
|
|
132
|
+
signal_type: "signal-0",
|
|
133
|
+
aggregation: "max",
|
|
134
|
+
operator: ">=",
|
|
135
|
+
value: 0.7,
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
type: "threshold",
|
|
139
|
+
trail: "bench.eval",
|
|
140
|
+
signal_type: "signal-1",
|
|
141
|
+
aggregation: "count",
|
|
142
|
+
operator: ">=",
|
|
143
|
+
value: 3,
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
},
|
|
147
|
+
cooldown_ms: 0,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
console.log("\n🧠 CONDITION EVALUATION (1,000 pheromones, composite AND)");
|
|
151
|
+
runBench("evaluateScents (sync part)", () => {
|
|
152
|
+
// Evaluate without triggering (cooldown = 0 means it fires and resets)
|
|
153
|
+
const pheromones = [...(bb as any).store.values()];
|
|
154
|
+
const { evaluateCondition } = require("../src/conditions.js");
|
|
155
|
+
evaluateCondition(
|
|
156
|
+
{
|
|
157
|
+
type: "composite",
|
|
158
|
+
operator: "and",
|
|
159
|
+
conditions: [
|
|
160
|
+
{
|
|
161
|
+
type: "threshold",
|
|
162
|
+
trail: "bench.eval",
|
|
163
|
+
signal_type: "signal-0",
|
|
164
|
+
aggregation: "max",
|
|
165
|
+
operator: ">=",
|
|
166
|
+
value: 0.7,
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
type: "threshold",
|
|
170
|
+
trail: "bench.eval",
|
|
171
|
+
signal_type: "signal-1",
|
|
172
|
+
aggregation: "count",
|
|
173
|
+
operator: ">=",
|
|
174
|
+
value: 3,
|
|
175
|
+
},
|
|
176
|
+
],
|
|
177
|
+
},
|
|
178
|
+
{ pheromones, now: Date.now(), emissionHistory: [] }
|
|
179
|
+
);
|
|
180
|
+
}, 10_000);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function benchGarbageCollection(): void {
|
|
184
|
+
const bb = new Blackboard({ defaultDecay: { type: "linear", rate_per_ms: 100 } });
|
|
185
|
+
for (let i = 0; i < 10_000; i++) {
|
|
186
|
+
bb.emit({
|
|
187
|
+
trail: "bench.gc",
|
|
188
|
+
type: `type-${i}`,
|
|
189
|
+
intensity: 0.5,
|
|
190
|
+
merge_strategy: "new",
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
// Let them evaporate
|
|
194
|
+
const delay = (ms: number) => {
|
|
195
|
+
const end = Date.now() + ms;
|
|
196
|
+
while (Date.now() < end) { }
|
|
197
|
+
};
|
|
198
|
+
delay(50);
|
|
199
|
+
|
|
200
|
+
console.log("\n🧹 GARBAGE COLLECTION (10,000 evaporated pheromones)");
|
|
201
|
+
const start = performance.now();
|
|
202
|
+
const removed = bb.gc();
|
|
203
|
+
const elapsed = performance.now() - start;
|
|
204
|
+
console.log(` gc() removed ${removed.toLocaleString()} pheromones in ${formatDuration(elapsed)}`);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function benchStoreOperations(): void {
|
|
208
|
+
console.log("\n💾 STORE OPERATIONS (MemoryStore)");
|
|
209
|
+
const store = new MemoryStore();
|
|
210
|
+
|
|
211
|
+
runBench("store.set + store.get", () => {
|
|
212
|
+
const p = {
|
|
213
|
+
id: "test-id",
|
|
214
|
+
trail: "bench",
|
|
215
|
+
type: "test",
|
|
216
|
+
emitted_at: Date.now(),
|
|
217
|
+
last_reinforced_at: Date.now(),
|
|
218
|
+
initial_intensity: 0.5,
|
|
219
|
+
decay_model: { type: "exponential" as const, half_life_ms: 300000 },
|
|
220
|
+
payload: {},
|
|
221
|
+
tags: [],
|
|
222
|
+
ttl_floor: 0.01,
|
|
223
|
+
};
|
|
224
|
+
store.set("test-id", p);
|
|
225
|
+
store.get("test-id");
|
|
226
|
+
}, 100_000);
|
|
227
|
+
|
|
228
|
+
// Fill store then iterate
|
|
229
|
+
for (let i = 0; i < 10_000; i++) {
|
|
230
|
+
store.set(`p-${i}`, {
|
|
231
|
+
id: `p-${i}`,
|
|
232
|
+
trail: "bench",
|
|
233
|
+
type: "test",
|
|
234
|
+
emitted_at: Date.now(),
|
|
235
|
+
last_reinforced_at: Date.now(),
|
|
236
|
+
initial_intensity: 0.5,
|
|
237
|
+
decay_model: { type: "exponential" as const, half_life_ms: 300000 },
|
|
238
|
+
payload: {},
|
|
239
|
+
tags: [],
|
|
240
|
+
ttl_floor: 0.01,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
runBench("store.values() iteration (10k entries)", () => {
|
|
245
|
+
let count = 0;
|
|
246
|
+
for (const _p of store.values()) count++;
|
|
247
|
+
}, 1_000);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// ============================================================================
|
|
251
|
+
// MAIN
|
|
252
|
+
// ============================================================================
|
|
253
|
+
|
|
254
|
+
console.log("═══════════════════════════════════════════════════════════════");
|
|
255
|
+
console.log(" SBP Benchmark Suite");
|
|
256
|
+
console.log("═══════════════════════════════════════════════════════════════");
|
|
257
|
+
console.log(` Node ${process.version} | ${process.platform} ${process.arch}`);
|
|
258
|
+
console.log(` Date: ${new Date().toISOString()}`);
|
|
259
|
+
console.log("═══════════════════════════════════════════════════════════════");
|
|
260
|
+
|
|
261
|
+
benchEmitNew();
|
|
262
|
+
benchEmitReinforce();
|
|
263
|
+
benchSniff(100);
|
|
264
|
+
benchSniff(1_000);
|
|
265
|
+
benchSniff(10_000);
|
|
266
|
+
benchConditionEvaluation();
|
|
267
|
+
benchGarbageCollection();
|
|
268
|
+
benchStoreOperations();
|
|
269
|
+
|
|
270
|
+
console.log("\n═══════════════════════════════════════════════════════════════");
|
|
271
|
+
console.log(" Done.");
|
|
272
|
+
console.log("═══════════════════════════════════════════════════════════════\n");
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SBP Authentication Middleware
|
|
3
|
+
* Basic API key authentication for the SBP server
|
|
4
|
+
*/
|
|
5
|
+
import type { FastifyRequest, FastifyReply, HookHandlerDoneFunction } from "fastify";
|
|
6
|
+
export interface AuthOptions {
|
|
7
|
+
/** List of valid API keys */
|
|
8
|
+
apiKeys?: string[];
|
|
9
|
+
/** Whether authentication is required (default: false) */
|
|
10
|
+
requireAuth?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Create a Fastify onRequest hook for API key authentication.
|
|
14
|
+
*
|
|
15
|
+
* Checks the `Authorization: Bearer <key>` header against the
|
|
16
|
+
* configured list of API keys. Returns 401 with SBP error code
|
|
17
|
+
* -32005 (UNAUTHORIZED) if the key is missing or invalid.
|
|
18
|
+
*/
|
|
19
|
+
export declare function createAuthHook(options: AuthOptions): (request: FastifyRequest, reply: FastifyReply, done: HookHandlerDoneFunction) => void;
|
|
20
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAErF,MAAM,WAAW,WAAW;IACxB,6BAA6B;IAC7B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,0DAA0D;IAC1D,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB;AAKD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,IAI3C,SAAS,cAAc,EACvB,OAAO,YAAY,EACnB,MAAM,uBAAuB,KAC9B,IAAI,CAyDV"}
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SBP Authentication Middleware
|
|
3
|
+
* Basic API key authentication for the SBP server
|
|
4
|
+
*/
|
|
5
|
+
/** Paths that skip authentication */
|
|
6
|
+
const PUBLIC_PATHS = ["/health"];
|
|
7
|
+
/**
|
|
8
|
+
* Create a Fastify onRequest hook for API key authentication.
|
|
9
|
+
*
|
|
10
|
+
* Checks the `Authorization: Bearer <key>` header against the
|
|
11
|
+
* configured list of API keys. Returns 401 with SBP error code
|
|
12
|
+
* -32005 (UNAUTHORIZED) if the key is missing or invalid.
|
|
13
|
+
*/
|
|
14
|
+
export function createAuthHook(options) {
|
|
15
|
+
const { apiKeys = [], requireAuth = false } = options;
|
|
16
|
+
return function authHook(request, reply, done) {
|
|
17
|
+
// Skip auth if not required
|
|
18
|
+
if (!requireAuth || apiKeys.length === 0) {
|
|
19
|
+
done();
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
// Skip auth for public paths and OPTIONS
|
|
23
|
+
const url = request.url.split("?")[0];
|
|
24
|
+
if (PUBLIC_PATHS.includes(url) || request.method === "OPTIONS") {
|
|
25
|
+
done();
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
// Extract bearer token
|
|
29
|
+
const authHeader = request.headers.authorization;
|
|
30
|
+
if (!authHeader) {
|
|
31
|
+
reply.status(401).send({
|
|
32
|
+
jsonrpc: "2.0",
|
|
33
|
+
id: null,
|
|
34
|
+
error: {
|
|
35
|
+
code: -32005,
|
|
36
|
+
message: "Unauthorized: Missing Authorization header",
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const parts = authHeader.split(" ");
|
|
42
|
+
if (parts.length !== 2 || parts[0].toLowerCase() !== "bearer") {
|
|
43
|
+
reply.status(401).send({
|
|
44
|
+
jsonrpc: "2.0",
|
|
45
|
+
id: null,
|
|
46
|
+
error: {
|
|
47
|
+
code: -32005,
|
|
48
|
+
message: "Unauthorized: Invalid Authorization header format. Expected: Bearer <api-key>",
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const token = parts[1];
|
|
54
|
+
if (!apiKeys.includes(token)) {
|
|
55
|
+
reply.status(401).send({
|
|
56
|
+
jsonrpc: "2.0",
|
|
57
|
+
id: null,
|
|
58
|
+
error: {
|
|
59
|
+
code: -32005,
|
|
60
|
+
message: "Unauthorized: Invalid API key",
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
// Valid key — attach agent info from token position (optional extension point)
|
|
66
|
+
done();
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,qCAAqC;AACrC,MAAM,YAAY,GAAG,CAAC,SAAS,CAAC,CAAC;AAEjC;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,OAAoB;IAC/C,MAAM,EAAE,OAAO,GAAG,EAAE,EAAE,WAAW,GAAG,KAAK,EAAE,GAAG,OAAO,CAAC;IAEtD,OAAO,SAAS,QAAQ,CACpB,OAAuB,EACvB,KAAmB,EACnB,IAA6B;QAE7B,4BAA4B;QAC5B,IAAI,CAAC,WAAW,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvC,IAAI,EAAE,CAAC;YACP,OAAO;QACX,CAAC;QAED,yCAAyC;QACzC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACtC,IAAI,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC7D,IAAI,EAAE,CAAC;YACP,OAAO;QACX,CAAC;QAED,uBAAuB;QACvB,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC;QACjD,IAAI,CAAC,UAAU,EAAE,CAAC;YACd,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnB,OAAO,EAAE,KAAK;gBACd,EAAE,EAAE,IAAI;gBACR,KAAK,EAAE;oBACH,IAAI,EAAE,CAAC,KAAK;oBACZ,OAAO,EAAE,4CAA4C;iBACxD;aACJ,CAAC,CAAC;YACH,OAAO;QACX,CAAC;QAED,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,QAAQ,EAAE,CAAC;YAC5D,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnB,OAAO,EAAE,KAAK;gBACd,EAAE,EAAE,IAAI;gBACR,KAAK,EAAE;oBACH,IAAI,EAAE,CAAC,KAAK;oBACZ,OAAO,EAAE,+EAA+E;iBAC3F;aACJ,CAAC,CAAC;YACH,OAAO;QACX,CAAC;QAED,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACvB,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3B,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnB,OAAO,EAAE,KAAK;gBACd,EAAE,EAAE,IAAI;gBACR,KAAK,EAAE;oBACH,IAAI,EAAE,CAAC,KAAK;oBACZ,OAAO,EAAE,+BAA+B;iBAC3C;aACJ,CAAC,CAAC;YACH,OAAO;QACX,CAAC;QAED,+EAA+E;QAC/E,IAAI,EAAE,CAAC;IACX,CAAC,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SBP Blackboard - Core State Management
|
|
3
|
+
*/
|
|
4
|
+
import type { PheromoneStore } from "./store.js";
|
|
5
|
+
import type { DecayModel, EmitParams, EmitResult, SniffParams, SniffResult, RegisterScentParams, RegisterScentResult, DeregisterScentParams, DeregisterScentResult, EvaporateParams, EvaporateResult, InspectParams, InspectResult, TriggerPayload } from "./types.js";
|
|
6
|
+
export interface BlackboardOptions {
|
|
7
|
+
/** Interval for scent evaluation in ms (default: 100) */
|
|
8
|
+
evaluationInterval?: number;
|
|
9
|
+
/** Default decay model for new pheromones */
|
|
10
|
+
defaultDecay?: DecayModel;
|
|
11
|
+
/** Default TTL floor for evaporation */
|
|
12
|
+
defaultTtlFloor?: number;
|
|
13
|
+
/** Maximum pheromones before GC triggers */
|
|
14
|
+
maxPheromones?: number;
|
|
15
|
+
/** Enable emission history tracking for rate conditions */
|
|
16
|
+
trackEmissionHistory?: boolean;
|
|
17
|
+
/** How long to keep emission history (ms) */
|
|
18
|
+
emissionHistoryWindow?: number;
|
|
19
|
+
/** Pluggable pheromone storage backend (default: MemoryStore) */
|
|
20
|
+
store?: PheromoneStore;
|
|
21
|
+
}
|
|
22
|
+
export interface TriggerHandler {
|
|
23
|
+
(payload: TriggerPayload): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
export declare class Blackboard {
|
|
26
|
+
private store;
|
|
27
|
+
private scents;
|
|
28
|
+
private triggerHandlers;
|
|
29
|
+
private emissionHistory;
|
|
30
|
+
private evaluationTimer;
|
|
31
|
+
private startTime;
|
|
32
|
+
private options;
|
|
33
|
+
constructor(options?: BlackboardOptions);
|
|
34
|
+
emit(params: EmitParams): EmitResult;
|
|
35
|
+
sniff(params?: SniffParams): SniffResult;
|
|
36
|
+
registerScent(params: RegisterScentParams): RegisterScentResult;
|
|
37
|
+
deregisterScent(params: DeregisterScentParams): DeregisterScentResult;
|
|
38
|
+
evaporate(params?: EvaporateParams): EvaporateResult;
|
|
39
|
+
inspect(params?: InspectParams): InspectResult;
|
|
40
|
+
/**
|
|
41
|
+
* Register a local handler for a scent
|
|
42
|
+
*/
|
|
43
|
+
onTrigger(scentId: string, handler: TriggerHandler): void;
|
|
44
|
+
/**
|
|
45
|
+
* Remove a local trigger handler
|
|
46
|
+
*/
|
|
47
|
+
offTrigger(scentId: string): void;
|
|
48
|
+
/**
|
|
49
|
+
* Start the background scent evaluation loop
|
|
50
|
+
*/
|
|
51
|
+
start(): void;
|
|
52
|
+
/**
|
|
53
|
+
* Stop the background evaluation loop
|
|
54
|
+
*/
|
|
55
|
+
stop(): void;
|
|
56
|
+
/**
|
|
57
|
+
* Evaluate all scents and trigger as needed
|
|
58
|
+
*/
|
|
59
|
+
evaluateScents(): Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* Determine if a scent should trigger based on mode
|
|
62
|
+
*/
|
|
63
|
+
private shouldTrigger;
|
|
64
|
+
/**
|
|
65
|
+
* Dispatch a trigger to the agent
|
|
66
|
+
*/
|
|
67
|
+
private dispatchTrigger;
|
|
68
|
+
/**
|
|
69
|
+
* Garbage collect evaporated pheromones
|
|
70
|
+
*/
|
|
71
|
+
gc(): number;
|
|
72
|
+
/**
|
|
73
|
+
* Get raw pheromone count (for testing)
|
|
74
|
+
*/
|
|
75
|
+
get size(): number;
|
|
76
|
+
/**
|
|
77
|
+
* Get scent count
|
|
78
|
+
*/
|
|
79
|
+
get scentCount(): number;
|
|
80
|
+
private hashPayload;
|
|
81
|
+
private matchTags;
|
|
82
|
+
private pruneEmissionHistory;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=blackboard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blackboard.d.ts","sourceRoot":"","sources":["../src/blackboard.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,OAAO,KAAK,EAIV,UAAU,EACV,UAAU,EACV,UAAU,EACV,WAAW,EACX,WAAW,EAEX,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,EACrB,eAAe,EACf,eAAe,EACf,aAAa,EACb,aAAa,EACb,cAAc,EAEf,MAAM,YAAY,CAAC;AAKpB,MAAM,WAAW,iBAAiB;IAChC,yDAAyD;IACzD,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,6CAA6C;IAC7C,YAAY,CAAC,EAAE,UAAU,CAAC;IAC1B,wCAAwC;IACxC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,4CAA4C;IAC5C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2DAA2D;IAC3D,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,6CAA6C;IAC7C,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,iEAAiE;IACjE,KAAK,CAAC,EAAE,cAAc,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,eAAe,CAAqC;IAC5D,OAAO,CAAC,eAAe,CAAiE;IACxF,OAAO,CAAC,eAAe,CAA+C;IACtE,OAAO,CAAC,SAAS,CAAc;IAE/B,OAAO,CAAC,OAAO,CAA6C;gBAEhD,OAAO,GAAE,iBAAsB;IAgB3C,IAAI,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU;IAoHpC,KAAK,CAAC,MAAM,GAAE,WAAgB,GAAG,WAAW;IAwE5C,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,mBAAmB;IAoD/D,eAAe,CAAC,MAAM,EAAE,qBAAqB,GAAG,qBAAqB;IAgBrE,SAAS,CAAC,MAAM,GAAE,eAAoB,GAAG,eAAe;IAgCxD,OAAO,CAAC,MAAM,GAAE,aAAkB,GAAG,aAAa;IA0DlD;;OAEG;IACH,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,IAAI;IAIzD;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAQjC;;OAEG;IACH,KAAK,IAAI,IAAI;IAUb;;OAEG;IACH,IAAI,IAAI,IAAI;IAOZ;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IA0BrC;;OAEG;IACH,OAAO,CAAC,aAAa;IAgBrB;;OAEG;YACW,eAAe;IA6E7B;;OAEG;IACH,EAAE,IAAI,MAAM;IAiBZ;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;OAEG;IACH,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,SAAS;IAajB,OAAO,CAAC,oBAAoB;CAI7B"}
|