baselineos 0.2.0-beta.1
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/LICENSE +17 -0
- package/README.md +198 -0
- package/dist/__evals__/runner.d.ts +2 -0
- package/dist/__evals__/runner.js +14687 -0
- package/dist/__evals__/runner.js.map +1 -0
- package/dist/api/server.d.ts +21 -0
- package/dist/api/server.js +1007 -0
- package/dist/api/server.js.map +1 -0
- package/dist/cli/bin.d.ts +1 -0
- package/dist/cli/bin.js +8427 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/core/agent-bus.d.ts +110 -0
- package/dist/core/agent-bus.js +242 -0
- package/dist/core/agent-bus.js.map +1 -0
- package/dist/core/cache.d.ts +66 -0
- package/dist/core/cache.js +160 -0
- package/dist/core/cache.js.map +1 -0
- package/dist/core/config.d.ts +1002 -0
- package/dist/core/config.js +429 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/indexer.d.ts +152 -0
- package/dist/core/indexer.js +481 -0
- package/dist/core/indexer.js.map +1 -0
- package/dist/core/llm-tracer.d.ts +2 -0
- package/dist/core/llm-tracer.js +241 -0
- package/dist/core/llm-tracer.js.map +1 -0
- package/dist/core/memory.d.ts +86 -0
- package/dist/core/memory.js +346 -0
- package/dist/core/memory.js.map +1 -0
- package/dist/core/opa-client.d.ts +51 -0
- package/dist/core/opa-client.js +157 -0
- package/dist/core/opa-client.js.map +1 -0
- package/dist/core/opa-policy-gate.d.ts +133 -0
- package/dist/core/opa-policy-gate.js +454 -0
- package/dist/core/opa-policy-gate.js.map +1 -0
- package/dist/core/orchestrator.d.ts +14 -0
- package/dist/core/orchestrator.js +1297 -0
- package/dist/core/orchestrator.js.map +1 -0
- package/dist/core/pii-detector.d.ts +82 -0
- package/dist/core/pii-detector.js +126 -0
- package/dist/core/pii-detector.js.map +1 -0
- package/dist/core/rag-engine.d.ts +121 -0
- package/dist/core/rag-engine.js +504 -0
- package/dist/core/rag-engine.js.map +1 -0
- package/dist/core/task-queue.d.ts +69 -0
- package/dist/core/task-queue.js +124 -0
- package/dist/core/task-queue.js.map +1 -0
- package/dist/core/telemetry.d.ts +56 -0
- package/dist/core/telemetry.js +94 -0
- package/dist/core/telemetry.js.map +1 -0
- package/dist/core/types.d.ts +328 -0
- package/dist/core/types.js +24 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +12444 -0
- package/dist/index.js.map +1 -0
- package/dist/llm-tracer-CIIujuO-.d.ts +493 -0
- package/dist/mcp/server.d.ts +2651 -0
- package/dist/mcp/server.js +676 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/orchestrator-DF89k_AK.d.ts +506 -0
- package/package.json +157 -0
- package/templates/README.md +7 -0
- package/templates/baseline.config.ts +207 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import { existsSync, mkdirSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import Database from 'better-sqlite3';
|
|
4
|
+
|
|
5
|
+
// src/core/memory.ts
|
|
6
|
+
var MemorySystem = class {
|
|
7
|
+
config;
|
|
8
|
+
workingMemory;
|
|
9
|
+
sessionMemory;
|
|
10
|
+
longTermMemory;
|
|
11
|
+
sharedMemory;
|
|
12
|
+
db = null;
|
|
13
|
+
initialized = false;
|
|
14
|
+
constructor(config = {}) {
|
|
15
|
+
this.config = {
|
|
16
|
+
persistPath: config.persistPath ?? ".baseline/memory"
|
|
17
|
+
};
|
|
18
|
+
this.workingMemory = /* @__PURE__ */ new Map();
|
|
19
|
+
this.sessionMemory = /* @__PURE__ */ new Map();
|
|
20
|
+
this.longTermMemory = /* @__PURE__ */ new Map();
|
|
21
|
+
this.sharedMemory = /* @__PURE__ */ new Map();
|
|
22
|
+
}
|
|
23
|
+
async initialize() {
|
|
24
|
+
if (this.initialized) return;
|
|
25
|
+
if (!existsSync(this.config.persistPath)) {
|
|
26
|
+
mkdirSync(this.config.persistPath, { recursive: true });
|
|
27
|
+
}
|
|
28
|
+
const dbPath = join(this.config.persistPath, "memory.db");
|
|
29
|
+
this.db = new Database(dbPath);
|
|
30
|
+
this.db.pragma("journal_mode = WAL");
|
|
31
|
+
this.db.pragma("synchronous = NORMAL");
|
|
32
|
+
this.db.exec(`
|
|
33
|
+
CREATE TABLE IF NOT EXISTS memory (
|
|
34
|
+
storage_key TEXT PRIMARY KEY,
|
|
35
|
+
key TEXT NOT NULL,
|
|
36
|
+
value TEXT NOT NULL,
|
|
37
|
+
scope TEXT NOT NULL,
|
|
38
|
+
session_id TEXT,
|
|
39
|
+
task_id TEXT,
|
|
40
|
+
repo TEXT,
|
|
41
|
+
persona TEXT,
|
|
42
|
+
tags TEXT DEFAULT '[]',
|
|
43
|
+
created_at INTEGER NOT NULL,
|
|
44
|
+
updated_at INTEGER NOT NULL,
|
|
45
|
+
expires_at INTEGER,
|
|
46
|
+
access_count INTEGER DEFAULT 0
|
|
47
|
+
);
|
|
48
|
+
CREATE INDEX IF NOT EXISTS idx_memory_scope ON memory(scope);
|
|
49
|
+
CREATE INDEX IF NOT EXISTS idx_memory_repo ON memory(repo);
|
|
50
|
+
CREATE INDEX IF NOT EXISTS idx_memory_tags ON memory(tags);
|
|
51
|
+
`);
|
|
52
|
+
const rows = this.db.prepare("SELECT * FROM memory").all();
|
|
53
|
+
for (const row of rows) {
|
|
54
|
+
if (row.expires_at && row.expires_at < Date.now()) {
|
|
55
|
+
this.db.prepare("DELETE FROM memory WHERE storage_key = ?").run(row.storage_key);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const entry = {
|
|
59
|
+
key: row.key,
|
|
60
|
+
value: JSON.parse(row.value),
|
|
61
|
+
scope: row.scope,
|
|
62
|
+
sessionId: row.session_id ?? void 0,
|
|
63
|
+
taskId: row.task_id ?? void 0,
|
|
64
|
+
repo: row.repo ?? void 0,
|
|
65
|
+
persona: row.persona ?? void 0,
|
|
66
|
+
tags: JSON.parse(row.tags),
|
|
67
|
+
createdAt: row.created_at,
|
|
68
|
+
updatedAt: row.updated_at,
|
|
69
|
+
expiresAt: row.expires_at ?? void 0,
|
|
70
|
+
accessCount: row.access_count
|
|
71
|
+
};
|
|
72
|
+
if (row.scope === "long-term") {
|
|
73
|
+
this.longTermMemory.set(row.storage_key, entry);
|
|
74
|
+
} else if (row.scope === "shared") {
|
|
75
|
+
this.sharedMemory.set(row.storage_key, entry);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
this.initialized = true;
|
|
79
|
+
}
|
|
80
|
+
async store(key, value, options = {}) {
|
|
81
|
+
const scope = options.scope ?? "session";
|
|
82
|
+
const now = Date.now();
|
|
83
|
+
const storageKey = this.buildStorageKey(key, options);
|
|
84
|
+
const entry = {
|
|
85
|
+
key,
|
|
86
|
+
value,
|
|
87
|
+
scope,
|
|
88
|
+
sessionId: options.sessionId,
|
|
89
|
+
taskId: options.taskId,
|
|
90
|
+
repo: options.repo,
|
|
91
|
+
persona: options.persona,
|
|
92
|
+
tags: options.tags ?? [],
|
|
93
|
+
createdAt: now,
|
|
94
|
+
updatedAt: now,
|
|
95
|
+
expiresAt: options.ttl ? now + options.ttl : void 0,
|
|
96
|
+
accessCount: 0
|
|
97
|
+
};
|
|
98
|
+
switch (scope) {
|
|
99
|
+
case "working":
|
|
100
|
+
this.workingMemory.set(storageKey, entry);
|
|
101
|
+
break;
|
|
102
|
+
case "session": {
|
|
103
|
+
const sessionId = options.sessionId ?? "default";
|
|
104
|
+
if (!this.sessionMemory.has(sessionId)) {
|
|
105
|
+
this.sessionMemory.set(sessionId, /* @__PURE__ */ new Map());
|
|
106
|
+
}
|
|
107
|
+
this.sessionMemory.get(sessionId).set(storageKey, entry);
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
case "long-term":
|
|
111
|
+
this.longTermMemory.set(storageKey, entry);
|
|
112
|
+
this.persistEntry(storageKey, entry);
|
|
113
|
+
break;
|
|
114
|
+
case "shared":
|
|
115
|
+
this.sharedMemory.set(storageKey, entry);
|
|
116
|
+
this.persistEntry(storageKey, entry);
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
async retrieve(key, options = {}) {
|
|
121
|
+
const scope = options.scope ?? "all";
|
|
122
|
+
const sessionId = options.sessionId ?? "default";
|
|
123
|
+
const storageKey = this.buildStorageKey(key, options);
|
|
124
|
+
const scopes = scope === "all" ? ["working", "session", "long-term", "shared"] : [scope];
|
|
125
|
+
for (const s of scopes) {
|
|
126
|
+
const entry = this.getFromScope(storageKey, s, sessionId);
|
|
127
|
+
if (entry) {
|
|
128
|
+
if (!this.matchesContext(entry, options)) {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (entry.expiresAt && entry.expiresAt < Date.now()) {
|
|
132
|
+
await this.delete(key, { scope: s, sessionId, taskId: options.taskId, repo: options.repo, persona: options.persona });
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
entry.accessCount++;
|
|
136
|
+
return entry.value;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return void 0;
|
|
140
|
+
}
|
|
141
|
+
async delete(key, options = {}) {
|
|
142
|
+
const scope = options.scope ?? "all";
|
|
143
|
+
const sessionId = options.sessionId ?? "default";
|
|
144
|
+
const storageKey = this.buildStorageKey(key, options);
|
|
145
|
+
let deleted = false;
|
|
146
|
+
if (scope === "all" || scope === "working") {
|
|
147
|
+
deleted = this.workingMemory.delete(storageKey) || deleted;
|
|
148
|
+
}
|
|
149
|
+
if (scope === "all" || scope === "session") {
|
|
150
|
+
const session = this.sessionMemory.get(sessionId);
|
|
151
|
+
if (session) {
|
|
152
|
+
deleted = session.delete(storageKey) || deleted;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (scope === "all" || scope === "long-term") {
|
|
156
|
+
deleted = this.longTermMemory.delete(storageKey) || deleted;
|
|
157
|
+
}
|
|
158
|
+
if (scope === "all" || scope === "shared") {
|
|
159
|
+
deleted = this.sharedMemory.delete(storageKey) || deleted;
|
|
160
|
+
}
|
|
161
|
+
return deleted;
|
|
162
|
+
}
|
|
163
|
+
async search(query) {
|
|
164
|
+
const results = [];
|
|
165
|
+
const sessionId = query.sessionId ?? "default";
|
|
166
|
+
const checkEntry = (entry) => {
|
|
167
|
+
if (query.prefix && !entry.key.startsWith(query.prefix)) {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
if (query.tags && !query.tags.every((t) => entry.tags.includes(t))) {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
if (query.taskId && entry.taskId !== query.taskId) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
if (query.repo && entry.repo !== query.repo) {
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
if (query.persona && entry.persona !== query.persona) {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
if (entry.expiresAt && entry.expiresAt < Date.now()) {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
return true;
|
|
186
|
+
};
|
|
187
|
+
const scope = query.scope;
|
|
188
|
+
if (!scope || scope === "working") {
|
|
189
|
+
for (const entry of this.workingMemory.values()) {
|
|
190
|
+
if (checkEntry(entry)) results.push(entry);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (!scope || scope === "session") {
|
|
194
|
+
const session = this.sessionMemory.get(sessionId);
|
|
195
|
+
if (session) {
|
|
196
|
+
for (const entry of session.values()) {
|
|
197
|
+
if (checkEntry(entry)) results.push(entry);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (!scope || scope === "long-term") {
|
|
202
|
+
for (const entry of this.longTermMemory.values()) {
|
|
203
|
+
if (checkEntry(entry)) results.push(entry);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (!scope || scope === "shared") {
|
|
207
|
+
for (const entry of this.sharedMemory.values()) {
|
|
208
|
+
if (checkEntry(entry)) results.push(entry);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return results;
|
|
212
|
+
}
|
|
213
|
+
clearWorking() {
|
|
214
|
+
this.workingMemory.clear();
|
|
215
|
+
}
|
|
216
|
+
clearSession(sessionId = "default") {
|
|
217
|
+
this.sessionMemory.delete(sessionId);
|
|
218
|
+
}
|
|
219
|
+
async flush() {
|
|
220
|
+
if (!this.db) return;
|
|
221
|
+
const upsert = this.db.prepare(`
|
|
222
|
+
INSERT OR REPLACE INTO memory
|
|
223
|
+
(storage_key, key, value, scope, session_id, task_id, repo, persona, tags, created_at, updated_at, expires_at, access_count)
|
|
224
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
225
|
+
`);
|
|
226
|
+
const transaction = this.db.transaction(() => {
|
|
227
|
+
for (const [storageKey, entry] of this.longTermMemory) {
|
|
228
|
+
upsert.run(
|
|
229
|
+
storageKey,
|
|
230
|
+
entry.key,
|
|
231
|
+
JSON.stringify(entry.value),
|
|
232
|
+
entry.scope,
|
|
233
|
+
entry.sessionId ?? null,
|
|
234
|
+
entry.taskId ?? null,
|
|
235
|
+
entry.repo ?? null,
|
|
236
|
+
entry.persona ?? null,
|
|
237
|
+
JSON.stringify(entry.tags),
|
|
238
|
+
entry.createdAt,
|
|
239
|
+
entry.updatedAt,
|
|
240
|
+
entry.expiresAt ?? null,
|
|
241
|
+
entry.accessCount
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
for (const [storageKey, entry] of this.sharedMemory) {
|
|
245
|
+
upsert.run(
|
|
246
|
+
storageKey,
|
|
247
|
+
entry.key,
|
|
248
|
+
JSON.stringify(entry.value),
|
|
249
|
+
entry.scope,
|
|
250
|
+
entry.sessionId ?? null,
|
|
251
|
+
entry.taskId ?? null,
|
|
252
|
+
entry.repo ?? null,
|
|
253
|
+
entry.persona ?? null,
|
|
254
|
+
JSON.stringify(entry.tags),
|
|
255
|
+
entry.createdAt,
|
|
256
|
+
entry.updatedAt,
|
|
257
|
+
entry.expiresAt ?? null,
|
|
258
|
+
entry.accessCount
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
transaction();
|
|
263
|
+
}
|
|
264
|
+
close() {
|
|
265
|
+
if (this.db) {
|
|
266
|
+
this.db.close();
|
|
267
|
+
this.db = null;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
persistEntry(storageKey, entry) {
|
|
271
|
+
if (!this.db) return;
|
|
272
|
+
this.db.prepare(`
|
|
273
|
+
INSERT OR REPLACE INTO memory
|
|
274
|
+
(storage_key, key, value, scope, session_id, task_id, repo, persona, tags, created_at, updated_at, expires_at, access_count)
|
|
275
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
276
|
+
`).run(
|
|
277
|
+
storageKey,
|
|
278
|
+
entry.key,
|
|
279
|
+
JSON.stringify(entry.value),
|
|
280
|
+
entry.scope,
|
|
281
|
+
entry.sessionId ?? null,
|
|
282
|
+
entry.taskId ?? null,
|
|
283
|
+
entry.repo ?? null,
|
|
284
|
+
entry.persona ?? null,
|
|
285
|
+
JSON.stringify(entry.tags),
|
|
286
|
+
entry.createdAt,
|
|
287
|
+
entry.updatedAt,
|
|
288
|
+
entry.expiresAt ?? null,
|
|
289
|
+
entry.accessCount
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
getFromScope(key, scope, sessionId) {
|
|
293
|
+
switch (scope) {
|
|
294
|
+
case "working":
|
|
295
|
+
return this.workingMemory.get(key);
|
|
296
|
+
case "session":
|
|
297
|
+
return this.sessionMemory.get(sessionId)?.get(key);
|
|
298
|
+
case "long-term":
|
|
299
|
+
return this.longTermMemory.get(key);
|
|
300
|
+
case "shared":
|
|
301
|
+
return this.sharedMemory.get(key);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
buildStorageKey(key, options) {
|
|
305
|
+
const parts = [key];
|
|
306
|
+
if (options.taskId) parts.push(`task:${options.taskId}`);
|
|
307
|
+
if (options.repo) parts.push(`repo:${options.repo}`);
|
|
308
|
+
if (options.persona) parts.push(`persona:${options.persona}`);
|
|
309
|
+
return parts.join("|");
|
|
310
|
+
}
|
|
311
|
+
matchesContext(entry, options) {
|
|
312
|
+
if (options.taskId && entry.taskId !== options.taskId) return false;
|
|
313
|
+
if (options.repo && entry.repo !== options.repo) return false;
|
|
314
|
+
if (options.persona && entry.persona !== options.persona) return false;
|
|
315
|
+
return true;
|
|
316
|
+
}
|
|
317
|
+
getStats() {
|
|
318
|
+
let sessionCount = 0;
|
|
319
|
+
for (const session of this.sessionMemory.values()) {
|
|
320
|
+
sessionCount += session.size;
|
|
321
|
+
}
|
|
322
|
+
return {
|
|
323
|
+
working: this.workingMemory.size,
|
|
324
|
+
session: sessionCount,
|
|
325
|
+
longTerm: this.longTermMemory.size,
|
|
326
|
+
shared: this.sharedMemory.size
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
/**
|
|
331
|
+
* BaselineOS Memory System
|
|
332
|
+
*
|
|
333
|
+
* Multi-scope persistent memory for AI agents.
|
|
334
|
+
*
|
|
335
|
+
* Scopes:
|
|
336
|
+
* - Working: Per-request, cleared after each call (fastest)
|
|
337
|
+
* - Session: Per-conversation, cleared on disconnect
|
|
338
|
+
* - Long-term: Persists across sessions (SQLite)
|
|
339
|
+
* - Shared: Visible across all agents and sessions
|
|
340
|
+
*
|
|
341
|
+
* @license Apache-2.0
|
|
342
|
+
*/
|
|
343
|
+
|
|
344
|
+
export { MemorySystem };
|
|
345
|
+
//# sourceMappingURL=memory.js.map
|
|
346
|
+
//# sourceMappingURL=memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/core/memory.ts"],"names":[],"mappings":"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","file":"memory.js","sourcesContent":["/**\n * BaselineOS Memory System\n * \n * Multi-scope persistent memory for AI agents.\n * \n * Scopes:\n * - Working: Per-request, cleared after each call (fastest)\n * - Session: Per-conversation, cleared on disconnect\n * - Long-term: Persists across sessions (SQLite)\n * - Shared: Visible across all agents and sessions\n * \n * @license Apache-2.0\n */\n\nimport { mkdirSync, existsSync } from 'fs';\nimport { join } from 'path';\nimport Database from 'better-sqlite3';\n\nexport type MemoryScope = 'working' | 'session' | 'long-term' | 'shared';\n\nexport interface MemoryConfig {\n persistPath: string;\n}\n\nexport interface StoreOptions {\n scope?: MemoryScope;\n sessionId?: string;\n ttl?: number;\n tags?: string[];\n taskId?: string;\n repo?: string;\n persona?: string;\n}\n\nexport interface RetrieveOptions {\n scope?: MemoryScope | 'all';\n sessionId?: string;\n taskId?: string;\n repo?: string;\n persona?: string;\n}\n\nexport interface MemoryEntry {\n key: string;\n value: unknown;\n scope: MemoryScope;\n sessionId?: string;\n taskId?: string;\n repo?: string;\n persona?: string;\n tags: string[];\n createdAt: number;\n updatedAt: number;\n expiresAt?: number;\n accessCount: number;\n}\n\nexport class MemorySystem {\n private config: MemoryConfig;\n private workingMemory: Map<string, MemoryEntry>;\n private sessionMemory: Map<string, Map<string, MemoryEntry>>;\n private longTermMemory: Map<string, MemoryEntry>;\n private sharedMemory: Map<string, MemoryEntry>;\n private db: InstanceType<typeof Database> | null = null;\n private initialized: boolean = false;\n\n constructor(config: Partial<MemoryConfig> = {}) {\n this.config = {\n persistPath: config.persistPath ?? '.baseline/memory',\n };\n this.workingMemory = new Map();\n this.sessionMemory = new Map();\n this.longTermMemory = new Map();\n this.sharedMemory = new Map();\n }\n\n async initialize(): Promise<void> {\n if (this.initialized) return;\n\n // Ensure persistence directory exists\n if (!existsSync(this.config.persistPath)) {\n mkdirSync(this.config.persistPath, { recursive: true });\n }\n\n // Initialize SQLite for long-term and shared memory\n const dbPath = join(this.config.persistPath, 'memory.db');\n this.db = new Database(dbPath);\n this.db.pragma('journal_mode = WAL');\n this.db.pragma('synchronous = NORMAL');\n\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS memory (\n storage_key TEXT PRIMARY KEY,\n key TEXT NOT NULL,\n value TEXT NOT NULL,\n scope TEXT NOT NULL,\n session_id TEXT,\n task_id TEXT,\n repo TEXT,\n persona TEXT,\n tags TEXT DEFAULT '[]',\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n expires_at INTEGER,\n access_count INTEGER DEFAULT 0\n );\n CREATE INDEX IF NOT EXISTS idx_memory_scope ON memory(scope);\n CREATE INDEX IF NOT EXISTS idx_memory_repo ON memory(repo);\n CREATE INDEX IF NOT EXISTS idx_memory_tags ON memory(tags);\n `);\n\n // Load persisted entries into memory maps\n const rows = this.db.prepare('SELECT * FROM memory').all() as Array<{\n storage_key: string;\n key: string;\n value: string;\n scope: string;\n session_id: string | null;\n task_id: string | null;\n repo: string | null;\n persona: string | null;\n tags: string;\n created_at: number;\n updated_at: number;\n expires_at: number | null;\n access_count: number;\n }>;\n\n for (const row of rows) {\n // Skip expired entries\n if (row.expires_at && row.expires_at < Date.now()) {\n this.db.prepare('DELETE FROM memory WHERE storage_key = ?').run(row.storage_key);\n continue;\n }\n\n const entry: MemoryEntry = {\n key: row.key,\n value: JSON.parse(row.value),\n scope: row.scope as MemoryScope,\n sessionId: row.session_id ?? undefined,\n taskId: row.task_id ?? undefined,\n repo: row.repo ?? undefined,\n persona: row.persona ?? undefined,\n tags: JSON.parse(row.tags),\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n expiresAt: row.expires_at ?? undefined,\n accessCount: row.access_count,\n };\n\n if (row.scope === 'long-term') {\n this.longTermMemory.set(row.storage_key, entry);\n } else if (row.scope === 'shared') {\n this.sharedMemory.set(row.storage_key, entry);\n }\n }\n\n this.initialized = true;\n }\n\n async store(key: string, value: unknown, options: StoreOptions = {}): Promise<void> {\n const scope = options.scope ?? 'session';\n const now = Date.now();\n const storageKey = this.buildStorageKey(key, options);\n \n const entry: MemoryEntry = {\n key,\n value,\n scope,\n sessionId: options.sessionId,\n taskId: options.taskId,\n repo: options.repo,\n persona: options.persona,\n tags: options.tags ?? [],\n createdAt: now,\n updatedAt: now,\n expiresAt: options.ttl ? now + options.ttl : undefined,\n accessCount: 0,\n };\n \n switch (scope) {\n case 'working':\n this.workingMemory.set(storageKey, entry);\n break;\n \n case 'session': {\n const sessionId = options.sessionId ?? 'default';\n if (!this.sessionMemory.has(sessionId)) {\n this.sessionMemory.set(sessionId, new Map());\n }\n this.sessionMemory.get(sessionId)!.set(storageKey, entry);\n break;\n }\n \n case 'long-term':\n this.longTermMemory.set(storageKey, entry);\n this.persistEntry(storageKey, entry);\n break;\n\n case 'shared':\n this.sharedMemory.set(storageKey, entry);\n this.persistEntry(storageKey, entry);\n break;\n }\n }\n\n async retrieve(key: string, options: RetrieveOptions = {}): Promise<unknown> {\n const scope = options.scope ?? 'all';\n const sessionId = options.sessionId ?? 'default';\n const storageKey = this.buildStorageKey(key, options);\n \n // Check each scope in order of priority\n const scopes = scope === 'all' \n ? ['working', 'session', 'long-term', 'shared'] as MemoryScope[]\n : [scope] as MemoryScope[];\n \n for (const s of scopes) {\n const entry = this.getFromScope(storageKey, s, sessionId);\n if (entry) {\n if (!this.matchesContext(entry, options)) {\n continue;\n }\n // Check expiration\n if (entry.expiresAt && entry.expiresAt < Date.now()) {\n await this.delete(key, { scope: s, sessionId, taskId: options.taskId, repo: options.repo, persona: options.persona });\n continue;\n }\n \n // Update access count\n entry.accessCount++;\n \n return entry.value;\n }\n }\n \n return undefined;\n }\n\n async delete(key: string, options: RetrieveOptions = {}): Promise<boolean> {\n const scope = options.scope ?? 'all';\n const sessionId = options.sessionId ?? 'default';\n const storageKey = this.buildStorageKey(key, options);\n \n let deleted = false;\n \n if (scope === 'all' || scope === 'working') {\n deleted = this.workingMemory.delete(storageKey) || deleted;\n }\n \n if (scope === 'all' || scope === 'session') {\n const session = this.sessionMemory.get(sessionId);\n if (session) {\n deleted = session.delete(storageKey) || deleted;\n }\n }\n \n if (scope === 'all' || scope === 'long-term') {\n deleted = this.longTermMemory.delete(storageKey) || deleted;\n }\n \n if (scope === 'all' || scope === 'shared') {\n deleted = this.sharedMemory.delete(storageKey) || deleted;\n }\n \n return deleted;\n }\n\n async search(query: { \n tags?: string[]; \n prefix?: string; \n scope?: MemoryScope;\n sessionId?: string;\n taskId?: string;\n repo?: string;\n persona?: string;\n }): Promise<MemoryEntry[]> {\n const results: MemoryEntry[] = [];\n const sessionId = query.sessionId ?? 'default';\n \n const checkEntry = (entry: MemoryEntry) => {\n if (query.prefix && !entry.key.startsWith(query.prefix)) {\n return false;\n }\n if (query.tags && !query.tags.every(t => entry.tags.includes(t))) {\n return false;\n }\n if (query.taskId && entry.taskId !== query.taskId) {\n return false;\n }\n if (query.repo && entry.repo !== query.repo) {\n return false;\n }\n if (query.persona && entry.persona !== query.persona) {\n return false;\n }\n if (entry.expiresAt && entry.expiresAt < Date.now()) {\n return false;\n }\n return true;\n };\n \n const scope = query.scope;\n \n if (!scope || scope === 'working') {\n for (const entry of this.workingMemory.values()) {\n if (checkEntry(entry)) results.push(entry);\n }\n }\n \n if (!scope || scope === 'session') {\n const session = this.sessionMemory.get(sessionId);\n if (session) {\n for (const entry of session.values()) {\n if (checkEntry(entry)) results.push(entry);\n }\n }\n }\n \n if (!scope || scope === 'long-term') {\n for (const entry of this.longTermMemory.values()) {\n if (checkEntry(entry)) results.push(entry);\n }\n }\n \n if (!scope || scope === 'shared') {\n for (const entry of this.sharedMemory.values()) {\n if (checkEntry(entry)) results.push(entry);\n }\n }\n \n return results;\n }\n\n clearWorking(): void {\n this.workingMemory.clear();\n }\n\n clearSession(sessionId: string = 'default'): void {\n this.sessionMemory.delete(sessionId);\n }\n\n async flush(): Promise<void> {\n if (!this.db) return;\n\n const upsert = this.db.prepare(`\n INSERT OR REPLACE INTO memory\n (storage_key, key, value, scope, session_id, task_id, repo, persona, tags, created_at, updated_at, expires_at, access_count)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n\n const transaction = this.db.transaction(() => {\n for (const [storageKey, entry] of this.longTermMemory) {\n upsert.run(\n storageKey, entry.key, JSON.stringify(entry.value), entry.scope,\n entry.sessionId ?? null, entry.taskId ?? null, entry.repo ?? null, entry.persona ?? null,\n JSON.stringify(entry.tags), entry.createdAt, entry.updatedAt, entry.expiresAt ?? null, entry.accessCount\n );\n }\n for (const [storageKey, entry] of this.sharedMemory) {\n upsert.run(\n storageKey, entry.key, JSON.stringify(entry.value), entry.scope,\n entry.sessionId ?? null, entry.taskId ?? null, entry.repo ?? null, entry.persona ?? null,\n JSON.stringify(entry.tags), entry.createdAt, entry.updatedAt, entry.expiresAt ?? null, entry.accessCount\n );\n }\n });\n\n transaction();\n }\n\n close(): void {\n if (this.db) {\n this.db.close();\n this.db = null;\n }\n }\n\n private persistEntry(storageKey: string, entry: MemoryEntry): void {\n if (!this.db) return;\n\n this.db.prepare(`\n INSERT OR REPLACE INTO memory\n (storage_key, key, value, scope, session_id, task_id, repo, persona, tags, created_at, updated_at, expires_at, access_count)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `).run(\n storageKey, entry.key, JSON.stringify(entry.value), entry.scope,\n entry.sessionId ?? null, entry.taskId ?? null, entry.repo ?? null, entry.persona ?? null,\n JSON.stringify(entry.tags), entry.createdAt, entry.updatedAt, entry.expiresAt ?? null, entry.accessCount\n );\n }\n\n private getFromScope(key: string, scope: MemoryScope, sessionId: string): MemoryEntry | undefined {\n switch (scope) {\n case 'working':\n return this.workingMemory.get(key);\n case 'session':\n return this.sessionMemory.get(sessionId)?.get(key);\n case 'long-term':\n return this.longTermMemory.get(key);\n case 'shared':\n return this.sharedMemory.get(key);\n }\n }\n\n private buildStorageKey(\n key: string,\n options: { taskId?: string; repo?: string; persona?: string }\n ): string {\n const parts = [key];\n if (options.taskId) parts.push(`task:${options.taskId}`);\n if (options.repo) parts.push(`repo:${options.repo}`);\n if (options.persona) parts.push(`persona:${options.persona}`);\n return parts.join('|');\n }\n\n private matchesContext(entry: MemoryEntry, options: RetrieveOptions): boolean {\n if (options.taskId && entry.taskId !== options.taskId) return false;\n if (options.repo && entry.repo !== options.repo) return false;\n if (options.persona && entry.persona !== options.persona) return false;\n return true;\n }\n\n getStats() {\n let sessionCount = 0;\n for (const session of this.sessionMemory.values()) {\n sessionCount += session.size;\n }\n \n return {\n working: this.workingMemory.size,\n session: sessionCount,\n longTerm: this.longTermMemory.size,\n shared: this.sharedMemory.size,\n };\n }\n}\n"]}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OPA Client — SIGNAL-004
|
|
3
|
+
*
|
|
4
|
+
* Lightweight HTTP client for the Open Policy Agent REST API.
|
|
5
|
+
* Zero external dependencies — uses native fetch (Node 18+).
|
|
6
|
+
*
|
|
7
|
+
* Fail-open by default: if OPA is unreachable, evaluation returns
|
|
8
|
+
* the provided fallback value and emits a warning. This prevents
|
|
9
|
+
* OPA downtime from blocking agent execution.
|
|
10
|
+
*
|
|
11
|
+
* @license Apache-2.0
|
|
12
|
+
*/
|
|
13
|
+
interface OPAInput {
|
|
14
|
+
[key: string]: unknown;
|
|
15
|
+
}
|
|
16
|
+
interface OPAEvaluationResult<T = unknown> {
|
|
17
|
+
/** Policy decision result */
|
|
18
|
+
result: T;
|
|
19
|
+
/** OPA decision ID (present when OPA decision logging is enabled) */
|
|
20
|
+
decisionId?: string;
|
|
21
|
+
/** Whether this result came from the fallback (OPA was unreachable) */
|
|
22
|
+
fallback: boolean;
|
|
23
|
+
/** Evaluation latency in ms */
|
|
24
|
+
latencyMs: number;
|
|
25
|
+
}
|
|
26
|
+
interface OPAClientConfig {
|
|
27
|
+
/** OPA base URL. Defaults to BASELINE_OPA_URL env var or http://localhost:8181 */
|
|
28
|
+
url?: string;
|
|
29
|
+
/** Request timeout in ms. Default: 2000 */
|
|
30
|
+
timeoutMs?: number;
|
|
31
|
+
}
|
|
32
|
+
declare class OPAClient {
|
|
33
|
+
private readonly baseUrl;
|
|
34
|
+
private readonly timeoutMs;
|
|
35
|
+
constructor(config?: OPAClientConfig);
|
|
36
|
+
/**
|
|
37
|
+
* Evaluate a policy rule against an input document.
|
|
38
|
+
*
|
|
39
|
+
* @param policyPath Slash-separated path, e.g. 'baseline/task_allowed'
|
|
40
|
+
* @param input Arbitrary input document
|
|
41
|
+
* @param fallback Value to return if OPA is unreachable (default: true — fail-open)
|
|
42
|
+
*/
|
|
43
|
+
evaluate<T = boolean>(policyPath: string, input: OPAInput, fallback?: T): Promise<OPAEvaluationResult<T>>;
|
|
44
|
+
/**
|
|
45
|
+
* Check if OPA is healthy and reachable.
|
|
46
|
+
*/
|
|
47
|
+
healthCheck(): Promise<boolean>;
|
|
48
|
+
get url(): string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export { OPAClient, type OPAClientConfig, type OPAEvaluationResult, type OPAInput };
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __esm = (fn, res) => function __init() {
|
|
4
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
|
+
};
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// src/core/opa-local-fallback.ts
|
|
12
|
+
var opa_local_fallback_exports = {};
|
|
13
|
+
__export(opa_local_fallback_exports, {
|
|
14
|
+
evaluateAgentTrusted: () => evaluateAgentTrusted,
|
|
15
|
+
evaluateLocally: () => evaluateLocally,
|
|
16
|
+
evaluateOutputSafe: () => evaluateOutputSafe,
|
|
17
|
+
evaluateTaskAllowed: () => evaluateTaskAllowed
|
|
18
|
+
});
|
|
19
|
+
function evaluateTaskAllowed(input) {
|
|
20
|
+
if (input.trust_score < 50) return false;
|
|
21
|
+
if (input.complexity === "epic" && input.trust_score < 85) return false;
|
|
22
|
+
if (input.complexity === "complex" && input.trust_score < 70) return false;
|
|
23
|
+
if (input.priority === "critical" && input.trust_score < 70) return false;
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
function evaluateAgentTrusted(input) {
|
|
27
|
+
if (input.role === "supervisor" && input.trust_score < 70) return false;
|
|
28
|
+
if (input.role === "quality" && input.trust_score < 80) return false;
|
|
29
|
+
return input.trust_score >= 50;
|
|
30
|
+
}
|
|
31
|
+
function evaluateOutputSafe(input) {
|
|
32
|
+
if (input.pii_detected) return false;
|
|
33
|
+
if (input.schema_valid === false) return false;
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
function evaluateLocally(policyPath, input) {
|
|
37
|
+
const evaluator = LOCAL_EVALUATORS[policyPath];
|
|
38
|
+
if (!evaluator) return void 0;
|
|
39
|
+
try {
|
|
40
|
+
return evaluator(input);
|
|
41
|
+
} catch {
|
|
42
|
+
return void 0;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
var LOCAL_EVALUATORS;
|
|
46
|
+
var init_opa_local_fallback = __esm({
|
|
47
|
+
"src/core/opa-local-fallback.ts"() {
|
|
48
|
+
LOCAL_EVALUATORS = {
|
|
49
|
+
"baseline/task_allowed": (input) => evaluateTaskAllowed(input),
|
|
50
|
+
"baseline/agent_trusted": (input) => evaluateAgentTrusted(input),
|
|
51
|
+
"baseline/output_safe": (input) => evaluateOutputSafe(input)
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// src/core/opa-client.ts
|
|
57
|
+
var OPAClient = class {
|
|
58
|
+
baseUrl;
|
|
59
|
+
timeoutMs;
|
|
60
|
+
constructor(config = {}) {
|
|
61
|
+
this.baseUrl = (config.url ?? process.env["BASELINE_OPA_URL"] ?? "http://localhost:8181").replace(/\/$/, "");
|
|
62
|
+
this.timeoutMs = config.timeoutMs ?? 2e3;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Evaluate a policy rule against an input document.
|
|
66
|
+
*
|
|
67
|
+
* @param policyPath Slash-separated path, e.g. 'baseline/task_allowed'
|
|
68
|
+
* @param input Arbitrary input document
|
|
69
|
+
* @param fallback Value to return if OPA is unreachable (default: true — fail-open)
|
|
70
|
+
*/
|
|
71
|
+
async evaluate(policyPath, input, fallback = true) {
|
|
72
|
+
const start = Date.now();
|
|
73
|
+
const url = `${this.baseUrl}/v1/data/${policyPath}`;
|
|
74
|
+
try {
|
|
75
|
+
const response = await fetch(url, {
|
|
76
|
+
method: "POST",
|
|
77
|
+
headers: { "Content-Type": "application/json" },
|
|
78
|
+
body: JSON.stringify({ input }),
|
|
79
|
+
signal: AbortSignal.timeout(this.timeoutMs)
|
|
80
|
+
});
|
|
81
|
+
const latencyMs = Date.now() - start;
|
|
82
|
+
if (!response.ok) {
|
|
83
|
+
const body = await response.text().catch(() => "");
|
|
84
|
+
throw new Error(`OPA HTTP ${response.status}: ${body}`);
|
|
85
|
+
}
|
|
86
|
+
const data = await response.json();
|
|
87
|
+
return {
|
|
88
|
+
result: data.result ?? fallback,
|
|
89
|
+
decisionId: data.decision_id,
|
|
90
|
+
fallback: false,
|
|
91
|
+
latencyMs
|
|
92
|
+
};
|
|
93
|
+
} catch (err) {
|
|
94
|
+
const latencyMs = Date.now() - start;
|
|
95
|
+
const isTimeout = err instanceof Error && err.name === "TimeoutError";
|
|
96
|
+
const reason = isTimeout ? "timeout" : String(err);
|
|
97
|
+
try {
|
|
98
|
+
const { evaluateLocally: evaluateLocally2 } = await Promise.resolve().then(() => (init_opa_local_fallback(), opa_local_fallback_exports));
|
|
99
|
+
const localResult = evaluateLocally2(policyPath, input);
|
|
100
|
+
if (localResult !== void 0) {
|
|
101
|
+
console.warn(`[OPAClient] Unreachable (${reason}) \u2014 using local fallback for policy "${policyPath}"`);
|
|
102
|
+
return { result: localResult, fallback: true, latencyMs };
|
|
103
|
+
}
|
|
104
|
+
} catch {
|
|
105
|
+
}
|
|
106
|
+
console.warn(`[OPAClient] Unreachable (${reason}) \u2014 failing open for policy "${policyPath}"`);
|
|
107
|
+
return { result: fallback, fallback: true, latencyMs };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Check if OPA is healthy and reachable.
|
|
112
|
+
*/
|
|
113
|
+
async healthCheck() {
|
|
114
|
+
try {
|
|
115
|
+
const response = await fetch(`${this.baseUrl}/health`, {
|
|
116
|
+
signal: AbortSignal.timeout(1e3)
|
|
117
|
+
});
|
|
118
|
+
return response.ok;
|
|
119
|
+
} catch {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
get url() {
|
|
124
|
+
return this.baseUrl;
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
/**
|
|
128
|
+
* OPA Local Fallback — Offline Policy Evaluation
|
|
129
|
+
*
|
|
130
|
+
* TypeScript mirror of the 3 OPA .rego policies for offline enforcement.
|
|
131
|
+
* When OPA is unreachable, the OPAPolicyGate can use these evaluators
|
|
132
|
+
* instead of blindly failing open. This ensures governance primitives
|
|
133
|
+
* work without network connectivity (spec: README.md line 88).
|
|
134
|
+
*
|
|
135
|
+
* Policies mirrored:
|
|
136
|
+
* baseline/task_allowed — from policies/baseline/task.rego
|
|
137
|
+
* baseline/agent_trusted — from policies/baseline/agent.rego
|
|
138
|
+
* baseline/output_safe — from policies/baseline/output.rego
|
|
139
|
+
*
|
|
140
|
+
* @license Apache-2.0
|
|
141
|
+
*/
|
|
142
|
+
/**
|
|
143
|
+
* OPA Client — SIGNAL-004
|
|
144
|
+
*
|
|
145
|
+
* Lightweight HTTP client for the Open Policy Agent REST API.
|
|
146
|
+
* Zero external dependencies — uses native fetch (Node 18+).
|
|
147
|
+
*
|
|
148
|
+
* Fail-open by default: if OPA is unreachable, evaluation returns
|
|
149
|
+
* the provided fallback value and emits a warning. This prevents
|
|
150
|
+
* OPA downtime from blocking agent execution.
|
|
151
|
+
*
|
|
152
|
+
* @license Apache-2.0
|
|
153
|
+
*/
|
|
154
|
+
|
|
155
|
+
export { OPAClient };
|
|
156
|
+
//# sourceMappingURL=opa-client.js.map
|
|
157
|
+
//# sourceMappingURL=opa-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/core/opa-local-fallback.ts","../../src/core/opa-client.ts"],"names":["evaluateLocally"],"mappings":"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","file":"opa-client.js","sourcesContent":["/**\n * OPA Local Fallback — Offline Policy Evaluation\n *\n * TypeScript mirror of the 3 OPA .rego policies for offline enforcement.\n * When OPA is unreachable, the OPAPolicyGate can use these evaluators\n * instead of blindly failing open. This ensures governance primitives\n * work without network connectivity (spec: README.md line 88).\n *\n * Policies mirrored:\n * baseline/task_allowed — from policies/baseline/task.rego\n * baseline/agent_trusted — from policies/baseline/agent.rego\n * baseline/output_safe — from policies/baseline/output.rego\n *\n * @license Apache-2.0\n */\n\n// ─── task_allowed (mirrors task.rego) ────────────────────────────────────────\n\nexport function evaluateTaskAllowed(input: {\n trust_score: number;\n complexity: string;\n priority: string;\n}): boolean {\n // Agent suspended below 50\n if (input.trust_score < 50) return false;\n\n // Epic requires >= 85\n if (input.complexity === 'epic' && input.trust_score < 85) return false;\n\n // Complex requires >= 70\n if (input.complexity === 'complex' && input.trust_score < 70) return false;\n\n // Critical priority requires >= 70\n if (input.priority === 'critical' && input.trust_score < 70) return false;\n\n return true;\n}\n\n// ─── agent_trusted (mirrors agent.rego) ──────────────────────────────────────\n\nexport function evaluateAgentTrusted(input: {\n trust_score: number;\n role?: string;\n}): boolean {\n // Supervisors need trust >= 70\n if (input.role === 'supervisor' && input.trust_score < 70) return false;\n\n // Quality agents need trust >= 80\n if (input.role === 'quality' && input.trust_score < 80) return false;\n\n // All agents need trust >= 50\n return input.trust_score >= 50;\n}\n\n// ─── output_safe (mirrors output.rego) ───────────────────────────────────────\n\nexport function evaluateOutputSafe(input: {\n output?: string;\n pii_detected?: boolean;\n schema_valid?: boolean;\n}): boolean {\n // PII in output is never safe\n if (input.pii_detected) return false;\n\n // Schema validation failure is not safe\n if (input.schema_valid === false) return false;\n\n return true;\n}\n\n// ─── Dispatcher ──────────────────────────────────────────────────────────────\n\nconst LOCAL_EVALUATORS: Record<string, (input: Record<string, unknown>) => boolean> = {\n 'baseline/task_allowed': (input) => evaluateTaskAllowed(input as Parameters<typeof evaluateTaskAllowed>[0]),\n 'baseline/agent_trusted': (input) => evaluateAgentTrusted(input as Parameters<typeof evaluateAgentTrusted>[0]),\n 'baseline/output_safe': (input) => evaluateOutputSafe(input as Parameters<typeof evaluateOutputSafe>[0]),\n};\n\n/**\n * Evaluate a policy locally. Returns undefined if no local evaluator exists\n * for the given policy path (caller should fall back to fail-open).\n */\nexport function evaluateLocally(policyPath: string, input: Record<string, unknown>): boolean | undefined {\n const evaluator = LOCAL_EVALUATORS[policyPath];\n if (!evaluator) return undefined;\n try {\n return evaluator(input);\n } catch {\n return undefined;\n }\n}\n","/**\n * OPA Client — SIGNAL-004\n *\n * Lightweight HTTP client for the Open Policy Agent REST API.\n * Zero external dependencies — uses native fetch (Node 18+).\n *\n * Fail-open by default: if OPA is unreachable, evaluation returns\n * the provided fallback value and emits a warning. This prevents\n * OPA downtime from blocking agent execution.\n *\n * @license Apache-2.0\n */\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface OPAInput {\n [key: string]: unknown;\n}\n\nexport interface OPAEvaluationResult<T = unknown> {\n /** Policy decision result */\n result: T;\n /** OPA decision ID (present when OPA decision logging is enabled) */\n decisionId?: string;\n /** Whether this result came from the fallback (OPA was unreachable) */\n fallback: boolean;\n /** Evaluation latency in ms */\n latencyMs: number;\n}\n\nexport interface OPAClientConfig {\n /** OPA base URL. Defaults to BASELINE_OPA_URL env var or http://localhost:8181 */\n url?: string;\n /** Request timeout in ms. Default: 2000 */\n timeoutMs?: number;\n}\n\n// ─── OPAClient ────────────────────────────────────────────────────────────────\n\nexport class OPAClient {\n private readonly baseUrl: string;\n private readonly timeoutMs: number;\n\n constructor(config: OPAClientConfig = {}) {\n this.baseUrl = (\n config.url ??\n process.env['BASELINE_OPA_URL'] ??\n 'http://localhost:8181'\n ).replace(/\\/$/, '');\n this.timeoutMs = config.timeoutMs ?? 2000;\n }\n\n /**\n * Evaluate a policy rule against an input document.\n *\n * @param policyPath Slash-separated path, e.g. 'baseline/task_allowed'\n * @param input Arbitrary input document\n * @param fallback Value to return if OPA is unreachable (default: true — fail-open)\n */\n async evaluate<T = boolean>(\n policyPath: string,\n input: OPAInput,\n fallback: T = true as unknown as T,\n ): Promise<OPAEvaluationResult<T>> {\n const start = Date.now();\n const url = `${this.baseUrl}/v1/data/${policyPath}`;\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ input }),\n signal: AbortSignal.timeout(this.timeoutMs),\n });\n\n const latencyMs = Date.now() - start;\n\n if (!response.ok) {\n const body = await response.text().catch(() => '');\n throw new Error(`OPA HTTP ${response.status}: ${body}`);\n }\n\n const data = await response.json() as { result?: T; decision_id?: string };\n return {\n result: data.result ?? fallback,\n decisionId: data.decision_id,\n fallback: false,\n latencyMs,\n };\n } catch (err) {\n const latencyMs = Date.now() - start;\n const isTimeout = err instanceof Error && err.name === 'TimeoutError';\n const reason = isTimeout ? 'timeout' : String(err);\n\n // Try local policy evaluation before failing open\n try {\n const { evaluateLocally } = await import('./opa-local-fallback.js');\n const localResult = evaluateLocally(policyPath, input);\n if (localResult !== undefined) {\n console.warn(`[OPAClient] Unreachable (${reason}) — using local fallback for policy \"${policyPath}\"`);\n return { result: localResult as unknown as T, fallback: true, latencyMs };\n }\n } catch {\n // Local fallback unavailable — continue to fail-open\n }\n\n console.warn(`[OPAClient] Unreachable (${reason}) — failing open for policy \"${policyPath}\"`);\n return { result: fallback, fallback: true, latencyMs };\n }\n }\n\n /**\n * Check if OPA is healthy and reachable.\n */\n async healthCheck(): Promise<boolean> {\n try {\n const response = await fetch(`${this.baseUrl}/health`, {\n signal: AbortSignal.timeout(1000),\n });\n return response.ok;\n } catch {\n return false;\n }\n }\n\n get url(): string {\n return this.baseUrl;\n }\n}\n"]}
|