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,1007 @@
1
+ import express from 'express';
2
+ import { WebSocketServer, WebSocket } from 'ws';
3
+ import { createServer } from 'http';
4
+ import { NodeSDK } from '@opentelemetry/sdk-node';
5
+ import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
6
+ import { resourceFromAttributes } from '@opentelemetry/resources';
7
+ import { ATTR_SERVICE_VERSION, ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
8
+ import { trace, propagation, context } from '@opentelemetry/api';
9
+
10
+ // src/api/server.ts
11
+ var _sdk = null;
12
+ function initTelemetry() {
13
+ if (_sdk || process.env["BASELINE_OTEL_DISABLED"] === "1") return;
14
+ const endpoint = process.env["OTEL_EXPORTER_OTLP_ENDPOINT"] ?? "http://localhost:4318";
15
+ const exporter = new OTLPTraceExporter({
16
+ url: `${endpoint}/v1/traces`
17
+ });
18
+ _sdk = new NodeSDK({
19
+ resource: resourceFromAttributes({
20
+ [ATTR_SERVICE_NAME]: process.env["OTEL_SERVICE_NAME"] ?? "baselineos",
21
+ [ATTR_SERVICE_VERSION]: "0.2.0-beta.1"
22
+ }),
23
+ traceExporter: exporter
24
+ // W3C traceparent propagation is the default — no explicit config needed
25
+ });
26
+ _sdk.start();
27
+ process.on("SIGTERM", () => {
28
+ _sdk?.shutdown().catch(() => {
29
+ });
30
+ });
31
+ process.on("SIGINT", () => {
32
+ _sdk?.shutdown().catch(() => {
33
+ });
34
+ });
35
+ }
36
+ new Proxy({}, {
37
+ get(_target, prop) {
38
+ return trace.getTracer("baselineos", "0.2.0-beta.1")[prop];
39
+ }
40
+ });
41
+ function extractTraceContext() {
42
+ const carrier = {};
43
+ propagation.inject(context.active(), carrier);
44
+ return carrier["traceparent"];
45
+ }
46
+
47
+ // src/api/server.ts
48
+ var TASK_STATUSES = [
49
+ "pending",
50
+ "planning",
51
+ "executing",
52
+ "verifying",
53
+ "reviewing",
54
+ "correcting",
55
+ "completed",
56
+ "failed",
57
+ "blocked"
58
+ ];
59
+ var CONTEXT_LEVELS = ["oneliner", "summary", "working", "full", "expanded"];
60
+ var MEMORY_SCOPES = ["working", "session", "long-term", "shared", "all"];
61
+ function parseTaskStatus(value) {
62
+ return typeof value === "string" && TASK_STATUSES.includes(value) ? value : void 0;
63
+ }
64
+ function parseContextLevel(value) {
65
+ if (typeof value === "string" && CONTEXT_LEVELS.includes(value)) {
66
+ return value;
67
+ }
68
+ return "working";
69
+ }
70
+ function parseScope(value) {
71
+ if (typeof value === "string" && MEMORY_SCOPES.includes(value)) {
72
+ return value;
73
+ }
74
+ return void 0;
75
+ }
76
+ function parseJson(value) {
77
+ if (!value) return void 0;
78
+ try {
79
+ return JSON.parse(value);
80
+ } catch {
81
+ return void 0;
82
+ }
83
+ }
84
+ function parsePolicy(value) {
85
+ if (!value || typeof value !== "object") return void 0;
86
+ return value;
87
+ }
88
+ function parseBudget(value) {
89
+ if (!value || typeof value !== "object") return void 0;
90
+ return value;
91
+ }
92
+ function parseSocketMessage(value) {
93
+ if (!value || typeof value !== "object") return null;
94
+ const message = value;
95
+ if (message.type === "ping") return { type: "ping" };
96
+ if (message.type === "subscribe") {
97
+ return {
98
+ type: "subscribe",
99
+ taskId: typeof message.taskId === "string" ? message.taskId : void 0
100
+ };
101
+ }
102
+ return null;
103
+ }
104
+ var APIServer = class {
105
+ app;
106
+ server;
107
+ wss;
108
+ baseline;
109
+ config;
110
+ clients;
111
+ unsubscribeEvents;
112
+ // ── Prometheus counters (in-process, reset on restart) ──────────────────
113
+ metricsCounters = {
114
+ requestsTotal: /* @__PURE__ */ new Map(),
115
+ // label: "method_path_status"
116
+ requestDurationMs: /* @__PURE__ */ new Map(),
117
+ // label: "method"
118
+ tasksTotal: /* @__PURE__ */ new Map(),
119
+ // label: task status
120
+ tokensByAgentRole: /* @__PURE__ */ new Map(),
121
+ // label: agent role (SIGNAL-013)
122
+ wsConnections: 0,
123
+ wsMessagesTotal: 0,
124
+ serverStartTime: Date.now(),
125
+ /** Running ratio of autonomous tasks that passed self-verification (SIGNAL-046) */
126
+ autonomousAccuracy: 1,
127
+ autonomousTotal: 0,
128
+ autonomousPassed: 0
129
+ };
130
+ /** Optional CostGuard for per-tenant cost metrics (SIGNAL-052) */
131
+ costGuard;
132
+ /** Optional TraceCurator for dataset size gauge (SIGNAL-049) */
133
+ traceCurator;
134
+ /** Optional ModelVersionRegistry for active model version gauge (SIGNAL-049) */
135
+ modelVersionRegistry;
136
+ /** Optional ProductionEvalPipeline for latest eval score gauge (SIGNAL-056) */
137
+ productionEvalPipeline;
138
+ /** Optional DriftDetector for model drift metrics (SIGNAL-071) */
139
+ driftDetector;
140
+ /** Optional OPAPolicyGate for SLO-6 latency metrics */
141
+ opaPolicyGate;
142
+ constructor(baseline, config) {
143
+ this.baseline = baseline;
144
+ this.config = config;
145
+ this.clients = /* @__PURE__ */ new Set();
146
+ this.unsubscribeEvents = [];
147
+ initTelemetry();
148
+ this.app = express();
149
+ this.server = createServer(this.app);
150
+ this.wss = new WebSocketServer({ server: this.server, path: "/api/stream" });
151
+ this.wss.on("error", (error) => {
152
+ const code = error?.code;
153
+ if (code !== "EADDRINUSE") {
154
+ console.error("[WS Error]", error);
155
+ }
156
+ });
157
+ this.setupMiddleware();
158
+ this.setupRoutes();
159
+ this.setupWebSocket();
160
+ this.attachEventBridge();
161
+ }
162
+ // ─── Setup ─────────────────────────────────────────────────────────────────
163
+ setupMiddleware() {
164
+ this.app.use(express.json());
165
+ const allowedOrigins = this.config.allowedOrigins ?? ["http://localhost:3000", "http://localhost:3141", "http://localhost:5173", "tauri://localhost"];
166
+ this.app.use((req, res, next) => {
167
+ const origin = req.headers.origin;
168
+ if (origin && allowedOrigins.includes(origin)) {
169
+ res.header("Access-Control-Allow-Origin", origin);
170
+ }
171
+ res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
172
+ res.header("Access-Control-Allow-Headers", "Content-Type, Authorization");
173
+ if (req.method === "OPTIONS") {
174
+ return res.sendStatus(200);
175
+ }
176
+ next();
177
+ });
178
+ this.app.use((_req, res, next) => {
179
+ const traceparent = extractTraceContext();
180
+ if (traceparent) res.setHeader("traceparent", traceparent);
181
+ next();
182
+ });
183
+ this.app.use((req, res, next) => {
184
+ const start = Date.now();
185
+ res.on("finish", () => {
186
+ const duration = Date.now() - start;
187
+ console.log(`[API] ${req.method} ${req.path} ${res.statusCode} ${duration}ms`);
188
+ if (req.path !== "/metrics") {
189
+ const reqKey = `${req.method}_${res.statusCode}`;
190
+ this.metricsCounters.requestsTotal.set(
191
+ reqKey,
192
+ (this.metricsCounters.requestsTotal.get(reqKey) ?? 0) + 1
193
+ );
194
+ const durKey = req.method;
195
+ const existing = this.metricsCounters.requestDurationMs.get(durKey) ?? [];
196
+ existing.push(duration);
197
+ if (existing.length > 1e3) existing.shift();
198
+ this.metricsCounters.requestDurationMs.set(durKey, existing);
199
+ }
200
+ });
201
+ next();
202
+ });
203
+ }
204
+ setupRoutes() {
205
+ this.app.get("/api/health", (req, res) => {
206
+ res.json({ status: "ok", version: "0.2.0-beta.1", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
207
+ });
208
+ this.app.get("/api/ready", (req, res) => {
209
+ const ready = !!this.baseline;
210
+ res.status(ready ? 200 : 503).json({
211
+ ready,
212
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
213
+ });
214
+ });
215
+ this.app.post("/api/tasks", this.createTask.bind(this));
216
+ this.app.get("/api/tasks", this.listTasks.bind(this));
217
+ this.app.get("/api/tasks/:id", this.getTask.bind(this));
218
+ this.app.delete("/api/tasks/:id", this.cancelTask.bind(this));
219
+ this.app.post("/api/tasks/:id/approve", this.approveTask.bind(this));
220
+ this.app.get("/api/tasks/:id/artifacts", this.getTaskArtifacts.bind(this));
221
+ this.app.get("/api/tasks/:id/audit", this.getTaskAudit.bind(this));
222
+ this.app.get("/api/tasks/:id/evidence", this.getTaskEvidence.bind(this));
223
+ this.app.get("/api/workflows", this.listWorkflows.bind(this));
224
+ this.app.post("/api/workflows/:id/run", this.runWorkflow.bind(this));
225
+ this.app.get("/api/checkpoints/:id", this.getCheckpoint.bind(this));
226
+ this.app.post("/api/checkpoints/:id/recover", this.recoverCheckpoint.bind(this));
227
+ this.app.get("/api/agents", this.listAgents.bind(this));
228
+ this.app.post("/api/search", this.search.bind(this));
229
+ this.app.get("/api/context/:subject", this.getContext.bind(this));
230
+ this.app.post("/api/index", this.indexKnowledge.bind(this));
231
+ this.app.post("/api/init", this.initDeployment.bind(this));
232
+ this.app.post("/api/scan", this.scanDeployment.bind(this));
233
+ this.app.post("/api/memory", this.remember.bind(this));
234
+ this.app.get("/api/memory/:key", this.recall.bind(this));
235
+ this.app.get("/api/stats", this.getStats.bind(this));
236
+ this.app.get("/metrics", this.getMetrics.bind(this));
237
+ this.app.use(this.errorHandler.bind(this));
238
+ }
239
+ setupWebSocket() {
240
+ this.wss.on("connection", (ws) => {
241
+ this.clients.add(ws);
242
+ console.log("[WS] Client connected");
243
+ this.metricsCounters.wsConnections++;
244
+ ws.on("close", () => {
245
+ this.clients.delete(ws);
246
+ console.log("[WS] Client disconnected");
247
+ });
248
+ ws.on("message", async (data) => {
249
+ try {
250
+ const message = JSON.parse(data.toString());
251
+ await this.handleWebSocketMessage(ws, message);
252
+ } catch {
253
+ ws.send(JSON.stringify({ error: "Invalid message" }));
254
+ }
255
+ });
256
+ ws.send(JSON.stringify({
257
+ type: "connected",
258
+ timestamp: Date.now(),
259
+ message: "Connected to BaselineOS"
260
+ }));
261
+ });
262
+ }
263
+ attachEventBridge() {
264
+ const events = [
265
+ "task:created",
266
+ "task:decomposed",
267
+ "task:assigned",
268
+ "task:started",
269
+ "task:checkpoint",
270
+ "task:verified",
271
+ "task:review-requested",
272
+ "task:reviewed",
273
+ "task:correction-needed",
274
+ "task:corrected",
275
+ "task:completed",
276
+ "task:failed",
277
+ "task:blocked",
278
+ "task:approved",
279
+ "agent:status-changed",
280
+ "agent:trust-updated",
281
+ "escalation"
282
+ ];
283
+ for (const event of events) {
284
+ const unsubscribe = this.baseline.onEvent(event, (payload) => {
285
+ this.broadcast({ ...payload, timestamp: Date.now() });
286
+ if (event === "task:completed" || event === "task:failed") {
287
+ const status = event === "task:completed" ? "completed" : "failed";
288
+ this.metricsCounters.tasksTotal.set(
289
+ status,
290
+ (this.metricsCounters.tasksTotal.get(status) ?? 0) + 1
291
+ );
292
+ if (event === "task:completed") {
293
+ const completedPayload = payload;
294
+ const tokens = completedPayload.task.actualTokens ?? 0;
295
+ if (tokens > 0 && completedPayload.task.assignedAgent) {
296
+ const agents = this.baseline.listAgents();
297
+ const agent = agents.find((a) => a.id === completedPayload.task.assignedAgent);
298
+ const role = agent?.role ?? "unknown";
299
+ this.metricsCounters.tokensByAgentRole.set(
300
+ role,
301
+ (this.metricsCounters.tokensByAgentRole.get(role) ?? 0) + tokens
302
+ );
303
+ }
304
+ const task = completedPayload.task;
305
+ if (task.assignedAgent) {
306
+ this.metricsCounters.autonomousTotal += 1;
307
+ const hasFailure = task.verificationResults?.some((v) => !v.passed);
308
+ if (!hasFailure) {
309
+ this.metricsCounters.autonomousPassed += 1;
310
+ }
311
+ this.metricsCounters.autonomousAccuracy = this.metricsCounters.autonomousTotal > 0 ? this.metricsCounters.autonomousPassed / this.metricsCounters.autonomousTotal : 1;
312
+ }
313
+ }
314
+ }
315
+ if (event === "task:created") {
316
+ this.metricsCounters.tasksTotal.set(
317
+ "created",
318
+ (this.metricsCounters.tasksTotal.get("created") ?? 0) + 1
319
+ );
320
+ }
321
+ });
322
+ this.unsubscribeEvents.push(unsubscribe);
323
+ }
324
+ }
325
+ // ─── Task Handlers ─────────────────────────────────────────────────────────
326
+ async createTask(req, res, next) {
327
+ try {
328
+ const acceptanceCriteriaInput = Array.isArray(req.body.acceptanceCriteria) ? req.body.acceptanceCriteria : [];
329
+ const acceptanceCriteria = acceptanceCriteriaInput.filter((item) => typeof item?.description === "string" && item.description.trim().length > 0).map((item, index) => ({
330
+ description: item.description?.trim() || `Criteria ${index + 1}`,
331
+ type: item.type ?? "automated",
332
+ weight: typeof item.weight === "number" ? item.weight : 1
333
+ }));
334
+ const input = {
335
+ title: req.body.title,
336
+ description: req.body.description,
337
+ priority: req.body.priority || "medium",
338
+ requiredCapabilities: Array.isArray(req.body.requiredCapabilities) ? req.body.requiredCapabilities : [],
339
+ acceptanceCriteria,
340
+ context: req.body.context && typeof req.body.context === "object" ? req.body.context : {}
341
+ };
342
+ const handle = await this.baseline.run(input);
343
+ this.broadcast({
344
+ type: "task:created",
345
+ taskId: handle.id,
346
+ title: input.title,
347
+ timestamp: Date.now()
348
+ });
349
+ if (req.body.blocking) {
350
+ const result = await handle.wait(req.body.timeout);
351
+ res.json({ task: result });
352
+ } else {
353
+ res.status(201).json({
354
+ taskId: handle.id,
355
+ status: handle.status
356
+ });
357
+ }
358
+ } catch (error) {
359
+ next(error);
360
+ }
361
+ }
362
+ async listTasks(req, res, next) {
363
+ try {
364
+ const status = parseTaskStatus(req.query.status);
365
+ const tasks = this.baseline.listTasks(status ? { status } : void 0);
366
+ res.json({
367
+ tasks: tasks.map((t) => ({
368
+ id: t.id,
369
+ title: t.title,
370
+ status: t.status,
371
+ priority: t.priority,
372
+ complexity: t.complexity,
373
+ createdAt: t.createdAt,
374
+ completedAt: t.completedAt
375
+ }))
376
+ });
377
+ } catch (error) {
378
+ next(error);
379
+ }
380
+ }
381
+ async getTask(req, res, next) {
382
+ try {
383
+ const task = this.baseline.getTask(req.params.id);
384
+ if (!task) {
385
+ res.status(404).json({ error: "not_found", message: "Task not found" });
386
+ return;
387
+ }
388
+ res.json({ task });
389
+ } catch (error) {
390
+ next(error);
391
+ }
392
+ }
393
+ async cancelTask(req, res, next) {
394
+ try {
395
+ const cancelled = this.baseline.cancelTask(
396
+ req.params.id,
397
+ typeof req.body?.reason === "string" ? req.body.reason : void 0
398
+ );
399
+ if (!cancelled) {
400
+ res.status(404).json({ error: "not_found", message: "Task not found or not cancellable" });
401
+ return;
402
+ }
403
+ res.json({ cancelled: true });
404
+ } catch (error) {
405
+ next(error);
406
+ }
407
+ }
408
+ async approveTask(req, res, next) {
409
+ try {
410
+ const approvedBy = typeof req.body?.approvedBy === "string" ? req.body.approvedBy : "human";
411
+ const approvalToken = typeof req.body?.approvalToken === "string" ? req.body.approvalToken : void 0;
412
+ const task = await this.baseline.approveTask(req.params.id, approvedBy, approvalToken);
413
+ res.json({ task });
414
+ } catch (error) {
415
+ const message = error instanceof Error ? error.message : String(error);
416
+ if (message.includes("not found")) {
417
+ res.status(404).json({ error: "not_found", message });
418
+ } else if (message.includes("not blocked")) {
419
+ res.status(409).json({ error: "conflict", message });
420
+ } else if (message.includes("token") || message.includes("expired")) {
421
+ res.status(403).json({ error: "forbidden", message });
422
+ } else {
423
+ next(error);
424
+ }
425
+ }
426
+ }
427
+ async getTaskArtifacts(req, res, next) {
428
+ try {
429
+ const task = this.baseline.getTask(req.params.id);
430
+ if (!task) {
431
+ res.status(404).json({ error: "not_found", message: "Task not found" });
432
+ return;
433
+ }
434
+ res.json({ artifacts: task.artifacts });
435
+ } catch (error) {
436
+ next(error);
437
+ }
438
+ }
439
+ async getTaskAudit(req, res, next) {
440
+ try {
441
+ const taskId = req.params.id;
442
+ const audit = this.baseline.getAuditTrail(taskId);
443
+ res.json({ taskId, audit });
444
+ } catch (error) {
445
+ next(error);
446
+ }
447
+ }
448
+ async getTaskEvidence(req, res, next) {
449
+ try {
450
+ const taskId = req.params.id;
451
+ const bundle = this.baseline.getEvidenceBundle(taskId);
452
+ if (!bundle.task) {
453
+ res.status(404).json({ error: "not_found", message: "Task not found" });
454
+ return;
455
+ }
456
+ res.json({ taskId, bundle });
457
+ } catch (error) {
458
+ next(error);
459
+ }
460
+ }
461
+ async listWorkflows(req, res, next) {
462
+ try {
463
+ const workflows = this.baseline.listWorkflows();
464
+ res.json({ workflows });
465
+ } catch (error) {
466
+ next(error);
467
+ }
468
+ }
469
+ async runWorkflow(req, res, next) {
470
+ try {
471
+ const handle = await this.baseline.runWorkflow(req.params.id, {
472
+ title: req.body.title,
473
+ description: req.body.description,
474
+ priority: req.body.priority,
475
+ requiredCapabilities: Array.isArray(req.body.requiredCapabilities) ? req.body.requiredCapabilities : void 0,
476
+ acceptanceCriteria: Array.isArray(req.body.acceptanceCriteria) ? req.body.acceptanceCriteria : void 0,
477
+ context: req.body.context && typeof req.body.context === "object" ? req.body.context : void 0
478
+ });
479
+ if (req.body.blocking) {
480
+ const result = await handle.wait(req.body.timeout);
481
+ res.json({ task: result });
482
+ } else {
483
+ res.status(201).json({ taskId: handle.id, status: handle.status });
484
+ }
485
+ } catch (error) {
486
+ next(error);
487
+ }
488
+ }
489
+ async getCheckpoint(req, res, next) {
490
+ try {
491
+ const checkpoint = this.baseline.loadCheckpoint(req.params.id);
492
+ if (!checkpoint) {
493
+ res.status(404).json({ error: "not_found", message: "Checkpoint not found" });
494
+ return;
495
+ }
496
+ res.json({ checkpoint });
497
+ } catch (error) {
498
+ next(error);
499
+ }
500
+ }
501
+ async recoverCheckpoint(req, res, next) {
502
+ try {
503
+ const task = this.baseline.recoverTaskFromCheckpoint(req.params.id);
504
+ if (!task) {
505
+ res.status(404).json({ error: "not_found", message: "Checkpoint not found" });
506
+ return;
507
+ }
508
+ res.json({ task });
509
+ } catch (error) {
510
+ next(error);
511
+ }
512
+ }
513
+ // ─── Agent Handlers ────────────────────────────────────────────────────────
514
+ async listAgents(req, res, next) {
515
+ try {
516
+ const agents = this.baseline.listAgents();
517
+ res.json({ agents });
518
+ } catch (error) {
519
+ next(error);
520
+ }
521
+ }
522
+ // ─── Knowledge Handlers ────────────────────────────────────────────────────
523
+ async search(req, res, next) {
524
+ try {
525
+ const policy = req.body.policy && typeof req.body.policy === "object" ? req.body.policy : void 0;
526
+ const results = await this.baseline.search(req.body.query, {
527
+ limit: req.body.limit,
528
+ policy
529
+ });
530
+ res.json({ results });
531
+ } catch (error) {
532
+ next(error);
533
+ }
534
+ }
535
+ async getContext(req, res, next) {
536
+ try {
537
+ const level = parseContextLevel(req.query.level);
538
+ const policy = parsePolicy(parseJson(req.query.policy ? String(req.query.policy) : void 0));
539
+ const overlayOrder = req.query.overlayOrder ? String(req.query.overlayOrder).split(",").map((v) => v.trim()).filter((v) => v === "org" || v === "repo" || v === "public") : void 0;
540
+ const budget = parseBudget(parseJson(req.query.budget ? String(req.query.budget) : void 0));
541
+ const context2 = await this.baseline.getContext(req.params.subject, level, {
542
+ policy,
543
+ overlayOrder,
544
+ budget
545
+ });
546
+ if (!context2) {
547
+ res.status(404).json({ error: "not_found", message: "Context not found" });
548
+ return;
549
+ }
550
+ res.json({ context: context2 });
551
+ } catch (error) {
552
+ next(error);
553
+ }
554
+ }
555
+ async indexKnowledge(req, res, next) {
556
+ try {
557
+ const result = await this.baseline.index({ full: req.body.full });
558
+ res.json({
559
+ indexed: result.indexed,
560
+ errors: result.errors,
561
+ skipped: result.skipped,
562
+ changed: result.changed,
563
+ removed: result.removed,
564
+ message: `Indexed ${result.indexed} documents`
565
+ });
566
+ } catch (error) {
567
+ next(error);
568
+ }
569
+ }
570
+ // ─── Init & Scan Handlers (GA-07, GA-08) ───────────────────────────────────
571
+ async initDeployment(req, res, next) {
572
+ try {
573
+ const contextPath = req.body.contextPath || process.cwd();
574
+ const scan = await this.scanPath(contextPath);
575
+ const result = await this.baseline.index({ full: false });
576
+ res.json({
577
+ initialized: true,
578
+ contextPath,
579
+ detected: scan,
580
+ indexed: result.indexed,
581
+ message: `Baseline initialized for ${contextPath}`
582
+ });
583
+ } catch (error) {
584
+ next(error);
585
+ }
586
+ }
587
+ async scanDeployment(req, res, next) {
588
+ try {
589
+ const contextPath = req.body.contextPath || process.cwd();
590
+ const detected = await this.scanPath(contextPath);
591
+ res.json({ contextPath, detected });
592
+ } catch (error) {
593
+ next(error);
594
+ }
595
+ }
596
+ async scanPath(contextPath) {
597
+ const detected = {};
598
+ try {
599
+ const { existsSync, readFileSync } = await import('fs');
600
+ const { join } = await import('path');
601
+ const pkgPath = join(contextPath, "package.json");
602
+ if (existsSync(pkgPath)) {
603
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
604
+ const allDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
605
+ if (allDeps["@anthropic-ai/sdk"]) detected.provider = "anthropic";
606
+ else if (allDeps["openai"]) detected.provider = "openai";
607
+ if (allDeps["langchain"] || allDeps["@langchain/core"]) detected.framework = "langchain";
608
+ if (allDeps["@modelcontextprotocol/sdk"]) detected.mcp = true;
609
+ if (allDeps["bullmq"] || allDeps["@temporalio/client"]) detected.orchestration = true;
610
+ detected.runtime = "node";
611
+ detected.name = pkg.name;
612
+ }
613
+ const pyReq = join(contextPath, "requirements.txt");
614
+ if (existsSync(pyReq)) {
615
+ const reqs = readFileSync(pyReq, "utf8");
616
+ if (reqs.includes("anthropic")) detected.provider = "anthropic";
617
+ else if (reqs.includes("openai")) detected.provider = "openai";
618
+ if (reqs.includes("langchain")) detected.framework = "langchain";
619
+ detected.runtime = "python";
620
+ }
621
+ } catch {
622
+ }
623
+ return detected;
624
+ }
625
+ // ─── Memory Handlers ───────────────────────────────────────────────────────
626
+ async remember(req, res, next) {
627
+ try {
628
+ await this.baseline.remember(req.body.key, req.body.value, {
629
+ scope: req.body.scope,
630
+ sessionId: req.body.sessionId,
631
+ taskId: req.body.taskId,
632
+ repo: req.body.repo,
633
+ persona: req.body.persona,
634
+ ttl: req.body.ttl,
635
+ tags: Array.isArray(req.body.tags) ? req.body.tags : void 0
636
+ });
637
+ res.json({ stored: true });
638
+ } catch (error) {
639
+ next(error);
640
+ }
641
+ }
642
+ async recall(req, res, next) {
643
+ try {
644
+ const scope = parseScope(req.query.scope);
645
+ const sessionId = req.query.sessionId ? String(req.query.sessionId) : void 0;
646
+ const taskId = req.query.taskId ? String(req.query.taskId) : void 0;
647
+ const repo = req.query.repo ? String(req.query.repo) : void 0;
648
+ const persona = req.query.persona ? String(req.query.persona) : void 0;
649
+ const value = await this.baseline.recall(req.params.key, {
650
+ scope,
651
+ sessionId,
652
+ taskId,
653
+ repo,
654
+ persona
655
+ });
656
+ if (value === void 0) {
657
+ res.status(404).json({ error: "not_found", message: "Key not found" });
658
+ return;
659
+ }
660
+ res.json({ value });
661
+ } catch (error) {
662
+ next(error);
663
+ }
664
+ }
665
+ // ─── Stats Handler ─────────────────────────────────────────────────────────
666
+ async getStats(req, res, next) {
667
+ try {
668
+ const stats = this.baseline.getStats();
669
+ res.json(stats);
670
+ } catch (error) {
671
+ next(error);
672
+ }
673
+ }
674
+ // ─── Prometheus Metrics (SIGNAL-005) ───────────────────────────────────────
675
+ async getMetrics(req, res, next) {
676
+ try {
677
+ const stats = this.baseline.getStats();
678
+ const lines = [];
679
+ const gauge = (name, help, value, labels) => {
680
+ lines.push(`# HELP ${name} ${help}`);
681
+ lines.push(`# TYPE ${name} gauge`);
682
+ lines.push(labels ? `${name}{${labels}} ${value}` : `${name} ${value}`);
683
+ };
684
+ const counter = (name, help, value, labels) => {
685
+ lines.push(`# HELP ${name} ${help}`);
686
+ lines.push(`# TYPE ${name} counter`);
687
+ lines.push(labels ? `${name}{${labels}} ${value}` : `${name} ${value}`);
688
+ };
689
+ gauge(
690
+ "baseline_uptime_seconds",
691
+ "Seconds since API server started",
692
+ Math.round((Date.now() - this.metricsCounters.serverStartTime) / 1e3)
693
+ );
694
+ const tasks = this.baseline.listTasks();
695
+ const tasksByStatus = tasks.reduce((acc, t) => {
696
+ acc[t.status] = (acc[t.status] ?? 0) + 1;
697
+ return acc;
698
+ }, {});
699
+ lines.push("# HELP baseline_tasks_active Current tasks by status");
700
+ lines.push("# TYPE baseline_tasks_active gauge");
701
+ for (const [status, count] of Object.entries(tasksByStatus)) {
702
+ lines.push(`baseline_tasks_active{status="${status}"} ${count}`);
703
+ }
704
+ lines.push("# HELP baseline_tasks_total Cumulative tasks by terminal status");
705
+ lines.push("# TYPE baseline_tasks_total counter");
706
+ for (const [status, count] of this.metricsCounters.tasksTotal) {
707
+ lines.push(`baseline_tasks_total{status="${status}"} ${count}`);
708
+ }
709
+ const agentStats = stats.orchestrator;
710
+ gauge("baseline_agents_total", "Total registered agents", agentStats.agents?.total ?? 0);
711
+ gauge(
712
+ "baseline_agents_active",
713
+ "Currently active agents",
714
+ Object.values(agentStats.agents?.byRole ?? {}).reduce((a, b) => a + b, 0)
715
+ );
716
+ const memStats = stats.memory;
717
+ const memTotal = (memStats.working ?? 0) + (memStats.session ?? 0) + (memStats.longTerm ?? 0) + (memStats.shared ?? 0);
718
+ gauge("baseline_memory_entries", "Total memory entries stored", memTotal);
719
+ const idxStats = stats.indexer;
720
+ gauge("baseline_knowledge_documents", "Documents indexed in knowledge base", idxStats.documents ?? 0);
721
+ gauge("baseline_knowledge_compressions", "Pre-computed compressions in index", idxStats.compressions ?? 0);
722
+ const cacheStats = stats.cache;
723
+ if (typeof cacheStats.hits === "number") {
724
+ counter("baseline_cache_hits_total", "Total semantic cache hits", cacheStats.hits);
725
+ counter("baseline_cache_misses_total", "Total semantic cache misses", cacheStats.misses ?? 0);
726
+ if (cacheStats.hits + (cacheStats.misses ?? 0) > 0) {
727
+ gauge(
728
+ "baseline_cache_hit_rate",
729
+ "Cache hit rate (0\u20131)",
730
+ cacheStats.hits / (cacheStats.hits + (cacheStats.misses ?? 0))
731
+ );
732
+ }
733
+ }
734
+ lines.push("# HELP baseline_http_requests_total HTTP requests by method and status");
735
+ lines.push("# TYPE baseline_http_requests_total counter");
736
+ for (const [label, count] of this.metricsCounters.requestsTotal) {
737
+ const [method, status] = label.split("_");
738
+ lines.push(`baseline_http_requests_total{method="${method}",status="${status}"} ${count}`);
739
+ }
740
+ lines.push("# HELP baseline_http_request_duration_p50_ms HTTP request duration p50 in ms");
741
+ lines.push("# TYPE baseline_http_request_duration_p50_ms gauge");
742
+ lines.push("# HELP baseline_http_request_duration_p95_ms HTTP request duration p95 in ms");
743
+ lines.push("# TYPE baseline_http_request_duration_p95_ms gauge");
744
+ for (const [method, durations] of this.metricsCounters.requestDurationMs) {
745
+ if (durations.length > 0) {
746
+ const sorted = [...durations].sort((a, b) => a - b);
747
+ const p50 = sorted[Math.floor(sorted.length * 0.5)];
748
+ const p95 = sorted[Math.floor(sorted.length * 0.95)];
749
+ lines.push(`baseline_http_request_duration_p50_ms{method="${method}"} ${p50}`);
750
+ lines.push(`baseline_http_request_duration_p95_ms{method="${method}"} ${p95}`);
751
+ }
752
+ }
753
+ if (this.metricsCounters.tokensByAgentRole.size > 0) {
754
+ lines.push("# HELP baseline_task_tokens_total Cumulative tokens used by agent role");
755
+ lines.push("# TYPE baseline_task_tokens_total counter");
756
+ for (const [role, tokens] of this.metricsCounters.tokensByAgentRole) {
757
+ lines.push(`baseline_task_tokens_total{role="${role}"} ${tokens}`);
758
+ }
759
+ }
760
+ gauge("baseline_ws_active_connections", "Currently active WebSocket connections", this.clients.size);
761
+ counter("baseline_ws_connections_total", "Total WebSocket connections since start", this.metricsCounters.wsConnections);
762
+ const rollbackTotal = this.metricsCounters.tasksTotal.get("rolledback") ?? 0;
763
+ counter("baseline_rollback_total", "Cumulative rollbacks since start", rollbackTotal);
764
+ gauge(
765
+ "baseline_autonomous_accuracy",
766
+ "Ratio of self-verified autonomous task completions to total autonomous completions (0\u20131)",
767
+ this.metricsCounters.autonomousAccuracy
768
+ );
769
+ if (this.costGuard) {
770
+ const tenantCosts = this.costGuard.getCostByTenant();
771
+ if (tenantCosts.length > 0) {
772
+ lines.push("# HELP baseline_tenant_cost_usd Estimated LLM cost in USD by tenant");
773
+ lines.push("# TYPE baseline_tenant_cost_usd gauge");
774
+ for (const tc of tenantCosts) {
775
+ lines.push(`baseline_tenant_cost_usd{tenant_id="${tc.tenantId}"} ${tc.totalCostUsd.toFixed(6)}`);
776
+ }
777
+ }
778
+ }
779
+ if (this.traceCurator) {
780
+ gauge(
781
+ "baseline_trace_curator_dataset_size",
782
+ "Number of ground-truth cases in the TraceCurator dataset",
783
+ this.traceCurator.getDatasetSize()
784
+ );
785
+ }
786
+ if (this.modelVersionRegistry) {
787
+ const activeProviders = this.modelVersionRegistry.listActiveProviders();
788
+ if (activeProviders.length > 0) {
789
+ lines.push("# HELP baseline_active_model_version Active model version per provider (info metric, value=1)");
790
+ lines.push("# TYPE baseline_active_model_version gauge");
791
+ for (const { provider, modelId } of activeProviders) {
792
+ lines.push(`baseline_active_model_version{provider="${provider}",model="${modelId}"} 1`);
793
+ }
794
+ }
795
+ }
796
+ if (this.productionEvalPipeline) {
797
+ const history = this.productionEvalPipeline.getHistory();
798
+ if (history.length > 0) {
799
+ const latestByModel = /* @__PURE__ */ new Map();
800
+ for (const entry of history) {
801
+ latestByModel.set(entry.modelId, entry.score);
802
+ }
803
+ lines.push("# HELP baseline_eval_score_latest Latest production eval score per model (0-100)");
804
+ lines.push("# TYPE baseline_eval_score_latest gauge");
805
+ for (const [modelId, score] of latestByModel) {
806
+ lines.push(`baseline_eval_score_latest{model_id="${modelId}"} ${score}`);
807
+ }
808
+ }
809
+ }
810
+ if (this.driftDetector && this.productionEvalPipeline) {
811
+ const history = this.productionEvalPipeline.getHistory();
812
+ if (history.length >= 3) {
813
+ const driftReport = this.driftDetector.check(history);
814
+ const severityNum = driftReport.severity === "critical" ? 2 : driftReport.severity === "warning" ? 1 : 0;
815
+ gauge(
816
+ "baseline_model_drift_severity",
817
+ "Model quality drift severity: 0=none 1=warning 2=critical",
818
+ severityNum
819
+ );
820
+ gauge(
821
+ "baseline_model_drift_delta",
822
+ "Score delta from rolling baseline (negative = regression)",
823
+ driftReport.delta
824
+ );
825
+ gauge(
826
+ "baseline_model_drift_baseline_score",
827
+ "Rolling baseline score used for drift comparison",
828
+ driftReport.baselineScore
829
+ );
830
+ gauge(
831
+ "baseline_model_drift_current_score",
832
+ "Current model eval score from latest run",
833
+ driftReport.currentScore
834
+ );
835
+ counter(
836
+ "baseline_model_drift_samples_analyzed",
837
+ "Number of eval history entries analyzed for drift",
838
+ driftReport.samplesAnalyzed
839
+ );
840
+ }
841
+ }
842
+ if (this.opaPolicyGate) {
843
+ const opaMetrics = this.opaPolicyGate.getEvalMetrics();
844
+ if (opaMetrics.count > 0) {
845
+ gauge("baseline_opa_evaluation_p50_ms", "OPA policy evaluation p50 latency in ms", opaMetrics.p50Ms);
846
+ gauge("baseline_opa_evaluation_p95_ms", "OPA policy evaluation p95 latency in ms", opaMetrics.p95Ms);
847
+ gauge("baseline_opa_evaluation_p99_ms", "OPA policy evaluation p99 latency in ms (SLO-6 target: < 500ms)", opaMetrics.p99Ms);
848
+ counter("baseline_opa_evaluations_total", "Total OPA policy evaluations", opaMetrics.count);
849
+ }
850
+ }
851
+ gauge(
852
+ "baseline_governance_required_approvals",
853
+ "Operations requiring human approval",
854
+ stats.governance.requireApproval
855
+ );
856
+ gauge(
857
+ "baseline_governance_restricted_operations",
858
+ "Restricted operation count",
859
+ stats.governance.restricted
860
+ );
861
+ res.set("Content-Type", "text/plain; version=0.0.4; charset=utf-8");
862
+ res.send(lines.join("\n") + "\n");
863
+ } catch (error) {
864
+ next(error);
865
+ }
866
+ }
867
+ /** Inject a CostGuard instance for per-tenant cost metrics (SIGNAL-052). */
868
+ setCostGuard(guard) {
869
+ this.costGuard = guard;
870
+ }
871
+ /** Inject a TraceCurator for the dataset-size gauge (SIGNAL-049). */
872
+ setTraceCurator(curator) {
873
+ this.traceCurator = curator;
874
+ }
875
+ /** Inject a ModelVersionRegistry for the active-model gauge (SIGNAL-049). */
876
+ setModelVersionRegistry(registry) {
877
+ this.modelVersionRegistry = registry;
878
+ }
879
+ /** Inject a ProductionEvalPipeline for the eval score gauge (SIGNAL-056). */
880
+ setProductionEvalPipeline(pipeline) {
881
+ this.productionEvalPipeline = pipeline;
882
+ }
883
+ /** Inject a DriftDetector for model drift metrics (SIGNAL-071). */
884
+ setDriftDetector(detector) {
885
+ this.driftDetector = detector;
886
+ }
887
+ /** Inject an OPAPolicyGate for SLO-6 latency metrics. */
888
+ setOPAPolicyGate(gate) {
889
+ this.opaPolicyGate = gate;
890
+ }
891
+ // ─── WebSocket ─────────────────────────────────────────────────────────────
892
+ async handleWebSocketMessage(ws, message) {
893
+ const parsed = parseSocketMessage(message);
894
+ if (!parsed) {
895
+ ws.send(JSON.stringify({ error: "Unknown message type" }));
896
+ return;
897
+ }
898
+ switch (parsed.type) {
899
+ case "subscribe":
900
+ ws.send(JSON.stringify({ type: "subscribed", taskId: parsed.taskId }));
901
+ break;
902
+ case "ping":
903
+ ws.send(JSON.stringify({ type: "pong", timestamp: Date.now() }));
904
+ break;
905
+ default:
906
+ ws.send(JSON.stringify({ error: "Unknown message type" }));
907
+ }
908
+ }
909
+ /**
910
+ * Broadcast message to all connected WebSocket clients.
911
+ */
912
+ broadcast(message) {
913
+ const data = JSON.stringify(message);
914
+ for (const client of this.clients) {
915
+ if (client.readyState === WebSocket.OPEN) {
916
+ client.send(data);
917
+ }
918
+ }
919
+ }
920
+ // ─── Error Handler ─────────────────────────────────────────────────────────
921
+ errorHandler(err, req, res, _next) {
922
+ process.stderr.write(`[baseline:api] ${req.method} ${req.path} \u2014 ${err.stack ?? err.message}
923
+ `);
924
+ res.status(500).json({
925
+ error: "internal_error",
926
+ message: "An unexpected error occurred. Check server logs for details."
927
+ });
928
+ }
929
+ // ─── Lifecycle ─────────────────────────────────────────────────────────────
930
+ formatStartError(error) {
931
+ if (error.code === "EADDRINUSE") {
932
+ return new Error(
933
+ `Port ${this.config.port} is already in use. Stop the existing Baseline API process or choose a different port.`
934
+ );
935
+ }
936
+ if (error.code === "EACCES") {
937
+ return new Error(
938
+ `Insufficient permission to bind port ${this.config.port}. Try a non-privileged port (for example: 3141).`
939
+ );
940
+ }
941
+ return new Error(`Failed to start API server on port ${this.config.port}: ${error.message}`);
942
+ }
943
+ async start() {
944
+ if (this.server.listening) return;
945
+ return new Promise((resolve, reject) => {
946
+ const onError = (error) => {
947
+ this.server.off("listening", onListening);
948
+ reject(this.formatStartError(error));
949
+ };
950
+ const onListening = () => {
951
+ this.server.off("error", onError);
952
+ resolve();
953
+ };
954
+ this.server.once("error", onError);
955
+ this.server.once("listening", onListening);
956
+ this.server.listen(this.config.port);
957
+ });
958
+ }
959
+ async stop() {
960
+ this.wss.close();
961
+ if (!this.server.listening) return;
962
+ return new Promise((resolve, reject) => {
963
+ this.server.close((error) => {
964
+ if (error) {
965
+ reject(error);
966
+ return;
967
+ }
968
+ resolve();
969
+ });
970
+ });
971
+ }
972
+ };
973
+ /**
974
+ * BaselineOS OpenTelemetry — SIGNAL-012
975
+ *
976
+ * Distributed tracing for the multi-agent orchestration layer.
977
+ * Initializes the OTel Node SDK and exports a shared tracer.
978
+ *
979
+ * Spans emitted:
980
+ * task.execute root span per executeTask() call
981
+ * task.policy.pre OPA pre-execution gate
982
+ * task.policy.post OPA post-execution gate
983
+ * task.perform performExecution() step loop
984
+ * task.self_verify selfVerify() LLM call
985
+ * task.review conductReview() LLM call
986
+ *
987
+ * Trace context is propagated through AgentBus messages via the
988
+ * optional `traceContext` field (W3C traceparent format).
989
+ *
990
+ * Configuration:
991
+ * OTEL_EXPORTER_OTLP_ENDPOINT OTLP HTTP collector (default: http://localhost:4318)
992
+ * OTEL_SERVICE_NAME service name override (default: baselineos)
993
+ * BASELINE_OTEL_DISABLED set to '1' to disable entirely (e.g. in tests)
994
+ *
995
+ * @license Apache-2.0
996
+ */
997
+ /**
998
+ * BaselineOS API Server
999
+ *
1000
+ * REST API and WebSocket for programmatic access.
1001
+ *
1002
+ * @license Apache-2.0
1003
+ */
1004
+
1005
+ export { APIServer };
1006
+ //# sourceMappingURL=server.js.map
1007
+ //# sourceMappingURL=server.js.map