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.
Files changed (64) hide show
  1. package/LICENSE +17 -0
  2. package/README.md +198 -0
  3. package/dist/__evals__/runner.d.ts +2 -0
  4. package/dist/__evals__/runner.js +14687 -0
  5. package/dist/__evals__/runner.js.map +1 -0
  6. package/dist/api/server.d.ts +21 -0
  7. package/dist/api/server.js +1007 -0
  8. package/dist/api/server.js.map +1 -0
  9. package/dist/cli/bin.d.ts +1 -0
  10. package/dist/cli/bin.js +8427 -0
  11. package/dist/cli/bin.js.map +1 -0
  12. package/dist/core/agent-bus.d.ts +110 -0
  13. package/dist/core/agent-bus.js +242 -0
  14. package/dist/core/agent-bus.js.map +1 -0
  15. package/dist/core/cache.d.ts +66 -0
  16. package/dist/core/cache.js +160 -0
  17. package/dist/core/cache.js.map +1 -0
  18. package/dist/core/config.d.ts +1002 -0
  19. package/dist/core/config.js +429 -0
  20. package/dist/core/config.js.map +1 -0
  21. package/dist/core/indexer.d.ts +152 -0
  22. package/dist/core/indexer.js +481 -0
  23. package/dist/core/indexer.js.map +1 -0
  24. package/dist/core/llm-tracer.d.ts +2 -0
  25. package/dist/core/llm-tracer.js +241 -0
  26. package/dist/core/llm-tracer.js.map +1 -0
  27. package/dist/core/memory.d.ts +86 -0
  28. package/dist/core/memory.js +346 -0
  29. package/dist/core/memory.js.map +1 -0
  30. package/dist/core/opa-client.d.ts +51 -0
  31. package/dist/core/opa-client.js +157 -0
  32. package/dist/core/opa-client.js.map +1 -0
  33. package/dist/core/opa-policy-gate.d.ts +133 -0
  34. package/dist/core/opa-policy-gate.js +454 -0
  35. package/dist/core/opa-policy-gate.js.map +1 -0
  36. package/dist/core/orchestrator.d.ts +14 -0
  37. package/dist/core/orchestrator.js +1297 -0
  38. package/dist/core/orchestrator.js.map +1 -0
  39. package/dist/core/pii-detector.d.ts +82 -0
  40. package/dist/core/pii-detector.js +126 -0
  41. package/dist/core/pii-detector.js.map +1 -0
  42. package/dist/core/rag-engine.d.ts +121 -0
  43. package/dist/core/rag-engine.js +504 -0
  44. package/dist/core/rag-engine.js.map +1 -0
  45. package/dist/core/task-queue.d.ts +69 -0
  46. package/dist/core/task-queue.js +124 -0
  47. package/dist/core/task-queue.js.map +1 -0
  48. package/dist/core/telemetry.d.ts +56 -0
  49. package/dist/core/telemetry.js +94 -0
  50. package/dist/core/telemetry.js.map +1 -0
  51. package/dist/core/types.d.ts +328 -0
  52. package/dist/core/types.js +24 -0
  53. package/dist/core/types.js.map +1 -0
  54. package/dist/index.d.ts +21 -0
  55. package/dist/index.js +12444 -0
  56. package/dist/index.js.map +1 -0
  57. package/dist/llm-tracer-CIIujuO-.d.ts +493 -0
  58. package/dist/mcp/server.d.ts +2651 -0
  59. package/dist/mcp/server.js +676 -0
  60. package/dist/mcp/server.js.map +1 -0
  61. package/dist/orchestrator-DF89k_AK.d.ts +506 -0
  62. package/package.json +157 -0
  63. package/templates/README.md +7 -0
  64. package/templates/baseline.config.ts +207 -0
@@ -0,0 +1,241 @@
1
+ import Langfuse from 'langfuse';
2
+ import { trace } from '@opentelemetry/api';
3
+
4
+ // src/core/llm-tracer.ts
5
+ var PiiBlockedError = class extends Error {
6
+ constructor(types) {
7
+ super(`PII detected in prompt \u2014 blocked: ${types.join(", ")}`);
8
+ this.types = types;
9
+ this.name = "PiiBlockedError";
10
+ }
11
+ };
12
+ var PATTERNS = {
13
+ // API keys before email to avoid partial overlap on sk- prefixes
14
+ "api-key": /\b(sk-ant-[A-Za-z0-9-_]{20,}|sk-[A-Za-z0-9]{32,}|(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36}|AKIA[0-9A-Z]{16})\b/g,
15
+ "email": /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g,
16
+ "phone": /\b(?:\+?1[\s.-]?)?\(?[0-9]{3}\)?[\s.-][0-9]{3}[\s.-][0-9]{4}\b/g,
17
+ "ssn": /\b(?!000|666|9\d{2})\d{3}-(?!00)\d{2}-(?!0000)\d{4}\b/g,
18
+ "credit-card": /\b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13}|6(?:011|5[0-9]{2})[0-9]{12})\b/g,
19
+ "ip-address": /\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/g
20
+ };
21
+ var PiiDetector = class {
22
+ mode;
23
+ disabled;
24
+ bus;
25
+ constructor(config = {}) {
26
+ this.mode = config.mode ?? "redact";
27
+ this.disabled = new Set(config.disabledTypes ?? []);
28
+ this.bus = config.bus;
29
+ }
30
+ /**
31
+ * Scan text for PII without side effects.
32
+ * Safe to call in any context — no bus events, no span mutations.
33
+ */
34
+ scan(text) {
35
+ const matches = [];
36
+ for (const [type, pattern] of Object.entries(PATTERNS)) {
37
+ if (this.disabled.has(type)) continue;
38
+ pattern.lastIndex = 0;
39
+ let m;
40
+ while ((m = pattern.exec(text)) !== null) {
41
+ matches.push({ type, start: m.index, end: m.index + m[0].length });
42
+ }
43
+ }
44
+ matches.sort((a, b) => a.start - b.start || b.end - a.end);
45
+ const deduped = [];
46
+ let cursor = 0;
47
+ for (const match of matches) {
48
+ if (match.start >= cursor) {
49
+ deduped.push(match);
50
+ cursor = match.end;
51
+ }
52
+ }
53
+ const types = [...new Set(deduped.map((m) => m.type))];
54
+ const clean = deduped.length === 0;
55
+ let redacted = "";
56
+ let pos = 0;
57
+ for (const match of deduped) {
58
+ redacted += text.slice(pos, match.start);
59
+ redacted += `[REDACTED:${match.type.toUpperCase()}]`;
60
+ pos = match.end;
61
+ }
62
+ redacted += text.slice(pos);
63
+ return { clean, matches: deduped, types, redacted };
64
+ }
65
+ /**
66
+ * Process text before LLM routing.
67
+ *
68
+ * - Publishes 'governance:pii-detected' on the bus when PII is found.
69
+ * - Sets pii.detected and pii.types on the active OTel span.
70
+ * - redact mode: returns redacted text.
71
+ * - block mode: throws PiiBlockedError if PII found.
72
+ *
73
+ * @param text Text to scan (typically task.description).
74
+ * @param taskId Used for bus event correlation.
75
+ * @returns Safe text (redacted or original when clean).
76
+ */
77
+ processText(text, taskId) {
78
+ const result = this.scan(text);
79
+ if (!result.clean) {
80
+ const span = trace.getActiveSpan();
81
+ if (span) {
82
+ span.setAttribute("pii.detected", true);
83
+ span.setAttribute("pii.types", result.types.join(","));
84
+ span.setAttribute("pii.count", result.matches.length);
85
+ }
86
+ this.bus?.broadcast("system", "governance:pii-detected", {
87
+ taskId,
88
+ types: result.types,
89
+ count: result.matches.length,
90
+ blocked: this.mode === "block",
91
+ mode: this.mode
92
+ }, "high");
93
+ if (this.mode === "block") {
94
+ throw new PiiBlockedError(result.types);
95
+ }
96
+ }
97
+ return result.redacted;
98
+ }
99
+ };
100
+
101
+ // src/core/llm-tracer.ts
102
+ var LlmTracer = class {
103
+ langfuse;
104
+ piiScanner;
105
+ curator;
106
+ constructor(config = {}) {
107
+ this.langfuse = new Langfuse({
108
+ publicKey: config.publicKey ?? process.env["LANGFUSE_PUBLIC_KEY"] ?? "baseline-public-key",
109
+ secretKey: config.secretKey ?? process.env["LANGFUSE_SECRET_KEY"] ?? "baseline-secret-key",
110
+ baseUrl: config.baseUrl ?? process.env["LANGFUSE_BASE_URL"] ?? "http://localhost:3001",
111
+ flushAt: config.flushAt ?? 15,
112
+ flushInterval: config.flushInterval ?? 3e4
113
+ });
114
+ this.piiScanner = config.enablePiiMasking ?? true ? new PiiDetector({ mode: "redact" }) : null;
115
+ this.curator = config.curator ?? null;
116
+ }
117
+ /** Redact PII from a string if masking is enabled. No-op when scanner is null. */
118
+ maskPii(text) {
119
+ return this.piiScanner ? this.piiScanner.scan(text).redacted : text;
120
+ }
121
+ /**
122
+ * Log a single LLM generation to Langfuse.
123
+ *
124
+ * Multiple calls with the same `traceId` accumulate under one trace,
125
+ * so all generations for a task are grouped together automatically.
126
+ *
127
+ * @param traceId task.id — the top-level trace identifier
128
+ * @param traceName task.title — human-readable trace label in the UI
129
+ * @param options generation details
130
+ */
131
+ logGeneration(traceId, traceName, options) {
132
+ try {
133
+ const trace2 = this.langfuse.trace({
134
+ id: traceId,
135
+ name: traceName.slice(0, 200)
136
+ });
137
+ const maskedSystemPrompt = options.systemPrompt ? this.maskPii(options.systemPrompt) : void 0;
138
+ const maskedMessages = options.inputMessages.map((m) => ({
139
+ role: m.role,
140
+ content: typeof m.content === "string" ? this.maskPii(m.content) : m.content
141
+ }));
142
+ const maskedOutput = this.maskPii(options.output);
143
+ const input = maskedSystemPrompt ? [{ role: "system", content: maskedSystemPrompt }, ...maskedMessages] : maskedMessages;
144
+ trace2.generation({
145
+ name: options.name,
146
+ model: options.model,
147
+ input,
148
+ output: maskedOutput,
149
+ usage: {
150
+ input: options.inputTokens,
151
+ output: options.outputTokens,
152
+ total: options.inputTokens + options.outputTokens,
153
+ unit: "TOKENS"
154
+ },
155
+ startTime: options.startTime,
156
+ endTime: options.endTime,
157
+ metadata: options.metadata
158
+ });
159
+ if (this.curator) {
160
+ const firstUserMsg = options.inputMessages.find((m) => m.role === "user");
161
+ const inputText = typeof firstUserMsg?.content === "string" ? firstUserMsg.content : "";
162
+ const confidence = typeof options.metadata?.["confidence"] === "number" ? options.metadata["confidence"] : 0.8;
163
+ this.curator.ingest({
164
+ traceId,
165
+ title: traceName.slice(0, 200),
166
+ input: inputText,
167
+ output: options.output,
168
+ confidence,
169
+ benign: options.metadata?.["benign"] !== false,
170
+ tokens: options.inputTokens + options.outputTokens,
171
+ timestamp: options.endTime.toISOString(),
172
+ verified: typeof options.metadata?.["verified"] === "boolean" ? options.metadata["verified"] : void 0
173
+ });
174
+ }
175
+ } catch {
176
+ }
177
+ }
178
+ /** Flush pending spans. Call on graceful shutdown. */
179
+ async flush() {
180
+ try {
181
+ await this.langfuse.flushAsync();
182
+ } catch {
183
+ }
184
+ }
185
+ /** Shutdown Langfuse client (flushes pending spans). */
186
+ shutdown() {
187
+ this.flush().catch(() => {
188
+ });
189
+ }
190
+ };
191
+ /**
192
+ * PII Detector — SIGNAL-015
193
+ *
194
+ * Scans prompt text for personally identifiable information before it
195
+ * reaches the LLM router. Supports two enforcement modes:
196
+ *
197
+ * redact — replaces detected values with [REDACTED:TYPE] (default)
198
+ * block — throws PiiBlockedError, halting task execution
199
+ *
200
+ * Detected types:
201
+ * email — RFC 5321 address pattern
202
+ * phone — US/international format (10-digit minimum)
203
+ * ssn — US Social Security Number (NNN-NN-NNNN)
204
+ * credit-card — Visa, Mastercard, Amex, Discover
205
+ * api-key — OpenAI sk-, Anthropic sk-ant-, GitHub ghp_, AWS AKIA
206
+ * ip-address — IPv4 addresses
207
+ *
208
+ * Integration:
209
+ * Called by Orchestrator._performExecution() before engine.execute().
210
+ * Publishes 'governance:pii-detected' on the AgentBus when PII is found.
211
+ * Sets pii.detected and pii.types on the active OTel span.
212
+ *
213
+ * @license Apache-2.0
214
+ */
215
+ /**
216
+ * LLM Tracer — SIGNAL-014
217
+ *
218
+ * Logs every LLM call made by AnthropicEngine to Langfuse for prompt-level
219
+ * observability. Captures model, token usage, latency, input/output, and
220
+ * task metadata. Traces are keyed by task.id, so a multi-step task (execute
221
+ * → self-verify → review) produces one Langfuse trace with multiple
222
+ * generation spans.
223
+ *
224
+ * Self-hosted Langfuse:
225
+ * docker compose -f docker/docker-compose.monitoring.yml up -d
226
+ * → http://localhost:3001 (admin@baselineos.dev / baseline)
227
+ *
228
+ * Configuration (env or constructor):
229
+ * LANGFUSE_PUBLIC_KEY — project public key (default: baseline-public-key)
230
+ * LANGFUSE_SECRET_KEY — project secret key (default: baseline-secret-key)
231
+ * LANGFUSE_BASE_URL — Langfuse server URL (default: http://localhost:3001)
232
+ *
233
+ * The tracer is fail-safe: any Langfuse error is swallowed silently so it
234
+ * can never interrupt task execution.
235
+ *
236
+ * @license Apache-2.0
237
+ */
238
+
239
+ export { LlmTracer };
240
+ //# sourceMappingURL=llm-tracer.js.map
241
+ //# sourceMappingURL=llm-tracer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/pii-detector.ts","../../src/core/llm-tracer.ts"],"names":["trace"],"mappings":"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","file":"llm-tracer.js","sourcesContent":["/**\n * PII Detector — SIGNAL-015\n *\n * Scans prompt text for personally identifiable information before it\n * reaches the LLM router. Supports two enforcement modes:\n *\n * redact — replaces detected values with [REDACTED:TYPE] (default)\n * block — throws PiiBlockedError, halting task execution\n *\n * Detected types:\n * email — RFC 5321 address pattern\n * phone — US/international format (10-digit minimum)\n * ssn — US Social Security Number (NNN-NN-NNNN)\n * credit-card — Visa, Mastercard, Amex, Discover\n * api-key — OpenAI sk-, Anthropic sk-ant-, GitHub ghp_, AWS AKIA\n * ip-address — IPv4 addresses\n *\n * Integration:\n * Called by Orchestrator._performExecution() before engine.execute().\n * Publishes 'governance:pii-detected' on the AgentBus when PII is found.\n * Sets pii.detected and pii.types on the active OTel span.\n *\n * @license Apache-2.0\n */\n\nimport { trace } from '@opentelemetry/api';\nimport type { AgentBus } from './agent-bus.js';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type PiiType =\n | 'email'\n | 'phone'\n | 'ssn'\n | 'credit-card'\n | 'api-key'\n | 'ip-address';\n\nexport type PiiMode = 'redact' | 'block';\n\nexport interface PiiMatch {\n type: PiiType;\n start: number;\n end: number;\n}\n\nexport interface PiiScanResult {\n /** true when no PII was detected */\n clean: boolean;\n /** all matches found, sorted by start position */\n matches: PiiMatch[];\n /** unique PII types present */\n types: PiiType[];\n /** input text with all PII replaced by [REDACTED:TYPE]; equals input when clean=true */\n redacted: string;\n}\n\nexport interface PiiDetectorConfig {\n /** Enforcement mode. Default: 'redact' */\n mode?: PiiMode;\n /** PII types to skip. Useful for staged rollouts. */\n disabledTypes?: PiiType[];\n /** AgentBus for publishing governance:pii-detected events. */\n bus?: AgentBus;\n}\n\n// ─── Error ────────────────────────────────────────────────────────────────────\n\nexport class PiiBlockedError extends Error {\n constructor(public readonly types: PiiType[]) {\n super(`PII detected in prompt — blocked: ${types.join(', ')}`);\n this.name = 'PiiBlockedError';\n }\n}\n\n// ─── Patterns ────────────────────────────────────────────────────────────────\n\nconst PATTERNS: Record<PiiType, RegExp> = {\n // API keys before email to avoid partial overlap on sk- prefixes\n 'api-key': /\\b(sk-ant-[A-Za-z0-9-_]{20,}|sk-[A-Za-z0-9]{32,}|(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36}|AKIA[0-9A-Z]{16})\\b/g,\n 'email': /\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}\\b/g,\n 'phone': /\\b(?:\\+?1[\\s.-]?)?\\(?[0-9]{3}\\)?[\\s.-][0-9]{3}[\\s.-][0-9]{4}\\b/g,\n 'ssn': /\\b(?!000|666|9\\d{2})\\d{3}-(?!00)\\d{2}-(?!0000)\\d{4}\\b/g,\n 'credit-card': /\\b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13}|6(?:011|5[0-9]{2})[0-9]{12})\\b/g,\n 'ip-address': /\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\b/g,\n};\n\n// ─── PiiDetector ─────────────────────────────────────────────────────────────\n\nexport class PiiDetector {\n readonly mode: PiiMode;\n private readonly disabled: Set<PiiType>;\n private readonly bus?: AgentBus;\n\n constructor(config: PiiDetectorConfig = {}) {\n this.mode = config.mode ?? 'redact';\n this.disabled = new Set(config.disabledTypes ?? []);\n this.bus = config.bus;\n }\n\n /**\n * Scan text for PII without side effects.\n * Safe to call in any context — no bus events, no span mutations.\n */\n scan(text: string): PiiScanResult {\n const matches: PiiMatch[] = [];\n\n for (const [type, pattern] of Object.entries(PATTERNS) as Array<[PiiType, RegExp]>) {\n if (this.disabled.has(type)) continue;\n\n // Reset lastIndex — patterns are defined with /g flag\n pattern.lastIndex = 0;\n let m: RegExpExecArray | null;\n while ((m = pattern.exec(text)) !== null) {\n matches.push({ type, start: m.index, end: m.index + m[0].length });\n }\n }\n\n // Sort by start position, resolve overlaps (keep longest match)\n matches.sort((a, b) => a.start - b.start || b.end - a.end);\n const deduped: PiiMatch[] = [];\n let cursor = 0;\n for (const match of matches) {\n if (match.start >= cursor) {\n deduped.push(match);\n cursor = match.end;\n }\n }\n\n const types = [...new Set(deduped.map((m) => m.type))];\n const clean = deduped.length === 0;\n\n // Build redacted string\n let redacted = '';\n let pos = 0;\n for (const match of deduped) {\n redacted += text.slice(pos, match.start);\n redacted += `[REDACTED:${match.type.toUpperCase()}]`;\n pos = match.end;\n }\n redacted += text.slice(pos);\n\n return { clean, matches: deduped, types, redacted };\n }\n\n /**\n * Process text before LLM routing.\n *\n * - Publishes 'governance:pii-detected' on the bus when PII is found.\n * - Sets pii.detected and pii.types on the active OTel span.\n * - redact mode: returns redacted text.\n * - block mode: throws PiiBlockedError if PII found.\n *\n * @param text Text to scan (typically task.description).\n * @param taskId Used for bus event correlation.\n * @returns Safe text (redacted or original when clean).\n */\n processText(text: string, taskId?: string): string {\n const result = this.scan(text);\n\n if (!result.clean) {\n // Set OTel span attributes on the active span (task.perform)\n const span = trace.getActiveSpan();\n if (span) {\n span.setAttribute('pii.detected', true);\n span.setAttribute('pii.types', result.types.join(','));\n span.setAttribute('pii.count', result.matches.length);\n }\n\n // Publish governance event\n this.bus?.broadcast('system', 'governance:pii-detected', {\n taskId,\n types: result.types,\n count: result.matches.length,\n blocked: this.mode === 'block',\n mode: this.mode,\n }, 'high');\n\n if (this.mode === 'block') {\n throw new PiiBlockedError(result.types);\n }\n }\n\n return result.redacted;\n }\n}\n","/**\n * LLM Tracer — SIGNAL-014\n *\n * Logs every LLM call made by AnthropicEngine to Langfuse for prompt-level\n * observability. Captures model, token usage, latency, input/output, and\n * task metadata. Traces are keyed by task.id, so a multi-step task (execute\n * → self-verify → review) produces one Langfuse trace with multiple\n * generation spans.\n *\n * Self-hosted Langfuse:\n * docker compose -f docker/docker-compose.monitoring.yml up -d\n * → http://localhost:3001 (admin@baselineos.dev / baseline)\n *\n * Configuration (env or constructor):\n * LANGFUSE_PUBLIC_KEY — project public key (default: baseline-public-key)\n * LANGFUSE_SECRET_KEY — project secret key (default: baseline-secret-key)\n * LANGFUSE_BASE_URL — Langfuse server URL (default: http://localhost:3001)\n *\n * The tracer is fail-safe: any Langfuse error is swallowed silently so it\n * can never interrupt task execution.\n *\n * @license Apache-2.0\n */\n\nimport Langfuse from 'langfuse';\nimport { PiiDetector } from './pii-detector.js';\nimport type { TraceCurator } from './trace-curator.js';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface LlmTracerConfig {\n /** Langfuse project public key. Default: LANGFUSE_PUBLIC_KEY env or 'baseline-public-key' */\n publicKey?: string;\n /** Langfuse project secret key. Default: LANGFUSE_SECRET_KEY env or 'baseline-secret-key' */\n secretKey?: string;\n /** Langfuse server base URL. Default: LANGFUSE_BASE_URL env or http://localhost:3001 */\n baseUrl?: string;\n /** Flush batch size (default: 15) */\n flushAt?: number;\n /** Flush interval in ms (default: 30_000) */\n flushInterval?: number;\n /**\n * Scan and redact PII from system prompts, input messages, and output\n * before sending to Langfuse. Default: true (SIGNAL-037).\n */\n enablePiiMasking?: boolean;\n /**\n * TraceCurator to ingest sampled traces into the ground-truth dataset.\n * When provided, each logGeneration() call feeds a TraceRecord into the\n * curator buffer for downstream curation and eval dataset expansion (SIGNAL-049).\n */\n curator?: TraceCurator;\n}\n\nexport interface LlmGenerationOptions {\n /** Span name within the trace, e.g. 'execute', 'self-verify', 'review', 'agent-loop:3' */\n name: string;\n /** Model identifier, e.g. 'claude-sonnet-4-6' */\n model: string;\n /** System prompt sent to the model */\n systemPrompt?: string;\n /** User + assistant turns passed to the model */\n inputMessages: Array<{ role: string; content: unknown }>;\n /** Raw text output from the model */\n output: string;\n inputTokens: number;\n outputTokens: number;\n startTime: Date;\n endTime: Date;\n /** Arbitrary metadata attached to the generation (taskId, agentId, etc.) */\n metadata?: Record<string, unknown>;\n}\n\n// ─── LlmTracer ────────────────────────────────────────────────────────────────\n\nexport class LlmTracer {\n private readonly langfuse: Langfuse;\n private readonly piiScanner: PiiDetector | null;\n private readonly curator: TraceCurator | null;\n\n constructor(config: LlmTracerConfig = {}) {\n this.langfuse = new Langfuse({\n publicKey: config.publicKey ?? process.env['LANGFUSE_PUBLIC_KEY'] ?? 'baseline-public-key',\n secretKey: config.secretKey ?? process.env['LANGFUSE_SECRET_KEY'] ?? 'baseline-secret-key',\n baseUrl: config.baseUrl ?? process.env['LANGFUSE_BASE_URL'] ?? 'http://localhost:3001',\n flushAt: config.flushAt ?? 15,\n flushInterval: config.flushInterval ?? 30_000,\n });\n // PII masking enabled by default (SIGNAL-037)\n this.piiScanner = (config.enablePiiMasking ?? true)\n ? new PiiDetector({ mode: 'redact' })\n : null;\n this.curator = config.curator ?? null;\n }\n\n /** Redact PII from a string if masking is enabled. No-op when scanner is null. */\n private maskPii(text: string): string {\n return this.piiScanner ? this.piiScanner.scan(text).redacted : text;\n }\n\n /**\n * Log a single LLM generation to Langfuse.\n *\n * Multiple calls with the same `traceId` accumulate under one trace,\n * so all generations for a task are grouped together automatically.\n *\n * @param traceId task.id — the top-level trace identifier\n * @param traceName task.title — human-readable trace label in the UI\n * @param options generation details\n */\n logGeneration(\n traceId: string,\n traceName: string,\n options: LlmGenerationOptions,\n ): void {\n try {\n const trace = this.langfuse.trace({\n id: traceId,\n name: traceName.slice(0, 200),\n });\n\n // Redact PII before sending to Langfuse (SIGNAL-037)\n const maskedSystemPrompt = options.systemPrompt\n ? this.maskPii(options.systemPrompt)\n : undefined;\n const maskedMessages = options.inputMessages.map((m) => ({\n role: m.role,\n content: typeof m.content === 'string' ? this.maskPii(m.content) : m.content,\n }));\n const maskedOutput = this.maskPii(options.output);\n\n const input = maskedSystemPrompt\n ? [{ role: 'system', content: maskedSystemPrompt }, ...maskedMessages]\n : maskedMessages;\n\n trace.generation({\n name: options.name,\n model: options.model,\n input,\n output: maskedOutput,\n usage: {\n input: options.inputTokens,\n output: options.outputTokens,\n total: options.inputTokens + options.outputTokens,\n unit: 'TOKENS',\n },\n startTime: options.startTime,\n endTime: options.endTime,\n metadata: options.metadata,\n });\n\n // Feed into TraceCurator for ground-truth dataset expansion (SIGNAL-049)\n if (this.curator) {\n const firstUserMsg = options.inputMessages.find((m) => m.role === 'user');\n const inputText = typeof firstUserMsg?.content === 'string' ? firstUserMsg.content : '';\n const confidence = typeof options.metadata?.['confidence'] === 'number'\n ? (options.metadata['confidence'] as number)\n : 0.8;\n this.curator.ingest({\n traceId,\n title: traceName.slice(0, 200),\n input: inputText,\n output: options.output,\n confidence,\n benign: options.metadata?.['benign'] !== false,\n tokens: options.inputTokens + options.outputTokens,\n timestamp: options.endTime.toISOString(),\n verified: typeof options.metadata?.['verified'] === 'boolean'\n ? (options.metadata['verified'] as boolean)\n : undefined,\n });\n }\n } catch {\n // Silently ignore — tracer must never interrupt task execution\n }\n }\n\n /** Flush pending spans. Call on graceful shutdown. */\n async flush(): Promise<void> {\n try {\n await this.langfuse.flushAsync();\n } catch {\n // Silently ignore\n }\n }\n\n /** Shutdown Langfuse client (flushes pending spans). */\n shutdown(): void {\n this.flush().catch(() => {});\n }\n}\n"]}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * BaselineOS Memory System
3
+ *
4
+ * Multi-scope persistent memory for AI agents.
5
+ *
6
+ * Scopes:
7
+ * - Working: Per-request, cleared after each call (fastest)
8
+ * - Session: Per-conversation, cleared on disconnect
9
+ * - Long-term: Persists across sessions (SQLite)
10
+ * - Shared: Visible across all agents and sessions
11
+ *
12
+ * @license Apache-2.0
13
+ */
14
+ type MemoryScope = 'working' | 'session' | 'long-term' | 'shared';
15
+ interface MemoryConfig {
16
+ persistPath: string;
17
+ }
18
+ interface StoreOptions {
19
+ scope?: MemoryScope;
20
+ sessionId?: string;
21
+ ttl?: number;
22
+ tags?: string[];
23
+ taskId?: string;
24
+ repo?: string;
25
+ persona?: string;
26
+ }
27
+ interface RetrieveOptions {
28
+ scope?: MemoryScope | 'all';
29
+ sessionId?: string;
30
+ taskId?: string;
31
+ repo?: string;
32
+ persona?: string;
33
+ }
34
+ interface MemoryEntry {
35
+ key: string;
36
+ value: unknown;
37
+ scope: MemoryScope;
38
+ sessionId?: string;
39
+ taskId?: string;
40
+ repo?: string;
41
+ persona?: string;
42
+ tags: string[];
43
+ createdAt: number;
44
+ updatedAt: number;
45
+ expiresAt?: number;
46
+ accessCount: number;
47
+ }
48
+ declare class MemorySystem {
49
+ private config;
50
+ private workingMemory;
51
+ private sessionMemory;
52
+ private longTermMemory;
53
+ private sharedMemory;
54
+ private db;
55
+ private initialized;
56
+ constructor(config?: Partial<MemoryConfig>);
57
+ initialize(): Promise<void>;
58
+ store(key: string, value: unknown, options?: StoreOptions): Promise<void>;
59
+ retrieve(key: string, options?: RetrieveOptions): Promise<unknown>;
60
+ delete(key: string, options?: RetrieveOptions): Promise<boolean>;
61
+ search(query: {
62
+ tags?: string[];
63
+ prefix?: string;
64
+ scope?: MemoryScope;
65
+ sessionId?: string;
66
+ taskId?: string;
67
+ repo?: string;
68
+ persona?: string;
69
+ }): Promise<MemoryEntry[]>;
70
+ clearWorking(): void;
71
+ clearSession(sessionId?: string): void;
72
+ flush(): Promise<void>;
73
+ close(): void;
74
+ private persistEntry;
75
+ private getFromScope;
76
+ private buildStorageKey;
77
+ private matchesContext;
78
+ getStats(): {
79
+ working: number;
80
+ session: number;
81
+ longTerm: number;
82
+ shared: number;
83
+ };
84
+ }
85
+
86
+ export { type MemoryConfig, type MemoryEntry, type MemoryScope, MemorySystem, type RetrieveOptions, type StoreOptions };