@aaac/observability 0.1.4 → 0.1.6

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.
@@ -1225,6 +1225,282 @@ function buildEventFilter(filter) {
1225
1225
  return { clauses, params };
1226
1226
  }
1227
1227
 
1228
+ // src/event-mapping/engine.ts
1229
+ import { readFile } from "fs/promises";
1230
+ import { parse as parseYaml } from "yaml";
1231
+
1232
+ // src/event-mapping/template.ts
1233
+ var TEMPLATE_RE = /\{\{([^}]+)\}\}/g;
1234
+ function getFieldValue(input, path) {
1235
+ const parts = path.split(".");
1236
+ let current = input;
1237
+ for (const part of parts) {
1238
+ if (current === null || current === void 0 || typeof current !== "object") {
1239
+ return void 0;
1240
+ }
1241
+ current = current[part];
1242
+ }
1243
+ return current;
1244
+ }
1245
+ function resolveExpression(expr, input, context) {
1246
+ const trimmed = expr.trim();
1247
+ if (trimmed.startsWith("env:")) {
1248
+ const varName = trimmed.slice(4).trim();
1249
+ return process.env[varName] ?? "";
1250
+ }
1251
+ if (trimmed === "item" && context?.item !== void 0) {
1252
+ return context.item;
1253
+ }
1254
+ if (trimmed.startsWith("resolve:")) {
1255
+ return "";
1256
+ }
1257
+ const value = getFieldValue(input, trimmed);
1258
+ if (value === void 0 || value === null) {
1259
+ return "";
1260
+ }
1261
+ return String(value);
1262
+ }
1263
+ function resolveTemplate(template, input, context) {
1264
+ if (!TEMPLATE_RE.test(template)) {
1265
+ return template;
1266
+ }
1267
+ TEMPLATE_RE.lastIndex = 0;
1268
+ return template.replace(
1269
+ TEMPLATE_RE,
1270
+ (_match, expr) => resolveExpression(expr, input, context)
1271
+ );
1272
+ }
1273
+ function evaluateCondition(condition, input, context) {
1274
+ const resolved = resolveTemplate(condition, input, context);
1275
+ if (resolved === "" || resolved === "false" || resolved === "0") {
1276
+ return false;
1277
+ }
1278
+ return true;
1279
+ }
1280
+ function resolveEachItems(eachTemplate, input) {
1281
+ const fieldMatch = eachTemplate.match(/^\{\{([^}]+)\}\}$/);
1282
+ if (fieldMatch) {
1283
+ const value = getFieldValue(input, fieldMatch[1].trim());
1284
+ if (Array.isArray(value)) {
1285
+ return value.map(String);
1286
+ }
1287
+ }
1288
+ const resolved = resolveTemplate(eachTemplate, input);
1289
+ if (resolved === "") {
1290
+ return [];
1291
+ }
1292
+ return resolved.split("\n").map((s) => s.trim()).filter(Boolean);
1293
+ }
1294
+
1295
+ // src/event-mapping/engine.ts
1296
+ function eventTypeFromRule(rule) {
1297
+ return rule.name;
1298
+ }
1299
+ function resolveAttributes(attributes, input, context) {
1300
+ if (!attributes) return { axis: "" };
1301
+ const resolved = {};
1302
+ for (const [key, template] of Object.entries(attributes)) {
1303
+ const value = resolveTemplate(template, input, context);
1304
+ if (value !== "") {
1305
+ resolved[key] = value;
1306
+ }
1307
+ }
1308
+ return resolved;
1309
+ }
1310
+ function evaluateSpanRule(rule, input, spanNameToId, parentSpanId) {
1311
+ if (rule.condition && !evaluateCondition(rule.condition, input)) {
1312
+ return [];
1313
+ }
1314
+ const eventType = eventTypeFromRule(rule);
1315
+ const spans = [];
1316
+ const emitOne = (context) => {
1317
+ const spanId = generateId();
1318
+ spanNameToId.set(rule.name, spanId);
1319
+ const attrs = resolveAttributes(rule.attributes, input, context);
1320
+ if (!attrs.axis) {
1321
+ attrs.axis = rule.axis;
1322
+ }
1323
+ return {
1324
+ eventType,
1325
+ lifecycle: rule.lifecycle,
1326
+ spanId,
1327
+ parentSpanId,
1328
+ attributes: attrs
1329
+ };
1330
+ };
1331
+ if (rule.each) {
1332
+ const items = resolveEachItems(rule.each, input);
1333
+ for (const item of items) {
1334
+ spans.push(emitOne({ item }));
1335
+ }
1336
+ } else {
1337
+ spans.push(emitOne());
1338
+ }
1339
+ return spans;
1340
+ }
1341
+ function evaluateMapping(rule, hookInput) {
1342
+ const spanNameToId = /* @__PURE__ */ new Map();
1343
+ const spans = [];
1344
+ for (const spanRule of rule.spans) {
1345
+ const resolved = evaluateSpanRule(spanRule, hookInput, spanNameToId);
1346
+ spans.push(...resolved);
1347
+ }
1348
+ const links = [];
1349
+ if (rule.links) {
1350
+ for (const linkRule of rule.links) {
1351
+ const fromSpanId = spanNameToId.get(linkRule.from);
1352
+ const toSpanId = spanNameToId.get(linkRule.to);
1353
+ if (fromSpanId && toSpanId) {
1354
+ links.push({
1355
+ linkType: linkRule.type,
1356
+ fromSpanId,
1357
+ toSpanId
1358
+ });
1359
+ }
1360
+ }
1361
+ }
1362
+ return { spans, links };
1363
+ }
1364
+ async function loadEventMappingConfig(filePath) {
1365
+ const content = await readFile(filePath, "utf8");
1366
+ const parsed = filePath.endsWith(".json") ? JSON.parse(content) : parseYaml(content);
1367
+ const mappings = parsed.mappings ?? parsed.event_mapping ?? parsed;
1368
+ return { mappings };
1369
+ }
1370
+
1371
+ // src/event-mapping/lookup.ts
1372
+ function generateArtifactLookup(artifactDeclarations) {
1373
+ const patterns = [];
1374
+ for (const decl of artifactDeclarations) {
1375
+ for (const pattern of decl.path_patterns) {
1376
+ patterns.push({
1377
+ artifact_id: decl.artifact_id,
1378
+ pattern
1379
+ });
1380
+ }
1381
+ }
1382
+ return patterns;
1383
+ }
1384
+ function generateTaskPatterns() {
1385
+ return [
1386
+ { tag_prefix: "DSL_TASK", field: "task.id" },
1387
+ { tag_prefix: "AGENT_ROLE", field: "task.target_agent" },
1388
+ { tag_prefix: "WORKFLOW_PHASE", field: "workflow.phase" }
1389
+ ];
1390
+ }
1391
+ var TAG_RE = /\[([A-Z_]+)=([^\]]*)\]/g;
1392
+ function parseStructuredTags(input) {
1393
+ const result = {};
1394
+ if (!input) return result;
1395
+ let match;
1396
+ TAG_RE.lastIndex = 0;
1397
+ while ((match = TAG_RE.exec(input)) !== null) {
1398
+ result[match[1]] = match[2];
1399
+ }
1400
+ return result;
1401
+ }
1402
+
1403
+ // src/collector/human-events.ts
1404
+ var QUALITY_GATE_PATTERNS = [
1405
+ { pattern: /\bnpm test\b/, gateType: "test" },
1406
+ { pattern: /\bnpx jest\b/, gateType: "test" },
1407
+ { pattern: /\bnpx vitest\b/, gateType: "test" },
1408
+ { pattern: /\bcargo test\b/, gateType: "test" },
1409
+ { pattern: /\bpytest\b/, gateType: "test" },
1410
+ { pattern: /\bgo test\b/, gateType: "test" },
1411
+ { pattern: /\bnpm run lint\b/, gateType: "lint" },
1412
+ { pattern: /\bnpx eslint\b/, gateType: "lint" },
1413
+ { pattern: /\bcargo clippy\b/, gateType: "lint" },
1414
+ { pattern: /\bruff check\b/, gateType: "lint" },
1415
+ { pattern: /\bnpx tsc\b/, gateType: "typecheck" },
1416
+ { pattern: /\bnpm run typecheck\b/, gateType: "typecheck" },
1417
+ { pattern: /\bcargo check\b/, gateType: "typecheck" },
1418
+ { pattern: /\bnpm run build\b/, gateType: "build" },
1419
+ { pattern: /\bcargo build\b/, gateType: "build" },
1420
+ { pattern: /\bnext build\b/, gateType: "build" }
1421
+ ];
1422
+ var PR_URL_RE = /https:\/\/github\.com\/[^/\s]+\/[^/\s]+\/pull\/(\d+)/;
1423
+ function emitHumanInstruction(collector, options) {
1424
+ const spanId = generateId();
1425
+ const attachments = options.attachments ?? [];
1426
+ collector.emit({
1427
+ source: "cursor-hook",
1428
+ eventType: "human.instruction",
1429
+ lifecycle: "instant",
1430
+ spanId,
1431
+ parentSpanId: options.parentSpanId,
1432
+ attributes: {
1433
+ axis: "agent",
1434
+ "agent.session_id": options.sessionId,
1435
+ "human.prompt_length": options.prompt.length,
1436
+ "human.prompt_head": options.prompt.slice(0, 200),
1437
+ "human.has_attachments": attachments.length > 0,
1438
+ "human.attachment_count": attachments.length
1439
+ }
1440
+ });
1441
+ return spanId;
1442
+ }
1443
+ function matchQualityGate(command) {
1444
+ for (const { pattern, gateType } of QUALITY_GATE_PATTERNS) {
1445
+ if (pattern.test(command)) {
1446
+ return { gateType };
1447
+ }
1448
+ }
1449
+ return null;
1450
+ }
1451
+ function emitQualityGateResult(collector, options) {
1452
+ const match = matchQualityGate(options.command);
1453
+ if (!match) return null;
1454
+ const spanId = generateId();
1455
+ const verdict = options.exitCode === 0 ? "passed" : "failed";
1456
+ collector.emit({
1457
+ source: "cursor-hook",
1458
+ eventType: "quality_gate.result",
1459
+ lifecycle: "instant",
1460
+ spanId,
1461
+ parentSpanId: options.parentSpanId,
1462
+ attributes: {
1463
+ axis: "process",
1464
+ "agent.session_id": options.sessionId,
1465
+ "gate.type": match.gateType,
1466
+ "gate.command": options.command,
1467
+ "gate.exit_code": options.exitCode,
1468
+ "gate.verdict": verdict,
1469
+ ...options.durationMs !== void 0 ? { "gate.duration_ms": options.durationMs } : {}
1470
+ }
1471
+ });
1472
+ return spanId;
1473
+ }
1474
+ function extractPrUrl(output) {
1475
+ const match = output.match(PR_URL_RE);
1476
+ if (!match) return null;
1477
+ return {
1478
+ url: match[0],
1479
+ number: Number(match[1])
1480
+ };
1481
+ }
1482
+ function emitPromotionPr(collector, options) {
1483
+ if (!options.command.includes("gh pr create")) return null;
1484
+ const pr = extractPrUrl(options.output);
1485
+ if (!pr) return null;
1486
+ const spanId = generateId();
1487
+ collector.emit({
1488
+ source: "cursor-hook",
1489
+ eventType: "promotion.pr",
1490
+ lifecycle: "instant",
1491
+ spanId,
1492
+ parentSpanId: options.parentSpanId,
1493
+ attributes: {
1494
+ axis: "promotion",
1495
+ "agent.session_id": options.sessionId,
1496
+ "pr.url": pr.url,
1497
+ "pr.number": pr.number,
1498
+ "pr.command": options.command
1499
+ }
1500
+ });
1501
+ return spanId;
1502
+ }
1503
+
1228
1504
  // src/index.ts
1229
1505
  function createPipeline(options = {}) {
1230
1506
  const {
@@ -1302,6 +1578,18 @@ export {
1302
1578
  mapEventsToSpan,
1303
1579
  mapAllEventsToSpans,
1304
1580
  SqliteQueryAdapter,
1581
+ resolveTemplate,
1582
+ evaluateMapping,
1583
+ loadEventMappingConfig,
1584
+ generateArtifactLookup,
1585
+ generateTaskPatterns,
1586
+ parseStructuredTags,
1587
+ QUALITY_GATE_PATTERNS,
1588
+ emitHumanInstruction,
1589
+ matchQualityGate,
1590
+ emitQualityGateResult,
1591
+ extractPrUrl,
1592
+ emitPromotionPr,
1305
1593
  createPipeline
1306
1594
  };
1307
- //# sourceMappingURL=chunk-FVFGVBXM.js.map
1595
+ //# sourceMappingURL=chunk-M3KM2QTL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types/ids.ts","../src/collector/event-collector.ts","../src/collector/external-registrar.ts","../src/normalize/validator.ts","../src/normalize/normalizer.ts","../src/correlate/correlator.ts","../src/correlate/keys.ts","../src/enrich/rules/artifact.ts","../src/enrich/rules/process.ts","../src/enrich/rules/cross-axis.ts","../src/enrich/rules/promotion.ts","../src/enrich/enricher.ts","../src/sink/sqlite-sink.ts","../src/otel/otel-emitter.ts","../src/otel/span-mapper.ts","../src/query/sqlite-adapter.ts","../src/event-mapping/engine.ts","../src/event-mapping/template.ts","../src/event-mapping/lookup.ts","../src/collector/human-events.ts","../src/index.ts"],"sourcesContent":["/**\n * ID generation (UUIDv7) and time utilities for @aaac/observability.\n *\n * UUIDv7 format (RFC 9562):\n * bytes 0-5 : 48-bit Unix timestamp in milliseconds\n * byte 6 : 0x7_ (version 7, high nibble) | rand_a (low nibble)\n * byte 7 : rand_a (remaining 8 bits)\n * byte 8 : 0x8_-0xb_ (variant 10, top 2 bits) | rand_b\n * bytes 9-15 : rand_b\n */\nimport { randomBytes } from \"node:crypto\";\n\n/**\n * Generate a UUIDv7 string.\n * Monotonically ordered by creation time at millisecond granularity.\n */\nexport function generateId(): string {\n const ms = BigInt(Date.now());\n const buf = randomBytes(16);\n\n // Embed 48-bit Unix ms timestamp into bytes 0-5\n buf[0] = Number((ms >> 40n) & 0xffn);\n buf[1] = Number((ms >> 32n) & 0xffn);\n buf[2] = Number((ms >> 24n) & 0xffn);\n buf[3] = Number((ms >> 16n) & 0xffn);\n buf[4] = Number((ms >> 8n) & 0xffn);\n buf[5] = Number(ms & 0xffn);\n\n // Set version 7 in the high nibble of byte 6\n buf[6] = (buf[6] & 0x0f) | 0x70;\n\n // Set RFC 4122 variant 10 in the top 2 bits of byte 8\n buf[8] = (buf[8] & 0x3f) | 0x80;\n\n const hex = buf.toString(\"hex\");\n return (\n `${hex.slice(0, 8)}-` +\n `${hex.slice(8, 12)}-` +\n `${hex.slice(12, 16)}-` +\n `${hex.slice(16, 20)}-` +\n `${hex.slice(20, 32)}`\n );\n}\n\n/**\n * Current wall-clock time as Unix nanoseconds (BigInt).\n * Resolution is millisecond (Date.now() × 1_000_000).\n */\nexport function currentTimeUnixNano(): bigint {\n return BigInt(Date.now()) * 1_000_000n;\n}\n\n/**\n * Convert an ISO-8601 / RFC-3339 string to Unix nanoseconds (BigInt).\n * Falls back to current time if the string cannot be parsed.\n */\nexport function isoToUnixNano(iso: string): bigint {\n const ms = Date.parse(iso);\n if (Number.isNaN(ms)) {\n return currentTimeUnixNano();\n }\n return BigInt(ms) * 1_000_000n;\n}\n","/**\n * EventCollector — in-process entry point for the write pipeline.\n *\n * Responsibilities:\n * - Accept events from @aaac/runtime via emit() (in-process observer)\n * - Accept events from external sources (git-hook, ci, …) via registerExternalEvent()\n * - Wrap them as RawEvent with a receivedAt timestamp and forward to the downstream handler\n *\n * The downstream handler (pipeline) is injected via the constructor so that the\n * collector stays decoupled from normalizer / correlator / sink.\n */\nimport type { RawEvent, Lifecycle, AttrValue, CanonicalLink } from \"../types/canonical-event.js\";\n\n/** Minimal shape expected by both emit() and registerExternalEvent(). */\nexport interface EmitInput {\n source: string;\n eventType: string;\n lifecycle: Lifecycle;\n spanId?: string;\n parentSpanId?: string;\n attributes: Record<string, AttrValue>;\n links?: CanonicalLink[];\n}\n\n/** Callback that receives each RawEvent produced by the collector. */\nexport type RawEventHandler = (event: RawEvent) => void;\n\n/**\n * EventCollector wraps an injected pipeline handler and stamps each incoming\n * event with a receivedAt timestamp before forwarding.\n */\nexport class EventCollector {\n private readonly handler: RawEventHandler;\n\n constructor(handler: RawEventHandler) {\n this.handler = handler;\n }\n\n /**\n * In-process observer entry point.\n * Called by @aaac/runtime (or any in-process producer) with an event payload.\n */\n emit(event: EmitInput): void {\n const raw: RawEvent = {\n ...event,\n receivedAt: new Date().toISOString(),\n };\n this.handler(raw);\n }\n\n /**\n * External event registration API.\n * Called by git hooks, Cursor hooks, CI, or the `aaac-observ record` CLI.\n * Identical wire format to emit(); separated to aid future routing logic.\n */\n registerExternalEvent(event: EmitInput): void {\n const raw: RawEvent = {\n ...event,\n receivedAt: new Date().toISOString(),\n };\n this.handler(raw);\n }\n}\n","/**\n * ExternalEventRegistrar — thin facade implementing the external event\n * registration contract from architecture.md §9.\n *\n * External processes (git hooks, Cursor hooks, CI) call registerEvent() to\n * inject events into the same write pipeline as in-process events.\n */\nimport type { CanonicalLink, Lifecycle, AttrValue } from \"../types/canonical-event.js\";\nimport type { EventCollector } from \"./event-collector.js\";\n\n/** Typed input for external event sources (architecture.md §9). */\nexport interface RawExternalEvent {\n /** Event origin: 'git-hook' | 'cursor-hook' | 'ci' | ... */\n source: string;\n eventType: string;\n lifecycle: Lifecycle;\n spanId?: string;\n parentSpanId?: string;\n attributes: Record<string, AttrValue>;\n links?: CanonicalLink[];\n}\n\n/** Interface contract for external event registration (architecture.md §9). */\nexport interface ExternalEventRegistrar {\n registerEvent(event: RawExternalEvent): void;\n}\n\n/**\n * Default implementation — delegates to EventCollector.registerExternalEvent().\n */\nexport class DefaultExternalRegistrar implements ExternalEventRegistrar {\n constructor(private readonly collector: EventCollector) {}\n\n registerEvent(event: RawExternalEvent): void {\n this.collector.registerExternalEvent(event);\n }\n}\n","/**\n * Validator — checks that a RawEvent has all required fields before normalization.\n * Throws NormalizationError with a descriptive message on failure.\n */\nimport type { RawEvent } from \"../types/canonical-event.js\";\n\nconst VALID_LIFECYCLES = new Set([\"open\", \"close\", \"event\", \"instant\"]);\n\n/** Thrown when a RawEvent fails validation. */\nexport class NormalizationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"NormalizationError\";\n }\n}\n\n/**\n * Validate a RawEvent.\n * Throws NormalizationError listing all violated constraints.\n */\nexport function validateRawEvent(event: RawEvent): void {\n const errors: string[] = [];\n\n if (!event.source || typeof event.source !== \"string\") {\n errors.push(\"'source' must be a non-empty string\");\n }\n if (!event.eventType || typeof event.eventType !== \"string\") {\n errors.push(\"'eventType' must be a non-empty string\");\n }\n if (!event.lifecycle || !VALID_LIFECYCLES.has(event.lifecycle)) {\n errors.push(\n `'lifecycle' must be one of: ${[...VALID_LIFECYCLES].join(\", \")}`\n );\n }\n if (!event.receivedAt || typeof event.receivedAt !== \"string\") {\n errors.push(\"'receivedAt' must be a non-empty string\");\n }\n if (event.attributes === null || typeof event.attributes !== \"object\" || Array.isArray(event.attributes)) {\n errors.push(\"'attributes' must be a plain object\");\n }\n\n if (errors.length > 0) {\n throw new NormalizationError(\n `Invalid RawEvent: ${errors.join(\"; \")}`\n );\n }\n}\n","/**\n * Normalizer — converts RawEvent → CanonicalEvent.\n *\n * Responsibilities (architecture.md §4.1 / §3):\n * - Required-key validation (delegated to validator.ts)\n * - Type coercion: attribute values are coerced to AttrValue\n * - Default values: id, timeUnixNano, spanId, traceId are generated if absent\n * - Link declaration conversion: RawEvent.links → CanonicalEvent.links\n * - Extraction of well-known scalar fields: sessionId, taskId, runId\n *\n * traceId *propagation* (child → parent) is the Correlator's responsibility;\n * the Normalizer only generates a fresh traceId as the default.\n */\nimport type { RawEvent, CanonicalEvent, AttrValue } from \"../types/canonical-event.js\";\nimport { generateId, isoToUnixNano } from \"../types/ids.js\";\nimport { validateRawEvent } from \"./validator.js\";\n\n/** Coerce an unknown attribute value to AttrValue (string | number | boolean). */\nfunction coerceAttrValue(value: unknown): AttrValue {\n if (\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n ) {\n return value;\n }\n return String(value);\n}\n\n/** Coerce all attribute values in the map. */\nfunction coerceAttributes(\n raw: Record<string, unknown>\n): Record<string, AttrValue> {\n const result: Record<string, AttrValue> = {};\n for (const [k, v] of Object.entries(raw)) {\n result[k] = coerceAttrValue(v);\n }\n return result;\n}\n\n/**\n * Normalizer converts a validated RawEvent into a CanonicalEvent.\n * An instance is stateless — the same instance may be reused for many events.\n */\nexport class Normalizer {\n /**\n * Validate and normalise a single RawEvent.\n * Throws NormalizationError on invalid input.\n */\n normalize(raw: RawEvent): CanonicalEvent {\n validateRawEvent(raw);\n\n const id = generateId();\n const timeUnixNano = isoToUnixNano(raw.receivedAt);\n const spanId = raw.spanId ?? generateId();\n const traceId = generateId(); // Correlator propagates the correct traceId later\n\n const attributes = coerceAttributes(\n raw.attributes as Record<string, unknown>\n );\n\n // Extract well-known scalars from attributes\n const sessionId = extractString(attributes, \"session_id\");\n const taskId = extractString(attributes, \"task_id\");\n const runId = extractString(attributes, \"run_id\");\n\n return {\n id,\n timeUnixNano,\n source: raw.source,\n eventType: raw.eventType,\n lifecycle: raw.lifecycle,\n traceId,\n spanId,\n parentSpanId: raw.parentSpanId,\n runId,\n sessionId,\n taskId,\n attributes,\n links: raw.links ?? [],\n };\n }\n}\n\nfunction extractString(\n attrs: Record<string, AttrValue>,\n key: string\n): string | undefined {\n const v = attrs[key];\n return typeof v === \"string\" && v.length > 0 ? v : undefined;\n}\n","/**\n * Correlator — generic, domain-agnostic span correlation.\n *\n * Responsibilities (architecture.md §4.1 / §8):\n * - span lifecycle pairing: tracks 'open' events by spanId; pairs with 'close'\n * - duration computation: stored in the in-memory state so SqliteSink can read\n * startTime when processing the close event\n * - traceId propagation: child spans inherit their parent's traceId\n * - in-memory short-term cache: cacheGet / cacheSet for Enricher use (Phase 1+)\n *\n * No domain logic. The correlator does not know about agent / process / promotion axes.\n */\nimport type { CanonicalEvent } from \"../types/canonical-event.js\";\n\n/** Cached state for an open span. */\ninterface OpenSpanEntry {\n event: CanonicalEvent;\n}\n\n/**\n * Correlator maintains in-memory state across events in the same write session.\n * A single instance should be reused for the lifetime of the write pipeline.\n */\nexport class Correlator {\n /** Open spans awaiting a matching 'close' event (keyed by spanId). */\n private readonly openSpans = new Map<string, OpenSpanEntry>();\n\n /** General-purpose short-term key-value cache (architecture.md §8). */\n private readonly cache = new Map<string, unknown>();\n\n /**\n * Correlate a single CanonicalEvent.\n *\n * Mutates the event's traceId if a parent span is found in the open set.\n * Updates open span state according to the event's lifecycle.\n *\n * Returns the (possibly updated) CanonicalEvent.\n */\n correlate(event: CanonicalEvent): CanonicalEvent {\n let result = { ...event };\n\n // traceId propagation: child events inherit parent's traceId\n if (result.parentSpanId !== undefined) {\n const parentEntry = this.openSpans.get(result.parentSpanId);\n if (parentEntry !== undefined) {\n result = { ...result, traceId: parentEntry.event.traceId };\n }\n }\n\n switch (result.lifecycle) {\n case \"open\":\n this.openSpans.set(result.spanId, { event: result });\n break;\n\n case \"close\": {\n // Duration computation is delegated to the sink (it has the stored\n // start_time and can do the arithmetic in SQL). We only clean up state.\n this.openSpans.delete(result.spanId);\n break;\n }\n\n case \"event\":\n // Span event: no state change needed; traceId already propagated above.\n break;\n\n case \"instant\":\n // Instant span: self-contained; no open state to track.\n break;\n }\n\n return result;\n }\n\n /**\n * Retrieve the currently-open entry for a span (undefined if not open).\n * Useful for SqliteSink to look up start_time without a DB round-trip.\n */\n getOpenSpan(spanId: string): CanonicalEvent | undefined {\n return this.openSpans.get(spanId)?.event;\n }\n\n // ── Generic key-value cache (architecture.md §8) ──────────────────────────\n\n /** Store a value under an arbitrary key (TTL not enforced in Phase 0). */\n cacheSet(key: string, value: unknown): void {\n this.cache.set(key, value);\n }\n\n /** Retrieve a cached value; returns undefined if absent. */\n cacheGet<T = unknown>(key: string): T | undefined {\n return this.cache.get(key) as T | undefined;\n }\n\n /** Remove a cached entry. */\n cacheDel(key: string): void {\n this.cache.delete(key);\n }\n\n /** Return the number of currently-open spans (useful for testing). */\n get openSpanCount(): number {\n return this.openSpans.size;\n }\n}\n","/**\n * Cache key helpers for the Correlator's in-memory short-term store.\n * architecture.md §8.2\n */\n\nexport const keys = {\n /** Per-run context: run:{id} */\n run: (id: string): string => `run:${id}`,\n /** Latest span in a trace: trace:{id}:latest */\n traceLatest: (id: string): string => `trace:${id}:latest`,\n /** Per-session context: session:{id} */\n session: (id: string): string => `session:${id}`,\n /** Tool call tracking: toolcall:{id} */\n toolCall: (id: string): string => `toolcall:${id}`,\n /** Request tracking: request:{id} */\n request: (id: string): string => `request:${id}`,\n} as const;\n","/**\n * R7 — Artifact ID resolution for process.edit events.\n *\n * shift-left-event-mapping.md §5.1:\n * \"R7: artifact_id 未解決 → file_path → artifact-lookup (§3.3) — longest-match glob,\n * 未マッチは unknown\"\n *\n * Resolution is via STRUCTURED lookup only (path glob → artifact_id).\n * No natural-language guessing; unresolved → \"unknown\".\n */\nimport type { EnrichRule, ArtifactPattern } from \"../api.js\";\n\n// ── Glob matching ─────────────────────────────────────────────────────────────\n\n/**\n * Convert a glob segment (no \"**\" present) to a regex fragment.\n * Escapes regex metacharacters and replaces glob wildcards * and ?.\n */\nfunction globSegmentToRegex(segment: string): string {\n return segment\n .replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\") // escape regex special chars\n .replace(/\\*/g, \"[^/]*\") // * → any non-separator sequence\n .replace(/\\?/g, \"[^/]\"); // ? → any single non-separator char\n}\n\n// globToRegex: convert a glob pattern to a RegExp.\n// Wildcards: ** = zero-or-more path segments (across slashes)\n// * = zero-or-more non-separator chars\n// ? = exactly one non-separator char\n// Basename-only patterns (no slash in pattern) match the filename anywhere.\n// Algorithm for **: split on \"**/\" boundaries; insert \"zero-or-more segments\"\n// regex between the split parts so src-star-star-foo.ts matches src/foo.ts.\nexport function globToRegex(pattern: string): RegExp {\n // Basename-only patterns (no \"/\") match the last path component anywhere.\n if (!pattern.includes(\"/\")) {\n const base = globSegmentToRegex(pattern);\n return new RegExp(`(?:^|/)${base}$`);\n }\n\n // Split on \"**/\" to handle zero-or-more segment matches.\n // \"src/**/*.ts\" → [\"src/\", \"*.ts\"]\n // \"**/*.md\" → [\"\", \"*.md\"]\n // \"a/b/c.ts\" → [\"a/b/c.ts\"] (no split, no ** present)\n const parts = pattern.split(\"**/\");\n\n let result = \"^\";\n for (let i = 0; i < parts.length; i++) {\n if (i > 0) {\n // At each \"**/\" split boundary: zero-or-more path segments (including none)\n result += \"(?:.*/)?\";\n }\n result += globSegmentToRegex(parts[i]);\n }\n result += \"$\";\n\n return new RegExp(result);\n}\n\n// ── Longest-match lookup ──────────────────────────────────────────────────────\n\n/**\n * Resolve a file path to an artifact_id using longest-match glob.\n *\n * Among all patterns that match `filePath`, the one with the longest pattern\n * string wins (longer = more specific). Ties are broken by declaration order\n * (first wins). Returns \"unknown\" if no pattern matches.\n *\n * shift-left-event-mapping.md §3.3\n */\nexport function lookupArtifact(\n filePath: string,\n patterns: ArtifactPattern[],\n): string {\n let bestArtifactId: string | undefined;\n let bestLen = -1;\n\n for (const entry of patterns) {\n const rx = globToRegex(entry.pattern);\n if (rx.test(filePath)) {\n if (entry.pattern.length > bestLen) {\n bestLen = entry.pattern.length;\n bestArtifactId = entry.artifact_id;\n }\n }\n }\n\n return bestArtifactId ?? \"unknown\";\n}\n\n// ── R7 enrichment rule ────────────────────────────────────────────────────────\n\n/**\n * Create the R7 artifact-resolution enrichment rule.\n *\n * Applies to: process.edit (instant)\n * Action: sets `edit.artifact_id` on the event attributes via lookupArtifact.\n * If no pattern matches, sets `edit.artifact_id = \"unknown\"`.\n * Skips if `edit.artifact_id` is already set to a non-empty, non-\"unknown\" value.\n *\n * The `patterns` list is provided via config/constructor (no hardcoded project values).\n */\nexport function createArtifactRule(patterns: ArtifactPattern[]): EnrichRule {\n return function artifactRule(event, ctx): void {\n if (event.eventType !== \"process.edit\" || event.lifecycle !== \"instant\") {\n return;\n }\n\n const filePath = event.attributes[\"edit.path\"];\n if (typeof filePath !== \"string\" || filePath.length === 0) {\n return; // No path to resolve\n }\n\n // Skip if already fully resolved by the event producer (non-empty, non-\"unknown\")\n const existing = event.attributes[\"edit.artifact_id\"];\n if (\n typeof existing === \"string\" &&\n existing.length > 0 &&\n existing !== \"unknown\"\n ) {\n return;\n }\n\n const artifactId = ctx.api.lookupArtifact(filePath);\n event.attributes[\"edit.artifact_id\"] = artifactId;\n ctx.api.logDebug(\n `R7: resolved artifact_id=${artifactId} for path=${filePath}`,\n );\n };\n}\n","/**\n * R3/R4 — Process parent resolution for process.edit and process.tool events.\n *\n * shift-left-event-mapping.md §5.1:\n * R3: process.edit → process.task (parent)\n * \"session_id の active task スタック + file_path→artifact で一意化\"\n * \"単一 active task なら確定、複数なら artifact 一致で一意化、不能なら ambiguous\"\n * R4: process.tool → process.task (parent)\n * \"単一なら確定、複数なら ambiguous\"\n *\n * All resolution via STRUCTURED keys only:\n * - active task stack per session (in-memory cache)\n * - artifact_id already set by R7 (for disambiguation in R3)\n * - Never guess from natural language or timing\n *\n * Cache schema maintained by this rule:\n * session:{sid}:active_tasks → ActiveTask[] (currently open tasks)\n * session:{sid}:all_task_spans → string[] (all task spanIds, open + closed, for R5)\n * session:{sid}:task_files:{taskSpanId} → string[] (accumulated modified files, for R5)\n */\nimport type { EnrichRule } from \"../api.js\";\n\n/** A currently-open process.task span tracked on the active stack. */\nexport interface ActiveTask {\n spanId: string;\n taskId: string | undefined;\n}\n\n// ── Cache key helpers (exported for cross-axis.ts) ────────────────────────────\n\nexport function activeTasksKey(sessionId: string): string {\n return `session:${sessionId}:active_tasks`;\n}\n\nexport function allTaskSpansKey(sessionId: string): string {\n return `session:${sessionId}:all_task_spans`;\n}\n\nexport function taskFilesKey(sessionId: string, taskSpanId: string): string {\n return `session:${sessionId}:task_files:${taskSpanId}`;\n}\n\n// ── R3/R4 enrichment rule ─────────────────────────────────────────────────────\n\n/**\n * Create the R3/R4 process-parent enrichment rule.\n *\n * Handles three event types:\n * 1. process.task open — push task onto active-task stack\n * 2. process.task close — pop task from active-task stack\n * 3. process.edit instant — resolve parentSpanId/taskId (R3)\n * 4. process.tool instant — resolve parentSpanId/taskId (R4)\n *\n * Resolution algorithm (R3/R4):\n * - If event already has parentSpanId → skip (already resolved)\n * - 0 active tasks → leave provisional (no parentSpanId)\n * - 1 active task → set parentSpanId + taskId (confirmed)\n * - 2+ active tasks (R3) → try to disambiguate by edit.artifact_id match\n * against task's artifact_ids; if still ambiguous → set reconcile.status=ambiguous\n * - 2+ active tasks (R4) → set reconcile.status=ambiguous (no artifact disambig)\n */\nexport function createProcessRule(): EnrichRule {\n return function processRule(event, ctx): void {\n const sid = event.sessionId;\n\n // ── Maintain active-task stack on process.task open/close ─────────────────\n\n if (event.eventType === \"process.task\") {\n if (!sid) return;\n\n if (event.lifecycle === \"open\") {\n const tasks =\n ctx.api.cacheGet<ActiveTask[]>(activeTasksKey(sid)) ?? [];\n tasks.push({ spanId: event.spanId, taskId: event.taskId });\n ctx.api.cacheSet(activeTasksKey(sid), tasks);\n\n // Register span in the all-task list (kept after close for R5)\n const allSpans =\n ctx.api.cacheGet<string[]>(allTaskSpansKey(sid)) ?? [];\n if (!allSpans.includes(event.spanId)) {\n allSpans.push(event.spanId);\n ctx.api.cacheSet(allTaskSpansKey(sid), allSpans);\n }\n // Initialise the file-tracking list for this task (used by R5)\n ctx.api.cacheSet(taskFilesKey(sid, event.spanId), []);\n ctx.api.logDebug(\n `R3/R4: pushed task spanId=${event.spanId} session=${sid}`,\n );\n return;\n }\n\n if (event.lifecycle === \"close\") {\n const tasks =\n ctx.api.cacheGet<ActiveTask[]>(activeTasksKey(sid)) ?? [];\n const updated = tasks.filter((t) => t.spanId !== event.spanId);\n ctx.api.cacheSet(activeTasksKey(sid), updated);\n ctx.api.logDebug(\n `R3/R4: popped task spanId=${event.spanId} session=${sid}`,\n );\n return;\n }\n\n return; // other lifecycles (event, instant) — no action for process.task\n }\n\n // ── R3: process.edit → resolve parent task ────────────────────────────────\n\n if (event.eventType === \"process.edit\" && event.lifecycle === \"instant\") {\n if (event.parentSpanId) return; // already resolved upstream\n if (!sid) return;\n\n const tasks =\n ctx.api.cacheGet<ActiveTask[]>(activeTasksKey(sid)) ?? [];\n\n if (tasks.length === 0) {\n // No open tasks — leave provisional (empty parentSpanId)\n return;\n }\n\n if (tasks.length === 1) {\n // Single active task → confirmed\n event.parentSpanId = tasks[0].spanId;\n event.taskId = tasks[0].taskId;\n\n // Record this file under the task for R5\n const fp = event.attributes[\"edit.path\"];\n if (typeof fp === \"string\" && fp.length > 0) {\n const files =\n ctx.api.cacheGet<string[]>(\n taskFilesKey(sid, tasks[0].spanId),\n ) ?? [];\n if (!files.includes(fp)) {\n files.push(fp);\n ctx.api.cacheSet(taskFilesKey(sid, tasks[0].spanId), files);\n }\n }\n ctx.api.logDebug(\n `R3: resolved parent=${tasks[0].spanId} for process.edit`,\n );\n return;\n }\n\n // Multiple active tasks — try to disambiguate by artifact_id\n // R7 must have already run to set edit.artifact_id\n const artifactId = event.attributes[\"edit.artifact_id\"];\n if (typeof artifactId === \"string\" && artifactId !== \"unknown\") {\n // Look for a task that owns this artifact\n // Task artifact ownership is stored by the task's `task.artifact_id` attribute\n // if it was set at emit time (structured key — no guessing)\n const owner = tasks.find(\n (t) => {\n // Try to look up the task's known artifact from cache\n const taskArtifact = ctx.api.cacheGet<string>(\n `session:${sid}:task_artifact:${t.spanId}`,\n );\n return taskArtifact === artifactId;\n },\n );\n if (owner) {\n event.parentSpanId = owner.spanId;\n event.taskId = owner.taskId;\n const fp = event.attributes[\"edit.path\"];\n if (typeof fp === \"string\" && fp.length > 0) {\n const files =\n ctx.api.cacheGet<string[]>(taskFilesKey(sid, owner.spanId)) ??\n [];\n if (!files.includes(fp)) {\n files.push(fp);\n ctx.api.cacheSet(taskFilesKey(sid, owner.spanId), files);\n }\n }\n ctx.api.logDebug(\n `R3: disambiguated parent=${owner.spanId} by artifact=${artifactId}`,\n );\n return;\n }\n }\n\n // Cannot disambiguate — mark reconcile.status=ambiguous, keep provisional\n event.attributes[\"reconcile.status\"] = \"ambiguous\";\n ctx.api.logDebug(\n `R3: ambiguous (${tasks.length} active tasks, no unique artifact match)`,\n );\n return;\n }\n\n // ── R4: process.tool → resolve parent task ────────────────────────────────\n\n if (event.eventType === \"process.tool\" && event.lifecycle === \"instant\") {\n if (event.parentSpanId) return; // already resolved upstream\n if (!sid) return;\n\n const tasks =\n ctx.api.cacheGet<ActiveTask[]>(activeTasksKey(sid)) ?? [];\n\n if (tasks.length === 0) {\n // No open tasks — leave provisional\n return;\n }\n\n if (tasks.length === 1) {\n // Single active task → confirmed\n event.parentSpanId = tasks[0].spanId;\n event.taskId = tasks[0].taskId;\n ctx.api.logDebug(\n `R4: resolved parent=${tasks[0].spanId} for process.tool`,\n );\n return;\n }\n\n // Multiple tasks, no artifact disambiguation for tools → ambiguous\n event.attributes[\"reconcile.status\"] = \"ambiguous\";\n ctx.api.logDebug(\n `R4: ambiguous (${tasks.length} active tasks)`,\n );\n }\n };\n}\n\n/**\n * Record a task's primary artifact in the cache so that R3 disambiguation can use it.\n * Call this when a process.task open event carries a known artifact_id attribute.\n *\n * This is an optional supplement to the createProcessRule — the main rule will\n * call lookupArtifact on the task's own attributes if available, but the cache\n * entry is more reliable for disambiguation.\n */\nexport function recordTaskArtifact(\n sessionId: string,\n taskSpanId: string,\n artifactId: string,\n cacheSet: (key: string, value: unknown) => void,\n): void {\n cacheSet(`session:${sessionId}:task_artifact:${taskSpanId}`, artifactId);\n}\n","/**\n * R5 — Cross-axis link: process.task → promotion.commit (materializes_as_commit).\n *\n * shift-left-event-mapping.md §5.1:\n * R5: \"on promotion.commit close, addLink materializes_as_commit for each task\n * whose modified_files ∩ committed_files is non-empty\"\n *\n * Correlation key: modified_files (accumulated by R3 on process.edit) ∩ committed_files\n * (attribute on the promotion.commit event).\n *\n * Implementation:\n * - process.ts R3/R4 rule maintains the task file lists:\n * session:{sid}:all_task_spans → string[] (all task spanIds)\n * session:{sid}:task_files:{taskSpanId} → string[] (files modified under that task)\n * - This rule reads those lists on promotion.commit close and calls addLink\n * for each intersecting task. The link is attached to the commit event.\n *\n * architecture.md §5.2:\n * \"事後発見リンクはリンクを発見したイベントの links に追加する\"\n * (Post-discovery links are added to the discovering event's links.)\n * → The materializes_as_commit link is added to the promotion.commit event.\n */\nimport { allTaskSpansKey, taskFilesKey } from \"./process.js\";\nimport type { EnrichRule } from \"../api.js\";\n\n// ── R5 enrichment rule ────────────────────────────────────────────────────────\n\n/**\n * Create the R5 cross-axis enrichment rule.\n *\n * Applies to: promotion.commit (close)\n * Action: for each known task whose accumulated modified_files intersect the\n * committed_files attribute, add a materializes_as_commit link to the\n * promotion.commit event (pointing to the task's spanId).\n *\n * committed_files must be a JSON-encoded string array on the commit event.\n * If absent or unparseable, the rule skips silently (fail-open).\n */\nexport function createCrossAxisRule(): EnrichRule {\n return function crossAxisRule(event, ctx): void {\n if (\n event.eventType !== \"promotion.commit\" ||\n event.lifecycle !== \"close\"\n ) {\n return;\n }\n\n const sid = event.sessionId;\n const committedFilesRaw = event.attributes[\"committed_files\"];\n if (!sid || typeof committedFilesRaw !== \"string\") {\n return;\n }\n\n let committedFiles: string[];\n try {\n const parsed: unknown = JSON.parse(committedFilesRaw);\n if (!Array.isArray(parsed)) {\n ctx.api.logDebug(\n \"R5: committed_files is not an array\",\n committedFilesRaw,\n );\n return;\n }\n committedFiles = parsed.filter((x): x is string => typeof x === \"string\");\n } catch {\n ctx.api.logDebug(\"R5: failed to parse committed_files\", committedFilesRaw);\n return;\n }\n\n if (committedFiles.length === 0) return;\n\n const committedSet = new Set(committedFiles);\n const allTaskSpans =\n ctx.api.cacheGet<string[]>(allTaskSpansKey(sid)) ?? [];\n\n for (const taskSpanId of allTaskSpans) {\n const taskFiles =\n ctx.api.cacheGet<string[]>(taskFilesKey(sid, taskSpanId)) ?? [];\n\n const hasIntersection = taskFiles.some((f) => committedSet.has(f));\n if (hasIntersection) {\n ctx.api.addLink({\n linkType: \"materializes_as_commit\",\n targetSpanId: taskSpanId,\n });\n ctx.api.logDebug(\n `R5: added materializes_as_commit → task=${taskSpanId}`,\n );\n }\n }\n };\n}\n","/**\n * R1/R2 — Promotion change → file → commit correlation.\n *\n * shift-left-event-mapping.md §5.1:\n * R1: promotion.change → promotion.file (parent / contains_change)\n * \"post-commit time, link orphan changes to file\"\n * Correlation key: session_id + file.path, commit time window\n * R2: promotion.file → promotion.commit (parent)\n * \"post-commit time, emitRecord で parent を commit に設定\"\n *\n * Algorithm:\n * 1. On promotion.change (instant): cache the change spanId by (session_id, file.path)\n * 2. On promotion.commit close:\n * - Parse committed_files from attributes (JSON string array)\n * - For each committed file:\n * a. Emit a promotion.file event with parentSpanId=commitSpanId (R2)\n * b. Attach contains_change links to all cached change spanIds for that file (R1)\n * - Clear the change cache for committed files\n *\n * Cache schema:\n * session:{sid}:changes:{filePath} → ChangeRef[]\n */\nimport { generateId } from \"../../types/ids.js\";\nimport type { CanonicalEvent, CanonicalLink } from \"../../types/canonical-event.js\";\nimport type { EnrichRule } from \"../api.js\";\n\n/** Reference to a cached promotion.change event. */\ninterface ChangeRef {\n spanId: string;\n traceId: string;\n}\n\n// ── Cache key helpers ─────────────────────────────────────────────────────────\n\nfunction changesKey(sessionId: string, filePath: string): string {\n return `session:${sessionId}:changes:${filePath}`;\n}\n\n// ── R1/R2 enrichment rule ─────────────────────────────────────────────────────\n\n/**\n * Create the R1/R2 promotion-correlation enrichment rule.\n *\n * Handles two event types:\n * 1. promotion.change instant — cache the change by (session_id, file.path)\n * 2. promotion.commit close — emit promotion.file per committed file (R2),\n * with contains_change links to cached changes (R1)\n *\n * The committed_files attribute on promotion.commit must be a JSON-encoded\n * string array (e.g., '[\"src/foo.ts\",\"README.md\"]').\n * If absent or unparseable, the rule skips silently (fail-open).\n */\nexport function createPromotionRule(): EnrichRule {\n return function promotionRule(event, ctx): void {\n // ── R1a: cache promotion.change by session_id + file.path ─────────────────\n\n if (\n event.eventType === \"promotion.change\" &&\n event.lifecycle === \"instant\"\n ) {\n const sid = event.sessionId;\n const fp = event.attributes[\"file.path\"];\n if (!sid || typeof fp !== \"string\" || fp.length === 0) {\n return;\n }\n\n const key = changesKey(sid, fp);\n const existing = ctx.api.cacheGet<ChangeRef[]>(key) ?? [];\n existing.push({ spanId: event.spanId, traceId: event.traceId });\n ctx.api.cacheSet(key, existing);\n ctx.api.logDebug(\n `R1: cached promotion.change spanId=${event.spanId} file=${fp} session=${sid}`,\n );\n return;\n }\n\n // ── R1/R2: on promotion.commit close, emit promotion.file per file ────────\n\n if (\n event.eventType === \"promotion.commit\" &&\n event.lifecycle === \"close\"\n ) {\n const sid = event.sessionId;\n const committedFilesRaw = event.attributes[\"committed_files\"];\n if (!sid || typeof committedFilesRaw !== \"string\") {\n return;\n }\n\n let committedFiles: string[];\n try {\n const parsed: unknown = JSON.parse(committedFilesRaw);\n if (!Array.isArray(parsed)) {\n ctx.api.logDebug(\"R1/R2: committed_files is not an array\", committedFilesRaw);\n return;\n }\n committedFiles = parsed.filter((x): x is string => typeof x === \"string\");\n } catch {\n ctx.api.logDebug(\"R1/R2: failed to parse committed_files\", committedFilesRaw);\n return;\n }\n\n const commitSpanId = event.spanId;\n const commitTraceId = event.traceId;\n\n for (const fp of committedFiles) {\n const cacheKey = changesKey(sid, fp);\n const changeRefs = ctx.api.cacheGet<ChangeRef[]>(cacheKey) ?? [];\n\n // Build contains_change links from the promotion.file to each cached change\n const links: CanonicalLink[] = changeRefs.map((ref) => ({\n linkType: \"contains_change\",\n targetSpanId: ref.spanId,\n // Omit targetTraceId if same trace as the commit (most common case)\n targetTraceId:\n ref.traceId !== commitTraceId ? ref.traceId : undefined,\n }));\n\n // R2: emit promotion.file with parentSpanId = commitSpanId\n const fileEvent: CanonicalEvent = {\n id: generateId(),\n timeUnixNano: ctx.api.nowUnixNano(),\n source: \"enricher\",\n eventType: \"promotion.file\",\n lifecycle: \"instant\",\n traceId: commitTraceId,\n spanId: generateId(),\n parentSpanId: commitSpanId, // R2: parent = commit\n sessionId: sid,\n runId: event.runId,\n taskId: event.taskId,\n attributes: {\n \"file.path\": fp,\n \"commit.span_id\": commitSpanId,\n },\n links, // R1: contains_change links\n };\n\n ctx.api.emitRecord(fileEvent);\n ctx.api.logDebug(\n `R2: emitted promotion.file file=${fp} commit=${commitSpanId} changes=${links.length}`,\n );\n\n // Clear the change cache for this file — changes have been accounted for\n ctx.api.cacheSet(cacheKey, []);\n }\n }\n };\n}\n","/**\n * Enricher — domain-specific enrichment stage in the write-path pipeline.\n *\n * architecture.md §4.1 (Enricher role) / §5.5 / shift-left-event-mapping.md §5:\n * Positioned between Correlator and the sinks (OtelEmitter + SqliteSink).\n * Applies enrichment rules to each CanonicalEvent:\n * - attribute completion (artifact_id, reconcile.status)\n * - parent resolution (parentSpanId, taskId)\n * - cross-axis link addition (materializes_as_commit, contains_change)\n * - derived event emission (promotion.file)\n *\n * Design:\n * - Reuses the Correlator's in-memory short-term store for cacheGet/cacheSet\n * - fail-open: a rule that throws is logged and execution continues\n * - emitRecord queues derived CanonicalEvents (written to sinks after the\n * current event's enrichment completes; NOT re-enriched to prevent cycles)\n * - dispatch: all registered rules run for every event; each rule checks\n * its own applicability (eventType / lifecycle / field presence)\n */\nimport type { CanonicalEvent } from \"../types/canonical-event.js\";\nimport type { Correlator } from \"../correlate/correlator.js\";\nimport { currentTimeUnixNano } from \"../types/ids.js\";\nimport { lookupArtifact } from \"./rules/artifact.js\";\nimport type { EnrichApi, EnrichContext, EnrichRule, ArtifactPattern } from \"./api.js\";\n\n// ── Public types ──────────────────────────────────────────────────────────────\n\nexport interface EnricherOptions {\n /** Correlator instance — its cache is shared with the Enricher (architecture.md §8). */\n correlator: Correlator;\n /**\n * Artifact path-glob patterns for lookupArtifact.\n * Provided via config / createPipeline options; generated by Phase 4 binding.\n * Pass [] for no artifact resolution (all paths → \"unknown\").\n */\n artifactPatterns?: ArtifactPattern[];\n}\n\n/** Return value of Enricher.enrich(). */\nexport interface EnrichResult {\n /** The (possibly mutated) canonical event ready to write to sinks. */\n enriched: CanonicalEvent;\n /**\n * Derived canonical events queued by rules via api.emitRecord().\n * Caller writes these to sinks after the primary event (no re-enrichment).\n */\n derived: CanonicalEvent[];\n}\n\n// ── Enricher ──────────────────────────────────────────────────────────────────\n\nexport class Enricher {\n private readonly correlator: Correlator;\n private readonly artifactPatterns: ArtifactPattern[];\n private readonly rules: EnrichRule[];\n\n /**\n * @param options Correlator + optional artifact patterns\n * @param rules Enrichment rules to apply in order.\n * Use createDefaultRules() for the standard Phase 5 rule set.\n */\n constructor(options: EnricherOptions, rules: EnrichRule[]) {\n this.correlator = options.correlator;\n this.artifactPatterns = options.artifactPatterns ?? [];\n this.rules = rules;\n }\n\n /**\n * Enrich a single CanonicalEvent.\n *\n * Returns a shallow copy of the event (with mutated fields from rules) and\n * a list of derived events queued by api.emitRecord().\n *\n * fail-open: each rule runs in a try/catch; a throwing rule logs to stderr\n * and does NOT prevent the event from reaching the sinks.\n */\n enrich(event: CanonicalEvent): EnrichResult {\n // Work on a shallow copy so the caller's object is not mutated\n const mutableEvent: CanonicalEvent = {\n ...event,\n attributes: { ...event.attributes },\n links: [...event.links],\n };\n\n const derived: CanonicalEvent[] = [];\n const patterns = this.artifactPatterns;\n const correlator = this.correlator;\n\n const api: EnrichApi = {\n cacheGet: <T>(k: string) => correlator.cacheGet<T>(k),\n cacheSet: (k, v) => correlator.cacheSet(k, v),\n emitRecord: (e) => derived.push(e),\n addLink: (link) => mutableEvent.links.push(link),\n lookupArtifact: (path) => lookupArtifact(path, patterns),\n logDebug: (msg, data?) => {\n // In production, route to a structured logger; for now, use console.debug.\n if (data !== undefined) {\n console.debug(`[Enricher] ${msg}`, data);\n } else {\n console.debug(`[Enricher] ${msg}`);\n }\n },\n nowUnixNano: () => currentTimeUnixNano(),\n };\n\n const ctx: EnrichContext = { event: mutableEvent, api };\n\n for (const rule of this.rules) {\n try {\n rule(mutableEvent, ctx);\n } catch (err) {\n // fail-open: log and continue — recording is never stopped by a rule error\n console.error(\"[Enricher] rule threw (fail-open):\", err);\n }\n }\n\n return { enriched: mutableEvent, derived };\n }\n}\n\n// ── Default rule set (Phase 5) ────────────────────────────────────────────────\n\nimport { createArtifactRule } from \"./rules/artifact.js\";\nimport { createProcessRule } from \"./rules/process.js\";\nimport { createCrossAxisRule } from \"./rules/cross-axis.js\";\nimport { createPromotionRule } from \"./rules/promotion.js\";\n\n/**\n * Build the standard Phase 5 enrichment rule set.\n *\n * Rule ordering:\n * 1. R7 artifact.ts — resolve edit.artifact_id first (R3 disambiguation depends on it)\n * 2. R3/R4 process.ts — maintain active-task stack; resolve process.edit/tool parent\n * 3. R5 cross-axis.ts — materializes_as_commit on promotion.commit close\n * 4. R1/R2 promotion.ts — cache changes; emit promotion.file on commit close\n *\n * @param artifactPatterns Passed to createArtifactRule for R7 lookupArtifact.\n */\nexport function createDefaultRules(\n artifactPatterns: ArtifactPattern[] = [],\n): EnrichRule[] {\n return [\n createArtifactRule(artifactPatterns), // R7\n createProcessRule(), // R3/R4\n createCrossAxisRule(), // R5\n createPromotionRule(), // R1/R2\n ];\n}\n","/**\n * SqliteSink — persists CanonicalEvents to a local SQLite database.\n *\n * architecture.md §10 / §4.1 (SqliteSink role):\n * - canonical_events : append-only INSERT (immutable log)\n * - spans : open→INSERT, close→UPDATE, instant→INSERT\n * - canonical_links : expand event.links into normalised rows\n *\n * Default path: .agent-logs/observability.db\n * Pass ':memory:' for an in-process in-memory database (tests, ephemeral use).\n *\n * Uses the built-in node:sqlite (stable in Node ≥ 24, experimental in 22.5+).\n */\nimport { DatabaseSync } from \"node:sqlite\";\nimport { mkdirSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport type { CanonicalEvent } from \"../types/canonical-event.js\";\nimport { generateId } from \"../types/ids.js\";\n\n/** Default database file location (relative to cwd). */\nexport const DEFAULT_DB_PATH = \".agent-logs/observability.db\";\n\n// ── Schema DDL (mirrors schema.sql exactly) ──────────────────────────────────\n\nconst SCHEMA = `\nCREATE TABLE IF NOT EXISTS canonical_events (\n id TEXT PRIMARY KEY,\n time_unix_nano INTEGER NOT NULL,\n source TEXT NOT NULL,\n event_type TEXT NOT NULL,\n lifecycle TEXT NOT NULL,\n trace_id TEXT,\n span_id TEXT NOT NULL,\n parent_span_id TEXT,\n run_id TEXT,\n session_id TEXT,\n task_id TEXT,\n attributes TEXT NOT NULL DEFAULT '{}'\n);\n\nCREATE INDEX IF NOT EXISTS idx_ce_trace_id ON canonical_events (trace_id);\nCREATE INDEX IF NOT EXISTS idx_ce_span_id ON canonical_events (span_id);\nCREATE INDEX IF NOT EXISTS idx_ce_event_type ON canonical_events (event_type);\nCREATE INDEX IF NOT EXISTS idx_ce_session_id ON canonical_events (session_id);\nCREATE INDEX IF NOT EXISTS idx_ce_time ON canonical_events (time_unix_nano);\n\nCREATE TABLE IF NOT EXISTS spans (\n span_id TEXT PRIMARY KEY,\n trace_id TEXT,\n parent_span_id TEXT,\n event_type TEXT NOT NULL,\n start_time INTEGER,\n end_time INTEGER,\n duration_ns INTEGER,\n status TEXT NOT NULL DEFAULT 'open',\n run_id TEXT,\n session_id TEXT,\n task_id TEXT,\n attributes TEXT NOT NULL DEFAULT '{}'\n);\n\nCREATE INDEX IF NOT EXISTS idx_spans_trace_id ON spans (trace_id);\nCREATE INDEX IF NOT EXISTS idx_spans_parent ON spans (parent_span_id);\nCREATE INDEX IF NOT EXISTS idx_spans_event_type ON spans (event_type);\nCREATE INDEX IF NOT EXISTS idx_spans_session_id ON spans (session_id);\nCREATE INDEX IF NOT EXISTS idx_spans_task_id ON spans (task_id);\nCREATE INDEX IF NOT EXISTS idx_spans_status ON spans (status);\n\nCREATE TABLE IF NOT EXISTS canonical_links (\n id TEXT PRIMARY KEY,\n source_event_id TEXT NOT NULL,\n source_span_id TEXT NOT NULL,\n target_span_id TEXT NOT NULL,\n target_trace_id TEXT,\n link_type TEXT NOT NULL,\n attributes TEXT NOT NULL DEFAULT '{}'\n);\n\nCREATE INDEX IF NOT EXISTS idx_links_source ON canonical_links (source_span_id);\nCREATE INDEX IF NOT EXISTS idx_links_target ON canonical_links (target_span_id);\nCREATE INDEX IF NOT EXISTS idx_links_type ON canonical_links (link_type);\n`;\n\n// ── SqliteSink ───────────────────────────────────────────────────────────────\n\nexport class SqliteSink {\n private readonly db: DatabaseSync;\n\n constructor(dbPath: string = DEFAULT_DB_PATH) {\n if (dbPath !== \":memory:\") {\n const abs = resolve(dbPath);\n mkdirSync(dirname(abs), { recursive: true });\n this.db = new DatabaseSync(abs);\n } else {\n this.db = new DatabaseSync(\":memory:\");\n }\n this.db.exec(SCHEMA);\n }\n\n // ── Public write API ───────────────────────────────────────────────────────\n\n /**\n * Persist a single CanonicalEvent.\n * 1. Append to canonical_events (immutable)\n * 2. Upsert spans materialised view\n * 3. Insert canonical_links rows\n */\n write(event: CanonicalEvent): void {\n this.insertCanonicalEvent(event);\n this.upsertSpan(event);\n if (event.links.length > 0) {\n this.insertLinks(event);\n }\n }\n\n /** Close the database connection. */\n close(): void {\n this.db.close();\n }\n\n /**\n * Expose the underlying DatabaseSync for testing / read-path adapters.\n * Should not be used in production write paths.\n */\n getDb(): DatabaseSync {\n return this.db;\n }\n\n // ── Private helpers ────────────────────────────────────────────────────────\n\n private insertCanonicalEvent(event: CanonicalEvent): void {\n const stmt = this.db.prepare(`\n INSERT INTO canonical_events\n (id, time_unix_nano, source, event_type, lifecycle,\n trace_id, span_id, parent_span_id, run_id, session_id, task_id, attributes)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n stmt.run(\n event.id,\n event.timeUnixNano,\n event.source,\n event.eventType,\n event.lifecycle,\n event.traceId ?? null,\n event.spanId,\n event.parentSpanId ?? null,\n event.runId ?? null,\n event.sessionId ?? null,\n event.taskId ?? null,\n JSON.stringify(event.attributes),\n );\n }\n\n private upsertSpan(event: CanonicalEvent): void {\n switch (event.lifecycle) {\n case \"open\":\n this.db\n .prepare(`\n INSERT INTO spans\n (span_id, trace_id, parent_span_id, event_type, start_time,\n status, run_id, session_id, task_id, attributes)\n VALUES (?, ?, ?, ?, ?, 'open', ?, ?, ?, ?)\n `)\n .run(\n event.spanId,\n event.traceId ?? null,\n event.parentSpanId ?? null,\n event.eventType,\n event.timeUnixNano,\n event.runId ?? null,\n event.sessionId ?? null,\n event.taskId ?? null,\n JSON.stringify(event.attributes),\n );\n break;\n\n case \"close\":\n // duration_ns is computed in SQL so we avoid a round-trip read.\n this.db\n .prepare(`\n UPDATE spans\n SET end_time = ?,\n duration_ns = (? - start_time),\n status = 'closed'\n WHERE span_id = ?\n `)\n .run(event.timeUnixNano, event.timeUnixNano, event.spanId);\n break;\n\n case \"instant\":\n this.db\n .prepare(`\n INSERT OR IGNORE INTO spans\n (span_id, trace_id, parent_span_id, event_type,\n start_time, end_time, duration_ns, status,\n run_id, session_id, task_id, attributes)\n VALUES (?, ?, ?, ?, ?, ?, 0, 'instant', ?, ?, ?, ?)\n `)\n .run(\n event.spanId,\n event.traceId ?? null,\n event.parentSpanId ?? null,\n event.eventType,\n event.timeUnixNano,\n event.timeUnixNano,\n event.runId ?? null,\n event.sessionId ?? null,\n event.taskId ?? null,\n JSON.stringify(event.attributes),\n );\n break;\n\n case \"event\":\n // Span event: belongs to an existing span; no row in spans.\n break;\n }\n }\n\n private insertLinks(event: CanonicalEvent): void {\n const stmt = this.db.prepare(`\n INSERT INTO canonical_links\n (id, source_event_id, source_span_id, target_span_id, target_trace_id, link_type, attributes)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n `);\n for (const link of event.links) {\n stmt.run(\n generateId(),\n event.id,\n event.spanId,\n link.targetSpanId,\n link.targetTraceId ?? null,\n link.linkType,\n JSON.stringify(link.attributes ?? {}),\n );\n }\n }\n}\n","/**\n * OtelEmitter — converts MappedSpans to OTEL Spans and emits them to an\n * OTLP-compatible backend asynchronously (batched).\n *\n * architecture.md §11:\n * - Uses @opentelemetry/sdk-trace-base + @opentelemetry/exporter-trace-otlp-http\n * - fail-open: emit failures are caught, logged, and swallowed.\n * The SQLite write path is never blocked or thrown into.\n * - When OTEL_EXPORTER_OTLP_ENDPOINT is not set, OtelEmitter is a no-op\n * (SqliteSink continues to record regardless).\n */\nimport { BasicTracerProvider, BatchSpanProcessor } from \"@opentelemetry/sdk-trace-base\";\nimport { OTLPTraceExporter } from \"@opentelemetry/exporter-trace-otlp-http\";\nimport { trace, SpanKind, SpanStatusCode, ROOT_CONTEXT, type Tracer } from \"@opentelemetry/api\";\nimport type { MappedSpan } from \"./span-mapper.js\";\nimport type { AttrValue } from \"../types/canonical-event.js\";\n\n// ── Configuration ─────────────────────────────────────────────────────────────\n\nexport interface OtelEmitterOptions {\n /**\n * OTLP HTTP endpoint URL.\n * Defaults to process.env.OTEL_EXPORTER_OTLP_ENDPOINT.\n * If neither is set, OtelEmitter becomes a no-op.\n */\n endpoint?: string;\n /** Service name reported to the OTEL backend. Default: '@aaac/observability'. */\n serviceName?: string;\n}\n\n// ── OtelEmitter ───────────────────────────────────────────────────────────────\n\nexport class OtelEmitter {\n private readonly provider: BasicTracerProvider | null;\n private readonly tracer: Tracer | null;\n private readonly enabled: boolean;\n\n constructor(options: OtelEmitterOptions = {}) {\n const endpoint =\n options.endpoint ?? process.env[\"OTEL_EXPORTER_OTLP_ENDPOINT\"];\n\n if (!endpoint) {\n // No backend configured — fail-open: become a no-op.\n this.provider = null;\n this.tracer = null;\n this.enabled = false;\n return;\n }\n\n const serviceName = options.serviceName ?? \"@aaac/observability\";\n\n const exporter = new OTLPTraceExporter({ url: `${endpoint}/v1/traces` });\n const processor = new BatchSpanProcessor(exporter);\n\n this.provider = new BasicTracerProvider({\n resource: {\n attributes: { \"service.name\": serviceName },\n } as never,\n spanProcessors: [processor],\n });\n\n // Register as global provider so trace.getTracer() uses our provider.\n trace.setGlobalTracerProvider(this.provider);\n this.tracer = this.provider.getTracer(\"@aaac/observability\", \"0.1.0\");\n this.enabled = true;\n }\n\n /**\n * Emit a single MappedSpan to the configured OTLP backend.\n *\n * fail-open: any error is caught, logged to stderr, and swallowed.\n * This method never throws into the caller.\n */\n emit(span: MappedSpan): void {\n if (!this.enabled || this.tracer === null) return;\n\n try {\n this._emitSpan(span);\n } catch (err) {\n // fail-open: log and swallow\n console.error(\"[OtelEmitter] emit error (swallowed):\", err);\n }\n }\n\n /**\n * Emit a batch of MappedSpans.\n * Each span is emitted independently; one failure does not block others.\n */\n emitBatch(spans: MappedSpan[]): void {\n for (const span of spans) {\n this.emit(span);\n }\n }\n\n /**\n * Flush pending spans and shut down the OTEL provider.\n * Returns a promise that resolves when the flush is complete (or on error).\n */\n async shutdown(): Promise<void> {\n if (this.provider === null) return;\n try {\n await this.provider.shutdown();\n } catch (err) {\n console.error(\"[OtelEmitter] shutdown error (swallowed):\", err);\n }\n }\n\n // ── Private helpers ────────────────────────────────────────────────────────\n\n private _emitSpan(mapped: MappedSpan): void {\n if (this.tracer === null) return;\n\n // Build a parent context if parentSpanId is known.\n const parentCtx = mapped.parentSpanId\n ? this._buildParentContext(mapped.traceId, mapped.parentSpanId)\n : ROOT_CONTEXT;\n\n // Convert nanoseconds (bigint) → milliseconds (number) for the SDK.\n const startMs = Number(mapped.startTimeUnixNano / 1_000_000n);\n const endMs = Number(mapped.endTimeUnixNano / 1_000_000n);\n\n const otelSpan = this.tracer.startSpan(\n mapped.name,\n {\n kind: SpanKind.INTERNAL,\n startTime: startMs,\n attributes: convertAttributes(mapped.attributes),\n links: mapped.links.map((l) => ({\n context: {\n traceId: l.targetTraceId ?? mapped.traceId,\n spanId: l.targetSpanId,\n traceFlags: 1,\n },\n attributes: l.attributes,\n })),\n },\n parentCtx,\n );\n\n // Add mid-span events.\n for (const ev of mapped.spanEvents) {\n otelSpan.addEvent(\n ev.name,\n convertAttributes(ev.attributes),\n Number(ev.timeUnixNano / 1_000_000n),\n );\n }\n\n otelSpan.setStatus({ code: SpanStatusCode.OK });\n otelSpan.end(endMs);\n }\n\n private _buildParentContext(traceId: string, spanId: string): typeof ROOT_CONTEXT {\n const spanContext = {\n traceId,\n spanId,\n traceFlags: 1, // SAMPLED\n isRemote: false,\n };\n return trace.setSpanContext(ROOT_CONTEXT, spanContext);\n }\n}\n\n// ── Attribute conversion ───────────────────────────────────────────────────────\n\nfunction convertAttributes(\n attrs: Record<string, AttrValue>,\n): Record<string, string | number | boolean> {\n return attrs as Record<string, string | number | boolean>;\n}\n","/**\n * span-mapper — converts a group of CanonicalEvents sharing the same spanId\n * into a structure ready for OTEL Span emission.\n *\n * architecture.md §11 / §4.1 (OtelEmitter role):\n * - open → Span start_time\n * - close → Span end_time\n * - event → SpanEvent (attached to the span)\n * - instant → start_time = end_time (duration-zero Span)\n *\n * attributes are merged across open / close / event events (open first,\n * then close, then each event in time order — later keys win).\n *\n * parentSpanId → OTEL parent context.\n * CanonicalEvent.links → OTEL SpanLinks (link_type stored as a SpanLink attribute).\n */\nimport type { CanonicalEvent, AttrValue, CanonicalLink } from \"../types/canonical-event.js\";\n\n// ── Public types ──────────────────────────────────────────────────────────────\n\n/** A single SpanEvent (mid-span event attached to the logical span). */\nexport interface MappedSpanEvent {\n name: string;\n timeUnixNano: bigint;\n attributes: Record<string, AttrValue>;\n}\n\n/** A SpanLink pointing to another span, carrying link_type as an attribute. */\nexport interface MappedSpanLink {\n /** Target span identifier. */\n targetSpanId: string;\n /** Target trace identifier (may be same as the span's traceId). */\n targetTraceId?: string;\n /** Attributes including 'link_type' (the semantic relationship). */\n attributes: Record<string, string>;\n}\n\n/**\n * A logical OTEL Span derived from one or more CanonicalEvents.\n * This is an intermediate representation, not coupled to any OTEL SDK type.\n */\nexport interface MappedSpan {\n /** Span identifier. */\n spanId: string;\n /** Trace identifier. */\n traceId: string;\n /** Parent span identifier (undefined for root spans). */\n parentSpanId?: string;\n /** Span name (== eventType of the constituent events). */\n name: string;\n /** Span start time in Unix nanoseconds. */\n startTimeUnixNano: bigint;\n /** Span end time in Unix nanoseconds. */\n endTimeUnixNano: bigint;\n /** Merged attributes from all constituent events. */\n attributes: Record<string, AttrValue>;\n /** Mid-span events (from lifecycle='event' CanonicalEvents). */\n spanEvents: MappedSpanEvent[];\n /** Links to other spans. */\n links: MappedSpanLink[];\n}\n\n// ── Implementation ────────────────────────────────────────────────────────────\n\n/**\n * Map a group of CanonicalEvents (all sharing the same spanId) to a MappedSpan.\n *\n * The caller must ensure all events belong to the same spanId.\n * Events need not be pre-sorted; this function sorts by timeUnixNano internally.\n *\n * Returns undefined if the group is empty.\n */\nexport function mapEventsToSpan(events: CanonicalEvent[]): MappedSpan | undefined {\n if (events.length === 0) return undefined;\n\n // Sort by time so attribute merge order is deterministic.\n const sorted = [...events].sort((a, b) =>\n a.timeUnixNano < b.timeUnixNano ? -1 : a.timeUnixNano > b.timeUnixNano ? 1 : 0,\n );\n\n const first = sorted[0];\n const spanId = first.spanId;\n const traceId = first.traceId;\n const parentSpanId = first.parentSpanId;\n const name = first.eventType;\n\n // Determine start / end times and collect span events.\n let startTimeUnixNano: bigint | undefined;\n let endTimeUnixNano: bigint | undefined;\n const spanEvents: MappedSpanEvent[] = [];\n\n // Merged attributes: open → close → each 'event' in time order (later wins).\n let mergedAttributes: Record<string, AttrValue> = {};\n\n // Collect all links from all constituent events.\n const allLinks: CanonicalLink[] = [];\n\n for (const ev of sorted) {\n switch (ev.lifecycle) {\n case \"open\":\n startTimeUnixNano = ev.timeUnixNano;\n mergedAttributes = { ...mergedAttributes, ...ev.attributes };\n break;\n\n case \"close\":\n endTimeUnixNano = ev.timeUnixNano;\n mergedAttributes = { ...mergedAttributes, ...ev.attributes };\n break;\n\n case \"event\":\n spanEvents.push({\n name: ev.eventType,\n timeUnixNano: ev.timeUnixNano,\n attributes: ev.attributes,\n });\n mergedAttributes = { ...mergedAttributes, ...ev.attributes };\n break;\n\n case \"instant\":\n startTimeUnixNano = ev.timeUnixNano;\n endTimeUnixNano = ev.timeUnixNano;\n mergedAttributes = { ...mergedAttributes, ...ev.attributes };\n break;\n }\n\n allLinks.push(...ev.links);\n }\n\n // Fallback: if we only have 'event' type events (unusual but safe).\n if (startTimeUnixNano === undefined) {\n startTimeUnixNano = first.timeUnixNano;\n }\n if (endTimeUnixNano === undefined) {\n endTimeUnixNano = startTimeUnixNano;\n }\n\n // Convert CanonicalLinks → MappedSpanLinks.\n const links: MappedSpanLink[] = allLinks.map((l) => ({\n targetSpanId: l.targetSpanId,\n targetTraceId: l.targetTraceId,\n attributes: {\n link_type: l.linkType,\n ...(l.attributes ?? {}),\n },\n }));\n\n return {\n spanId,\n traceId,\n parentSpanId,\n name,\n startTimeUnixNano,\n endTimeUnixNano,\n attributes: mergedAttributes,\n spanEvents,\n links,\n };\n}\n\n/**\n * Group an array of CanonicalEvents by spanId, then map each group to a MappedSpan.\n * Useful for batch conversion (e.g. flushing a set of events to OTEL).\n */\nexport function mapAllEventsToSpans(events: CanonicalEvent[]): MappedSpan[] {\n const groups = new Map<string, CanonicalEvent[]>();\n for (const ev of events) {\n const group = groups.get(ev.spanId);\n if (group !== undefined) {\n group.push(ev);\n } else {\n groups.set(ev.spanId, [ev]);\n }\n }\n\n const spans: MappedSpan[] = [];\n for (const group of groups.values()) {\n const mapped = mapEventsToSpan(group);\n if (mapped !== undefined) {\n spans.push(mapped);\n }\n }\n return spans;\n}\n","/**\n * SqliteQueryAdapter — reads the SqliteSink database independently of the\n * write path.\n *\n * architecture.md §12 / §12.3:\n * - Queries the `spans` materialised table for span-level queries.\n * - Queries `canonical_events` for raw event access.\n * - Queries `canonical_links` for link traversal (source/target bidirectional).\n * - Can open the same database file as SqliteSink, or an in-memory DB for tests.\n * - Does NOT touch write-path state (no Correlator, no Normalizer, etc.).\n */\nimport { DatabaseSync } from \"node:sqlite\";\nimport { mkdirSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport type {\n StoredEvent,\n SpanRecord,\n LinkRecord,\n SpanQueryFilter,\n EventQueryFilter,\n LinkDirection,\n} from \"./models.js\";\nimport type { QueryAdapter } from \"./query-adapter.js\";\nimport type { AttrValue } from \"../types/canonical-event.js\";\nimport type { SQLInputValue } from \"node:sqlite\";\nimport { DEFAULT_DB_PATH } from \"../sink/sqlite-sink.js\";\n\n// ── SqliteQueryAdapter ────────────────────────────────────────────────────────\n\nexport class SqliteQueryAdapter implements QueryAdapter {\n private readonly db: DatabaseSync;\n private readonly ownsDb: boolean;\n\n /**\n * @param dbPathOrDb Either a file path string (opens a new connection) or\n * an existing DatabaseSync instance (shared connection,\n * e.g. from SqliteSink.getDb() in tests).\n */\n constructor(dbPathOrDb: string | DatabaseSync = DEFAULT_DB_PATH) {\n if (typeof dbPathOrDb === \"string\") {\n if (dbPathOrDb !== \":memory:\") {\n const abs = resolve(dbPathOrDb);\n mkdirSync(dirname(abs), { recursive: true });\n this.db = new DatabaseSync(abs);\n } else {\n this.db = new DatabaseSync(\":memory:\");\n }\n this.ownsDb = true;\n } else {\n this.db = dbPathOrDb;\n this.ownsDb = false;\n }\n }\n\n // ── Span-level queries ─────────────────────────────────────────────────────\n\n querySpans(filter: SpanQueryFilter): SpanRecord[] {\n const { clauses, params } = buildSpanFilter(filter);\n const where = clauses.length > 0 ? `WHERE ${clauses.join(\" AND \")}` : \"\";\n const limit = filter.limit !== undefined ? `LIMIT ${Number(filter.limit)}` : \"\";\n const sql = `SELECT * FROM spans ${where} ORDER BY start_time ASC ${limit}`;\n const stmt = this.db.prepare(sql);\n stmt.setReadBigInts(true);\n const rows = stmt.all(...(params as SQLInputValue[])) as unknown as RawSpanRow[];\n return rows.map(parseSpanRow);\n }\n\n getSpan(spanId: string): SpanRecord | undefined {\n const stmt = this.db.prepare(\"SELECT * FROM spans WHERE span_id = ?\");\n stmt.setReadBigInts(true);\n const row = stmt.get(spanId) as unknown as RawSpanRow | undefined;\n return row !== undefined ? parseSpanRow(row) : undefined;\n }\n\n getTrace(traceId: string): SpanRecord[] {\n const stmt = this.db.prepare(\n \"SELECT * FROM spans WHERE trace_id = ? ORDER BY start_time ASC\",\n );\n stmt.setReadBigInts(true);\n const rows = stmt.all(traceId) as unknown as RawSpanRow[];\n return rows.map(parseSpanRow);\n }\n\n // ── Event-level queries ────────────────────────────────────────────────────\n\n queryEvents(filter: EventQueryFilter): StoredEvent[] {\n const { clauses, params } = buildEventFilter(filter);\n const where = clauses.length > 0 ? `WHERE ${clauses.join(\" AND \")}` : \"\";\n const limit = filter.limit !== undefined ? `LIMIT ${Number(filter.limit)}` : \"\";\n const sql = `SELECT * FROM canonical_events ${where} ORDER BY time_unix_nano ASC ${limit}`;\n const stmt = this.db.prepare(sql);\n stmt.setReadBigInts(true);\n const rows = stmt.all(...(params as SQLInputValue[])) as unknown as RawEventRow[];\n return rows.map(parseEventRow);\n }\n\n getSpanEvents(spanId: string): StoredEvent[] {\n const stmt = this.db.prepare(\n \"SELECT * FROM canonical_events WHERE span_id = ? ORDER BY time_unix_nano ASC\",\n );\n stmt.setReadBigInts(true);\n const rows = stmt.all(spanId) as unknown as RawEventRow[];\n return rows.map(parseEventRow);\n }\n\n // ── Link traversal ─────────────────────────────────────────────────────────\n\n getLinks(spanId: string, direction: LinkDirection = \"forward\"): LinkRecord[] {\n const rawRows: RawLinkRow[] = [];\n\n if (direction === \"forward\" || direction === \"both\") {\n const stmt = this.db.prepare(\n \"SELECT * FROM canonical_links WHERE source_span_id = ?\",\n );\n rawRows.push(...(stmt.all(spanId) as unknown as RawLinkRow[]));\n }\n\n if (direction === \"reverse\" || direction === \"both\") {\n const stmt = this.db.prepare(\n \"SELECT * FROM canonical_links WHERE target_span_id = ?\",\n );\n rawRows.push(...(stmt.all(spanId) as unknown as RawLinkRow[]));\n }\n\n // Deduplicate by id when direction === \"both\".\n if (direction === \"both\") {\n const seen = new Set<string>();\n return rawRows\n .filter((r) => {\n if (seen.has(r.id)) return false;\n seen.add(r.id);\n return true;\n })\n .map(parseLinkRow);\n }\n\n return rawRows.map(parseLinkRow);\n }\n\n getLinksByType(linkType: string): LinkRecord[] {\n const stmt = this.db.prepare(\n \"SELECT * FROM canonical_links WHERE link_type = ?\",\n );\n const rows = stmt.all(linkType) as unknown as RawLinkRow[];\n return rows.map(parseLinkRow);\n }\n\n // ── Lifecycle ──────────────────────────────────────────────────────────────\n\n close(): void {\n if (this.ownsDb) {\n this.db.close();\n }\n // If we don't own the DB (shared from SqliteSink), do not close it.\n }\n}\n\n// ── Raw row types (as returned by node:sqlite with BigInt mode) ───────────────\n\ninterface RawSpanRow {\n span_id: string;\n trace_id: string | null;\n parent_span_id: string | null;\n event_type: string;\n start_time: bigint | null;\n end_time: bigint | null;\n duration_ns: bigint | null;\n status: string;\n run_id: string | null;\n session_id: string | null;\n task_id: string | null;\n attributes: string;\n}\n\ninterface RawEventRow {\n id: string;\n time_unix_nano: bigint;\n source: string;\n event_type: string;\n lifecycle: string;\n trace_id: string | null;\n span_id: string;\n parent_span_id: string | null;\n run_id: string | null;\n session_id: string | null;\n task_id: string | null;\n attributes: string;\n}\n\ninterface RawLinkRow {\n id: string;\n source_event_id: string;\n source_span_id: string;\n target_span_id: string;\n target_trace_id: string | null;\n link_type: string;\n attributes: string;\n}\n\n// ── Row parsers ───────────────────────────────────────────────────────────────\n\nfunction parseSpanRow(row: RawSpanRow): SpanRecord {\n return {\n spanId: row.span_id,\n traceId: row.trace_id,\n parentSpanId: row.parent_span_id,\n eventType: row.event_type,\n startTime: row.start_time,\n endTime: row.end_time,\n durationNs: row.duration_ns,\n status: row.status,\n runId: row.run_id,\n sessionId: row.session_id,\n taskId: row.task_id,\n attributes: safeParseJson(row.attributes),\n };\n}\n\nfunction parseEventRow(row: RawEventRow): StoredEvent {\n return {\n id: row.id,\n timeUnixNano: row.time_unix_nano,\n source: row.source,\n eventType: row.event_type,\n lifecycle: row.lifecycle as StoredEvent[\"lifecycle\"],\n traceId: row.trace_id ?? \"\",\n spanId: row.span_id,\n parentSpanId: row.parent_span_id ?? undefined,\n runId: row.run_id ?? undefined,\n sessionId: row.session_id ?? undefined,\n taskId: row.task_id ?? undefined,\n attributes: safeParseJson(row.attributes),\n links: [], // links are stored in canonical_links, not inline on events\n };\n}\n\nfunction parseLinkRow(row: RawLinkRow): LinkRecord {\n return {\n id: row.id,\n sourceEventId: row.source_event_id,\n sourceSpanId: row.source_span_id,\n targetSpanId: row.target_span_id,\n targetTraceId: row.target_trace_id,\n linkType: row.link_type,\n attributes: safeParseJson(row.attributes) as Record<string, string>,\n };\n}\n\nfunction safeParseJson(json: string): Record<string, AttrValue> {\n try {\n return JSON.parse(json) as Record<string, AttrValue>;\n } catch {\n return {};\n }\n}\n\n// ── Filter builders ───────────────────────────────────────────────────────────\n\nfunction buildSpanFilter(filter: SpanQueryFilter): { clauses: string[]; params: unknown[] } {\n const clauses: string[] = [];\n const params: unknown[] = [];\n\n if (filter.traceId !== undefined) {\n clauses.push(\"trace_id = ?\");\n params.push(filter.traceId);\n }\n if (filter.spanId !== undefined) {\n clauses.push(\"span_id = ?\");\n params.push(filter.spanId);\n }\n if (filter.eventType !== undefined) {\n clauses.push(\"event_type = ?\");\n params.push(filter.eventType);\n }\n if (filter.taskId !== undefined) {\n clauses.push(\"task_id = ?\");\n params.push(filter.taskId);\n }\n if (filter.fromTimeUnixNano !== undefined) {\n clauses.push(\"start_time >= ?\");\n params.push(filter.fromTimeUnixNano);\n }\n if (filter.toTimeUnixNano !== undefined) {\n clauses.push(\"start_time <= ?\");\n params.push(filter.toTimeUnixNano);\n }\n\n return { clauses, params };\n}\n\nfunction buildEventFilter(filter: EventQueryFilter): { clauses: string[]; params: unknown[] } {\n const clauses: string[] = [];\n const params: unknown[] = [];\n\n if (filter.traceId !== undefined) {\n clauses.push(\"trace_id = ?\");\n params.push(filter.traceId);\n }\n if (filter.spanId !== undefined) {\n clauses.push(\"span_id = ?\");\n params.push(filter.spanId);\n }\n if (filter.eventType !== undefined) {\n clauses.push(\"event_type = ?\");\n params.push(filter.eventType);\n }\n if (filter.taskId !== undefined) {\n clauses.push(\"task_id = ?\");\n params.push(filter.taskId);\n }\n if (filter.fromTimeUnixNano !== undefined) {\n clauses.push(\"time_unix_nano >= ?\");\n params.push(filter.fromTimeUnixNano);\n }\n if (filter.toTimeUnixNano !== undefined) {\n clauses.push(\"time_unix_nano <= ?\");\n params.push(filter.toTimeUnixNano);\n }\n\n return { clauses, params };\n}\n","import { readFile } from \"node:fs/promises\";\nimport { parse as parseYaml } from \"yaml\";\nimport { generateId } from \"../types/ids.js\";\nimport {\n evaluateCondition,\n resolveEachItems,\n resolveTemplate,\n} from \"./template.js\";\nimport type {\n EventMappingConfig,\n EventMappingResult,\n EventMappingRule,\n ResolvedLink,\n ResolvedSpan,\n SpanRule,\n} from \"./types.js\";\n\nfunction eventTypeFromRule(rule: SpanRule): string {\n // name already includes axis prefix (e.g. \"process.task\")\n return rule.name;\n}\n\nfunction resolveAttributes(\n attributes: Record<string, string> | undefined,\n input: Record<string, unknown>,\n context?: { item?: string },\n): Record<string, string | number | boolean> {\n if (!attributes) return { axis: \"\" };\n\n const resolved: Record<string, string | number | boolean> = {};\n for (const [key, template] of Object.entries(attributes)) {\n const value = resolveTemplate(template, input, context);\n if (value !== \"\") {\n resolved[key] = value;\n }\n }\n return resolved;\n}\n\nfunction evaluateSpanRule(\n rule: SpanRule,\n input: Record<string, unknown>,\n spanNameToId: Map<string, string>,\n parentSpanId?: string,\n): ResolvedSpan[] {\n if (rule.condition && !evaluateCondition(rule.condition, input)) {\n return [];\n }\n\n const eventType = eventTypeFromRule(rule);\n const spans: ResolvedSpan[] = [];\n\n const emitOne = (context?: { item?: string }): ResolvedSpan => {\n const spanId = generateId();\n spanNameToId.set(rule.name, spanId);\n\n const attrs = resolveAttributes(rule.attributes, input, context);\n if (!attrs.axis) {\n attrs.axis = rule.axis;\n }\n\n return {\n eventType,\n lifecycle: rule.lifecycle,\n spanId,\n parentSpanId,\n attributes: attrs,\n };\n };\n\n if (rule.each) {\n const items = resolveEachItems(rule.each, input);\n for (const item of items) {\n spans.push(emitOne({ item }));\n }\n } else {\n spans.push(emitOne());\n }\n\n return spans;\n}\n\n/**\n * Evaluate an event_mapping rule against hook input to produce resolved spans and links.\n */\nexport function evaluateMapping(\n rule: EventMappingRule,\n hookInput: Record<string, unknown>,\n): EventMappingResult {\n const spanNameToId = new Map<string, string>();\n const spans: ResolvedSpan[] = [];\n\n for (const spanRule of rule.spans) {\n const resolved = evaluateSpanRule(spanRule, hookInput, spanNameToId);\n spans.push(...resolved);\n }\n\n const links: ResolvedLink[] = [];\n if (rule.links) {\n for (const linkRule of rule.links) {\n const fromSpanId = spanNameToId.get(linkRule.from);\n const toSpanId = spanNameToId.get(linkRule.to);\n if (fromSpanId && toSpanId) {\n links.push({\n linkType: linkRule.type,\n fromSpanId,\n toSpanId,\n });\n }\n }\n }\n\n return { spans, links };\n}\n\n/**\n * Load an event-mapping config file (YAML or JSON).\n */\nexport async function loadEventMappingConfig(\n filePath: string,\n): Promise<EventMappingConfig> {\n const content = await readFile(filePath, \"utf8\");\n const parsed = filePath.endsWith(\".json\")\n ? (JSON.parse(content) as Record<string, unknown>)\n : (parseYaml(content) as Record<string, unknown>);\n\n // Support both top-level `mappings` key and bare event_mapping object\n const mappings =\n (parsed.mappings as EventMappingConfig[\"mappings\"]) ??\n (parsed.event_mapping as EventMappingConfig[\"mappings\"]) ??\n (parsed as EventMappingConfig[\"mappings\"]);\n\n return { mappings };\n}\n","/**\n * Resolve a template expression against hook input data.\n * Supports: {{field}}, {{resolve:xxx}}, {{env:VAR}}, {{item}}\n * For now, implements {{field}} (direct field access) and literal passthrough.\n */\n\nconst TEMPLATE_RE = /\\{\\{([^}]+)\\}\\}/g;\n\nfunction getFieldValue(input: Record<string, unknown>, path: string): unknown {\n const parts = path.split(\".\");\n let current: unknown = input;\n for (const part of parts) {\n if (current === null || current === undefined || typeof current !== \"object\") {\n return undefined;\n }\n current = (current as Record<string, unknown>)[part];\n }\n return current;\n}\n\nfunction resolveExpression(\n expr: string,\n input: Record<string, unknown>,\n context?: { item?: string },\n): string {\n const trimmed = expr.trim();\n\n if (trimmed.startsWith(\"env:\")) {\n const varName = trimmed.slice(4).trim();\n return process.env[varName] ?? \"\";\n }\n\n if (trimmed === \"item\" && context?.item !== undefined) {\n return context.item;\n }\n\n if (trimmed.startsWith(\"resolve:\")) {\n // Placeholder: resolve logic deferred to Phase 4 binding\n return \"\";\n }\n\n const value = getFieldValue(input, trimmed);\n if (value === undefined || value === null) {\n return \"\";\n }\n return String(value);\n}\n\n/**\n * Resolve a template expression against hook input data.\n */\nexport function resolveTemplate(\n template: string,\n input: Record<string, unknown>,\n context?: { item?: string },\n): string {\n if (!TEMPLATE_RE.test(template)) {\n return template;\n }\n\n // Reset lastIndex after test()\n TEMPLATE_RE.lastIndex = 0;\n\n return template.replace(TEMPLATE_RE, (_match, expr: string) =>\n resolveExpression(expr, input, context),\n );\n}\n\n/**\n * Evaluate a condition template. Returns true when the resolved value is non-empty\n * and not \"false\" / \"0\".\n */\nexport function evaluateCondition(\n condition: string,\n input: Record<string, unknown>,\n context?: { item?: string },\n): boolean {\n const resolved = resolveTemplate(condition, input, context);\n if (resolved === \"\" || resolved === \"false\" || resolved === \"0\") {\n return false;\n }\n return true;\n}\n\n/**\n * Split an `each` template result into individual items.\n * Supports newline-separated strings and arrays from hook input.\n */\nexport function resolveEachItems(\n eachTemplate: string,\n input: Record<string, unknown>,\n): string[] {\n const fieldMatch = eachTemplate.match(/^\\{\\{([^}]+)\\}\\}$/);\n if (fieldMatch) {\n const value = getFieldValue(input, fieldMatch[1].trim());\n if (Array.isArray(value)) {\n return value.map(String);\n }\n }\n\n const resolved = resolveTemplate(eachTemplate, input);\n if (resolved === \"\") {\n return [];\n }\n return resolved.split(\"\\n\").map((s) => s.trim()).filter(Boolean);\n}\n","import type { ArtifactPattern } from \"../enrich/api.js\";\n\nexport interface TaskPattern {\n tag_prefix: string;\n field: string;\n}\n\n/**\n * Generate artifact-lookup.json from artifact-contracts declarations.\n * Each artifact with path globs becomes an ArtifactPattern entry.\n */\nexport function generateArtifactLookup(\n artifactDeclarations: Array<{ artifact_id: string; path_patterns: string[] }>,\n): ArtifactPattern[] {\n const patterns: ArtifactPattern[] = [];\n for (const decl of artifactDeclarations) {\n for (const pattern of decl.path_patterns) {\n patterns.push({\n artifact_id: decl.artifact_id,\n pattern,\n });\n }\n }\n return patterns;\n}\n\n/**\n * Generate task-patterns.json for structured tag parsing.\n * Default patterns: [DSL_TASK=...], [AGENT_ROLE=...], [WORKFLOW_PHASE=...]\n */\nexport function generateTaskPatterns(): TaskPattern[] {\n return [\n { tag_prefix: \"DSL_TASK\", field: \"task.id\" },\n { tag_prefix: \"AGENT_ROLE\", field: \"task.target_agent\" },\n { tag_prefix: \"WORKFLOW_PHASE\", field: \"workflow.phase\" },\n ];\n}\n\nconst TAG_RE = /\\[([A-Z_]+)=([^\\]]*)\\]/g;\n\n/**\n * Parse structured tags from a string.\n * E.g. \"[DSL_TASK=impl][AGENT_ROLE=implementer]\" → { DSL_TASK: \"impl\", AGENT_ROLE: \"implementer\" }\n */\nexport function parseStructuredTags(input: string): Record<string, string> {\n const result: Record<string, string> = {};\n if (!input) return result;\n\n let match: RegExpExecArray | null;\n TAG_RE.lastIndex = 0;\n while ((match = TAG_RE.exec(input)) !== null) {\n result[match[1]] = match[2];\n }\n return result;\n}\n","import { generateId } from \"../types/ids.js\";\nimport type { EventCollector } from \"./event-collector.js\";\n\n/**\n * Quality gate command patterns and their types.\n */\nexport const QUALITY_GATE_PATTERNS: Array<{\n pattern: RegExp;\n gateType: string;\n}> = [\n { pattern: /\\bnpm test\\b/, gateType: \"test\" },\n { pattern: /\\bnpx jest\\b/, gateType: \"test\" },\n { pattern: /\\bnpx vitest\\b/, gateType: \"test\" },\n { pattern: /\\bcargo test\\b/, gateType: \"test\" },\n { pattern: /\\bpytest\\b/, gateType: \"test\" },\n { pattern: /\\bgo test\\b/, gateType: \"test\" },\n { pattern: /\\bnpm run lint\\b/, gateType: \"lint\" },\n { pattern: /\\bnpx eslint\\b/, gateType: \"lint\" },\n { pattern: /\\bcargo clippy\\b/, gateType: \"lint\" },\n { pattern: /\\bruff check\\b/, gateType: \"lint\" },\n { pattern: /\\bnpx tsc\\b/, gateType: \"typecheck\" },\n { pattern: /\\bnpm run typecheck\\b/, gateType: \"typecheck\" },\n { pattern: /\\bcargo check\\b/, gateType: \"typecheck\" },\n { pattern: /\\bnpm run build\\b/, gateType: \"build\" },\n { pattern: /\\bcargo build\\b/, gateType: \"build\" },\n { pattern: /\\bnext build\\b/, gateType: \"build\" },\n];\n\nconst PR_URL_RE = /https:\\/\\/github\\.com\\/[^/\\s]+\\/[^/\\s]+\\/pull\\/(\\d+)/;\n\n/**\n * Emit a human.instruction event when a user submits a prompt.\n */\nexport function emitHumanInstruction(\n collector: EventCollector,\n options: {\n sessionId: string;\n prompt: string;\n attachments?: unknown[];\n parentSpanId?: string;\n },\n): string {\n const spanId = generateId();\n const attachments = options.attachments ?? [];\n\n collector.emit({\n source: \"cursor-hook\",\n eventType: \"human.instruction\",\n lifecycle: \"instant\",\n spanId,\n parentSpanId: options.parentSpanId,\n attributes: {\n axis: \"agent\",\n \"agent.session_id\": options.sessionId,\n \"human.prompt_length\": options.prompt.length,\n \"human.prompt_head\": options.prompt.slice(0, 200),\n \"human.has_attachments\": attachments.length > 0,\n \"human.attachment_count\": attachments.length,\n },\n });\n\n return spanId;\n}\n\n/**\n * Check if a command matches a quality gate pattern.\n */\nexport function matchQualityGate(command: string): { gateType: string } | null {\n for (const { pattern, gateType } of QUALITY_GATE_PATTERNS) {\n if (pattern.test(command)) {\n return { gateType };\n }\n }\n return null;\n}\n\n/**\n * Emit a quality_gate.result event after a shell command that matches a gate pattern.\n */\nexport function emitQualityGateResult(\n collector: EventCollector,\n options: {\n sessionId: string;\n command: string;\n exitCode: number;\n durationMs?: number;\n parentSpanId?: string;\n },\n): string | null {\n const match = matchQualityGate(options.command);\n if (!match) return null;\n\n const spanId = generateId();\n const verdict = options.exitCode === 0 ? \"passed\" : \"failed\";\n\n collector.emit({\n source: \"cursor-hook\",\n eventType: \"quality_gate.result\",\n lifecycle: \"instant\",\n spanId,\n parentSpanId: options.parentSpanId,\n attributes: {\n axis: \"process\",\n \"agent.session_id\": options.sessionId,\n \"gate.type\": match.gateType,\n \"gate.command\": options.command,\n \"gate.exit_code\": options.exitCode,\n \"gate.verdict\": verdict,\n ...(options.durationMs !== undefined\n ? { \"gate.duration_ms\": options.durationMs }\n : {}),\n },\n });\n\n return spanId;\n}\n\n/**\n * Extract PR URL from command output.\n */\nexport function extractPrUrl(\n output: string,\n): { url: string; number: number } | null {\n const match = output.match(PR_URL_RE);\n if (!match) return null;\n return {\n url: match[0],\n number: Number(match[1]),\n };\n}\n\n/**\n * Emit a promotion.pr event when a PR is created.\n */\nexport function emitPromotionPr(\n collector: EventCollector,\n options: {\n sessionId: string;\n command: string;\n output: string;\n parentSpanId?: string;\n },\n): string | null {\n if (!options.command.includes(\"gh pr create\")) return null;\n\n const pr = extractPrUrl(options.output);\n if (!pr) return null;\n\n const spanId = generateId();\n\n collector.emit({\n source: \"cursor-hook\",\n eventType: \"promotion.pr\",\n lifecycle: \"instant\",\n spanId,\n parentSpanId: options.parentSpanId,\n attributes: {\n axis: \"promotion\",\n \"agent.session_id\": options.sessionId,\n \"pr.url\": pr.url,\n \"pr.number\": pr.number,\n \"pr.command\": options.command,\n },\n });\n\n return spanId;\n}\n","/**\n * @aaac/observability — Phase 0–5 public API\n *\n * Write pipeline (Phase 5):\n * EventCollector → Normalizer → Correlator → [afterCorrelate?] → Enricher → SqliteSink\n * → OtelEmitter\n *\n * Quick start:\n * import { createPipeline } from '@aaac/observability';\n * const { collector, sink } = createPipeline();\n * collector.emit({ source: 'aaac-runtime', eventType: 'agent.session', lifecycle: 'open', attributes: {} });\n * sink.close();\n */\n\n// ── Types ────────────────────────────────────────────────────────────────────\nexport type {\n AttrValue,\n Lifecycle,\n CanonicalLink,\n RawEvent,\n CanonicalEvent,\n StoredEvent,\n} from \"./types/canonical-event.js\";\n\nexport type { ExecutionEnvelope } from \"./types/execution-envelope.js\";\nexport { generateId, currentTimeUnixNano, isoToUnixNano } from \"./types/ids.js\";\n\n// ── Collector ─────────────────────────────────────────────────────────────────\nexport {\n EventCollector,\n type EmitInput,\n type RawEventHandler,\n} from \"./collector/event-collector.js\";\n\nexport {\n DefaultExternalRegistrar,\n type ExternalEventRegistrar,\n type RawExternalEvent,\n} from \"./collector/external-registrar.js\";\n\n// ── Normalizer ────────────────────────────────────────────────────────────────\nexport { Normalizer } from \"./normalize/normalizer.js\";\nexport { validateRawEvent, NormalizationError } from \"./normalize/validator.js\";\n\n// ── Correlator ────────────────────────────────────────────────────────────────\nexport { Correlator } from \"./correlate/correlator.js\";\nexport { keys as correlateKeys } from \"./correlate/keys.js\";\n\n// ── Enricher (Phase 5) ────────────────────────────────────────────────────────\nexport type {\n ArtifactPattern,\n EnrichApi,\n EnrichContext,\n EnrichRule,\n} from \"./enrich/api.js\";\nexport {\n Enricher,\n createDefaultRules,\n type EnricherOptions,\n type EnrichResult,\n} from \"./enrich/enricher.js\";\nexport {\n createArtifactRule,\n lookupArtifact,\n globToRegex,\n createProcessRule,\n activeTasksKey,\n allTaskSpansKey,\n taskFilesKey,\n recordTaskArtifact,\n type ActiveTask,\n createCrossAxisRule,\n createPromotionRule,\n} from \"./enrich/index.js\";\n\n// ── Sink ──────────────────────────────────────────────────────────────────────\nexport { SqliteSink, DEFAULT_DB_PATH } from \"./sink/sqlite-sink.js\";\n\n// ── OtelEmitter (Phase 2) ──────────────────────────────────────────────────────\nexport { OtelEmitter } from \"./otel/otel-emitter.js\";\nexport type { OtelEmitterOptions } from \"./otel/otel-emitter.js\";\nexport { mapEventsToSpan, mapAllEventsToSpans } from \"./otel/span-mapper.js\";\nexport type { MappedSpan, MappedSpanEvent, MappedSpanLink } from \"./otel/span-mapper.js\";\n\n// ── Query (Phase 3) ───────────────────────────────────────────────────────────\nexport { SqliteQueryAdapter } from \"./query/sqlite-adapter.js\";\nexport type {\n SpanRecord,\n LinkRecord,\n SpanQueryFilter,\n EventQueryFilter,\n LinkDirection,\n} from \"./query/models.js\";\nexport type { QueryAdapter } from \"./query/query-adapter.js\";\n\n// ── Event Mapping (Phase 4) ───────────────────────────────────────────────────\nexport type {\n EventMappingRule,\n SpanRule,\n LinkRule,\n EventMappingConfig,\n ResolvedSpan,\n ResolvedLink,\n EventMappingResult,\n} from \"./event-mapping/types.js\";\nexport { evaluateMapping, loadEventMappingConfig } from \"./event-mapping/engine.js\";\nexport { resolveTemplate } from \"./event-mapping/template.js\";\nexport {\n generateArtifactLookup,\n generateTaskPatterns,\n parseStructuredTags,\n} from \"./event-mapping/lookup.js\";\n\n// ── Human Interaction Events ──────────────────────────────────────────────────\nexport {\n emitHumanInstruction,\n emitQualityGateResult,\n emitPromotionPr,\n matchQualityGate,\n extractPrUrl,\n QUALITY_GATE_PATTERNS,\n} from \"./collector/human-events.js\";\n\n// ── Pipeline factory ──────────────────────────────────────────────────────────\n\nimport { EventCollector } from \"./collector/event-collector.js\";\nimport { Normalizer } from \"./normalize/normalizer.js\";\nimport { Correlator } from \"./correlate/correlator.js\";\nimport { SqliteSink, DEFAULT_DB_PATH } from \"./sink/sqlite-sink.js\";\nimport { OtelEmitter } from \"./otel/otel-emitter.js\";\nimport { Enricher, createDefaultRules } from \"./enrich/enricher.js\";\nimport type { CanonicalEvent } from \"./types/canonical-event.js\";\nimport type { OtelEmitterOptions } from \"./otel/otel-emitter.js\";\nimport type { ArtifactPattern, EnrichRule } from \"./enrich/api.js\";\n\nexport interface PipelineOptions {\n /** SQLite database path. Defaults to DEFAULT_DB_PATH. Pass ':memory:' for tests. */\n dbPath?: string;\n /**\n * Optional post-correlate hook — runs before the Enricher.\n * Kept for backward compatibility; prefer using enrichRules for Phase 5+.\n */\n afterCorrelate?: (event: CanonicalEvent) => CanonicalEvent;\n /**\n * Optional OTEL emitter options.\n * If provided (and endpoint is set), events are also emitted to the OTEL backend.\n * fail-open: OTEL failures never block SQLite recording.\n */\n otel?: OtelEmitterOptions;\n /**\n * Artifact path-glob patterns for the Enricher's lookupArtifact (R7).\n * Provided from artifact-contracts declarations (generated by Phase 4 binding).\n * Defaults to [] (all file paths resolve to \"unknown\").\n */\n artifactPatterns?: ArtifactPattern[];\n /**\n * Custom enrichment rule list.\n * Defaults to createDefaultRules(artifactPatterns) — the standard Phase 5 set.\n * Pass [] to disable all enrichment.\n */\n enrichRules?: EnrichRule[];\n}\n\nexport interface Pipeline {\n collector: EventCollector;\n correlator: Correlator;\n sink: SqliteSink;\n otelEmitter: OtelEmitter;\n /** Phase 5 Enricher (Correlator → Enricher → OtelEmitter + SqliteSink). */\n enricher: Enricher;\n}\n\n/**\n * Create a complete write pipeline wired together:\n *\n * EventCollector → Normalizer → Correlator → [afterCorrelate?]\n * → Enricher → SqliteSink\n * → OtelEmitter (if configured, fail-open)\n *\n * Derived events from Enricher.emitRecord() are written to both sinks after\n * the primary event (depth-1; derived events are NOT re-enriched).\n */\nexport function createPipeline(options: PipelineOptions = {}): Pipeline {\n const {\n dbPath = DEFAULT_DB_PATH,\n afterCorrelate,\n otel,\n artifactPatterns = [],\n enrichRules,\n } = options;\n\n const sink = new SqliteSink(dbPath);\n const correlator = new Correlator();\n const normalizer = new Normalizer();\n const otelEmitter = new OtelEmitter(otel ?? {});\n\n const rules = enrichRules ?? createDefaultRules(artifactPatterns);\n const enricher = new Enricher({ correlator, artifactPatterns }, rules);\n\n /** Write a single CanonicalEvent to both sinks (SQLite always first, OTEL fail-open). */\n function sinkEvent(event: CanonicalEvent): void {\n sink.write(event);\n otelEmitter.emit({\n spanId: event.spanId,\n traceId: event.traceId,\n parentSpanId: event.parentSpanId,\n name: event.eventType,\n startTimeUnixNano: event.timeUnixNano,\n endTimeUnixNano: event.timeUnixNano,\n attributes: event.attributes,\n spanEvents: [],\n links: event.links.map((l) => ({\n targetSpanId: l.targetSpanId,\n targetTraceId: l.targetTraceId,\n attributes: { link_type: l.linkType, ...(l.attributes ?? {}) },\n })),\n });\n }\n\n const collector = new EventCollector((raw) => {\n const canonical = normalizer.normalize(raw);\n let correlated = correlator.correlate(canonical);\n\n // Optional post-correlate hook (backward compat; runs before Enricher)\n if (afterCorrelate) {\n correlated = afterCorrelate(correlated);\n }\n\n // Enricher: domain-specific enrichment (Phase 5)\n const { enriched, derived } = enricher.enrich(correlated);\n\n // Write primary event to both sinks\n sinkEvent(enriched);\n\n // Write derived events (from emitRecord) to both sinks\n // Derived events are NOT re-enriched (depth-1 limit prevents cycles)\n for (const d of derived) {\n sinkEvent(d);\n }\n });\n\n return { collector, correlator, sink, otelEmitter, enricher };\n}\n"],"mappings":"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","names":["DatabaseSync","mkdirSync","dirname","resolve","resolve","mkdirSync","dirname","DatabaseSync"]}
package/dist/cli/index.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  createPipeline,
6
6
  generateId,
7
7
  isoToUnixNano
8
- } from "../chunk-FVFGVBXM.js";
8
+ } from "../chunk-M3KM2QTL.js";
9
9
 
10
10
  // src/cli/index.ts
11
11
  import { readFileSync } from "fs";
package/dist/index.d.ts CHANGED
@@ -895,6 +895,137 @@ declare class SqliteQueryAdapter implements QueryAdapter {
895
895
  close(): void;
896
896
  }
897
897
 
898
+ interface EventMappingRule {
899
+ spans: SpanRule[];
900
+ links?: LinkRule[];
901
+ }
902
+ interface SpanRule {
903
+ axis: string;
904
+ name: string;
905
+ lifecycle: "open" | "close" | "instant";
906
+ attributes?: Record<string, string>;
907
+ each?: string;
908
+ condition?: string;
909
+ }
910
+ interface LinkRule {
911
+ type: string;
912
+ from: string;
913
+ to: string;
914
+ }
915
+ interface EventMappingConfig {
916
+ /** Map from hook event name to its span/link rules */
917
+ mappings: Record<string, EventMappingRule>;
918
+ }
919
+ interface ResolvedSpan {
920
+ eventType: string;
921
+ lifecycle: "open" | "close" | "instant";
922
+ spanId: string;
923
+ parentSpanId?: string;
924
+ attributes: Record<string, string | number | boolean>;
925
+ }
926
+ interface ResolvedLink {
927
+ linkType: string;
928
+ fromSpanId: string;
929
+ toSpanId: string;
930
+ }
931
+ interface EventMappingResult {
932
+ spans: ResolvedSpan[];
933
+ links: ResolvedLink[];
934
+ }
935
+
936
+ /**
937
+ * Evaluate an event_mapping rule against hook input to produce resolved spans and links.
938
+ */
939
+ declare function evaluateMapping(rule: EventMappingRule, hookInput: Record<string, unknown>): EventMappingResult;
940
+ /**
941
+ * Load an event-mapping config file (YAML or JSON).
942
+ */
943
+ declare function loadEventMappingConfig(filePath: string): Promise<EventMappingConfig>;
944
+
945
+ /**
946
+ * Resolve a template expression against hook input data.
947
+ * Supports: {{field}}, {{resolve:xxx}}, {{env:VAR}}, {{item}}
948
+ * For now, implements {{field}} (direct field access) and literal passthrough.
949
+ */
950
+ /**
951
+ * Resolve a template expression against hook input data.
952
+ */
953
+ declare function resolveTemplate(template: string, input: Record<string, unknown>, context?: {
954
+ item?: string;
955
+ }): string;
956
+
957
+ interface TaskPattern {
958
+ tag_prefix: string;
959
+ field: string;
960
+ }
961
+ /**
962
+ * Generate artifact-lookup.json from artifact-contracts declarations.
963
+ * Each artifact with path globs becomes an ArtifactPattern entry.
964
+ */
965
+ declare function generateArtifactLookup(artifactDeclarations: Array<{
966
+ artifact_id: string;
967
+ path_patterns: string[];
968
+ }>): ArtifactPattern[];
969
+ /**
970
+ * Generate task-patterns.json for structured tag parsing.
971
+ * Default patterns: [DSL_TASK=...], [AGENT_ROLE=...], [WORKFLOW_PHASE=...]
972
+ */
973
+ declare function generateTaskPatterns(): TaskPattern[];
974
+ /**
975
+ * Parse structured tags from a string.
976
+ * E.g. "[DSL_TASK=impl][AGENT_ROLE=implementer]" → { DSL_TASK: "impl", AGENT_ROLE: "implementer" }
977
+ */
978
+ declare function parseStructuredTags(input: string): Record<string, string>;
979
+
980
+ /**
981
+ * Quality gate command patterns and their types.
982
+ */
983
+ declare const QUALITY_GATE_PATTERNS: Array<{
984
+ pattern: RegExp;
985
+ gateType: string;
986
+ }>;
987
+ /**
988
+ * Emit a human.instruction event when a user submits a prompt.
989
+ */
990
+ declare function emitHumanInstruction(collector: EventCollector, options: {
991
+ sessionId: string;
992
+ prompt: string;
993
+ attachments?: unknown[];
994
+ parentSpanId?: string;
995
+ }): string;
996
+ /**
997
+ * Check if a command matches a quality gate pattern.
998
+ */
999
+ declare function matchQualityGate(command: string): {
1000
+ gateType: string;
1001
+ } | null;
1002
+ /**
1003
+ * Emit a quality_gate.result event after a shell command that matches a gate pattern.
1004
+ */
1005
+ declare function emitQualityGateResult(collector: EventCollector, options: {
1006
+ sessionId: string;
1007
+ command: string;
1008
+ exitCode: number;
1009
+ durationMs?: number;
1010
+ parentSpanId?: string;
1011
+ }): string | null;
1012
+ /**
1013
+ * Extract PR URL from command output.
1014
+ */
1015
+ declare function extractPrUrl(output: string): {
1016
+ url: string;
1017
+ number: number;
1018
+ } | null;
1019
+ /**
1020
+ * Emit a promotion.pr event when a PR is created.
1021
+ */
1022
+ declare function emitPromotionPr(collector: EventCollector, options: {
1023
+ sessionId: string;
1024
+ command: string;
1025
+ output: string;
1026
+ parentSpanId?: string;
1027
+ }): string | null;
1028
+
898
1029
  /**
899
1030
  * @aaac/observability — Phase 0–5 public API
900
1031
  *
@@ -956,4 +1087,4 @@ interface Pipeline {
956
1087
  */
957
1088
  declare function createPipeline(options?: PipelineOptions): Pipeline;
958
1089
 
959
- export { type ActiveTask, type ArtifactPattern, type AttrValue, type CanonicalEvent, type CanonicalLink, Correlator, DEFAULT_DB_PATH, DefaultExternalRegistrar, type EmitInput, type EnrichApi, type EnrichContext, type EnrichResult, type EnrichRule, Enricher, type EnricherOptions, EventCollector, type EventQueryFilter, type ExecutionEnvelope, type ExternalEventRegistrar, type Lifecycle, type LinkDirection, type LinkRecord, type MappedSpan, type MappedSpanEvent, type MappedSpanLink, NormalizationError, Normalizer, OtelEmitter, type OtelEmitterOptions, type Pipeline, type PipelineOptions, type QueryAdapter, type RawEvent, type RawEventHandler, type RawExternalEvent, type SpanQueryFilter, type SpanRecord, SqliteQueryAdapter, SqliteSink, type StoredEvent, activeTasksKey, allTaskSpansKey, keys as correlateKeys, createArtifactRule, createCrossAxisRule, createDefaultRules, createPipeline, createProcessRule, createPromotionRule, currentTimeUnixNano, generateId, globToRegex, isoToUnixNano, lookupArtifact, mapAllEventsToSpans, mapEventsToSpan, recordTaskArtifact, taskFilesKey, validateRawEvent };
1090
+ export { type ActiveTask, type ArtifactPattern, type AttrValue, type CanonicalEvent, type CanonicalLink, Correlator, DEFAULT_DB_PATH, DefaultExternalRegistrar, type EmitInput, type EnrichApi, type EnrichContext, type EnrichResult, type EnrichRule, Enricher, type EnricherOptions, EventCollector, type EventMappingConfig, type EventMappingResult, type EventMappingRule, type EventQueryFilter, type ExecutionEnvelope, type ExternalEventRegistrar, type Lifecycle, type LinkDirection, type LinkRecord, type LinkRule, type MappedSpan, type MappedSpanEvent, type MappedSpanLink, NormalizationError, Normalizer, OtelEmitter, type OtelEmitterOptions, type Pipeline, type PipelineOptions, QUALITY_GATE_PATTERNS, type QueryAdapter, type RawEvent, type RawEventHandler, type RawExternalEvent, type ResolvedLink, type ResolvedSpan, type SpanQueryFilter, type SpanRecord, type SpanRule, SqliteQueryAdapter, SqliteSink, type StoredEvent, activeTasksKey, allTaskSpansKey, keys as correlateKeys, createArtifactRule, createCrossAxisRule, createDefaultRules, createPipeline, createProcessRule, createPromotionRule, currentTimeUnixNano, emitHumanInstruction, emitPromotionPr, emitQualityGateResult, evaluateMapping, extractPrUrl, generateArtifactLookup, generateId, generateTaskPatterns, globToRegex, isoToUnixNano, loadEventMappingConfig, lookupArtifact, mapAllEventsToSpans, mapEventsToSpan, matchQualityGate, parseStructuredTags, recordTaskArtifact, resolveTemplate, taskFilesKey, validateRawEvent };
package/dist/index.js CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  NormalizationError,
8
8
  Normalizer,
9
9
  OtelEmitter,
10
+ QUALITY_GATE_PATTERNS,
10
11
  SqliteQueryAdapter,
11
12
  SqliteSink,
12
13
  activeTasksKey,
@@ -18,17 +19,28 @@ import {
18
19
  createProcessRule,
19
20
  createPromotionRule,
20
21
  currentTimeUnixNano,
22
+ emitHumanInstruction,
23
+ emitPromotionPr,
24
+ emitQualityGateResult,
25
+ evaluateMapping,
26
+ extractPrUrl,
27
+ generateArtifactLookup,
21
28
  generateId,
29
+ generateTaskPatterns,
22
30
  globToRegex,
23
31
  isoToUnixNano,
24
32
  keys,
33
+ loadEventMappingConfig,
25
34
  lookupArtifact,
26
35
  mapAllEventsToSpans,
27
36
  mapEventsToSpan,
37
+ matchQualityGate,
38
+ parseStructuredTags,
28
39
  recordTaskArtifact,
40
+ resolveTemplate,
29
41
  taskFilesKey,
30
42
  validateRawEvent
31
- } from "./chunk-FVFGVBXM.js";
43
+ } from "./chunk-M3KM2QTL.js";
32
44
  export {
33
45
  Correlator,
34
46
  DEFAULT_DB_PATH,
@@ -38,6 +50,7 @@ export {
38
50
  NormalizationError,
39
51
  Normalizer,
40
52
  OtelEmitter,
53
+ QUALITY_GATE_PATTERNS,
41
54
  SqliteQueryAdapter,
42
55
  SqliteSink,
43
56
  activeTasksKey,
@@ -50,13 +63,24 @@ export {
50
63
  createProcessRule,
51
64
  createPromotionRule,
52
65
  currentTimeUnixNano,
66
+ emitHumanInstruction,
67
+ emitPromotionPr,
68
+ emitQualityGateResult,
69
+ evaluateMapping,
70
+ extractPrUrl,
71
+ generateArtifactLookup,
53
72
  generateId,
73
+ generateTaskPatterns,
54
74
  globToRegex,
55
75
  isoToUnixNano,
76
+ loadEventMappingConfig,
56
77
  lookupArtifact,
57
78
  mapAllEventsToSpans,
58
79
  mapEventsToSpan,
80
+ matchQualityGate,
81
+ parseStructuredTags,
59
82
  recordTaskArtifact,
83
+ resolveTemplate,
60
84
  taskFilesKey,
61
85
  validateRawEvent
62
86
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aaac/observability",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "In-process observability for @aaac — EventCollector / Normalizer / Correlator / SqliteSink",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -47,6 +47,7 @@
47
47
  "@opentelemetry/api": "^1.9.1",
48
48
  "@opentelemetry/exporter-trace-otlp-http": "^0.218.0",
49
49
  "@opentelemetry/sdk-trace-base": "^2.7.1",
50
- "commander": "^15.0.0"
50
+ "commander": "^15.0.0",
51
+ "yaml": "^2.8.3"
51
52
  }
52
53
  }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/types/ids.ts","../src/collector/event-collector.ts","../src/collector/external-registrar.ts","../src/normalize/validator.ts","../src/normalize/normalizer.ts","../src/correlate/correlator.ts","../src/correlate/keys.ts","../src/enrich/rules/artifact.ts","../src/enrich/rules/process.ts","../src/enrich/rules/cross-axis.ts","../src/enrich/rules/promotion.ts","../src/enrich/enricher.ts","../src/sink/sqlite-sink.ts","../src/otel/otel-emitter.ts","../src/otel/span-mapper.ts","../src/query/sqlite-adapter.ts","../src/index.ts"],"sourcesContent":["/**\n * ID generation (UUIDv7) and time utilities for @aaac/observability.\n *\n * UUIDv7 format (RFC 9562):\n * bytes 0-5 : 48-bit Unix timestamp in milliseconds\n * byte 6 : 0x7_ (version 7, high nibble) | rand_a (low nibble)\n * byte 7 : rand_a (remaining 8 bits)\n * byte 8 : 0x8_-0xb_ (variant 10, top 2 bits) | rand_b\n * bytes 9-15 : rand_b\n */\nimport { randomBytes } from \"node:crypto\";\n\n/**\n * Generate a UUIDv7 string.\n * Monotonically ordered by creation time at millisecond granularity.\n */\nexport function generateId(): string {\n const ms = BigInt(Date.now());\n const buf = randomBytes(16);\n\n // Embed 48-bit Unix ms timestamp into bytes 0-5\n buf[0] = Number((ms >> 40n) & 0xffn);\n buf[1] = Number((ms >> 32n) & 0xffn);\n buf[2] = Number((ms >> 24n) & 0xffn);\n buf[3] = Number((ms >> 16n) & 0xffn);\n buf[4] = Number((ms >> 8n) & 0xffn);\n buf[5] = Number(ms & 0xffn);\n\n // Set version 7 in the high nibble of byte 6\n buf[6] = (buf[6] & 0x0f) | 0x70;\n\n // Set RFC 4122 variant 10 in the top 2 bits of byte 8\n buf[8] = (buf[8] & 0x3f) | 0x80;\n\n const hex = buf.toString(\"hex\");\n return (\n `${hex.slice(0, 8)}-` +\n `${hex.slice(8, 12)}-` +\n `${hex.slice(12, 16)}-` +\n `${hex.slice(16, 20)}-` +\n `${hex.slice(20, 32)}`\n );\n}\n\n/**\n * Current wall-clock time as Unix nanoseconds (BigInt).\n * Resolution is millisecond (Date.now() × 1_000_000).\n */\nexport function currentTimeUnixNano(): bigint {\n return BigInt(Date.now()) * 1_000_000n;\n}\n\n/**\n * Convert an ISO-8601 / RFC-3339 string to Unix nanoseconds (BigInt).\n * Falls back to current time if the string cannot be parsed.\n */\nexport function isoToUnixNano(iso: string): bigint {\n const ms = Date.parse(iso);\n if (Number.isNaN(ms)) {\n return currentTimeUnixNano();\n }\n return BigInt(ms) * 1_000_000n;\n}\n","/**\n * EventCollector — in-process entry point for the write pipeline.\n *\n * Responsibilities:\n * - Accept events from @aaac/runtime via emit() (in-process observer)\n * - Accept events from external sources (git-hook, ci, …) via registerExternalEvent()\n * - Wrap them as RawEvent with a receivedAt timestamp and forward to the downstream handler\n *\n * The downstream handler (pipeline) is injected via the constructor so that the\n * collector stays decoupled from normalizer / correlator / sink.\n */\nimport type { RawEvent, Lifecycle, AttrValue, CanonicalLink } from \"../types/canonical-event.js\";\n\n/** Minimal shape expected by both emit() and registerExternalEvent(). */\nexport interface EmitInput {\n source: string;\n eventType: string;\n lifecycle: Lifecycle;\n spanId?: string;\n parentSpanId?: string;\n attributes: Record<string, AttrValue>;\n links?: CanonicalLink[];\n}\n\n/** Callback that receives each RawEvent produced by the collector. */\nexport type RawEventHandler = (event: RawEvent) => void;\n\n/**\n * EventCollector wraps an injected pipeline handler and stamps each incoming\n * event with a receivedAt timestamp before forwarding.\n */\nexport class EventCollector {\n private readonly handler: RawEventHandler;\n\n constructor(handler: RawEventHandler) {\n this.handler = handler;\n }\n\n /**\n * In-process observer entry point.\n * Called by @aaac/runtime (or any in-process producer) with an event payload.\n */\n emit(event: EmitInput): void {\n const raw: RawEvent = {\n ...event,\n receivedAt: new Date().toISOString(),\n };\n this.handler(raw);\n }\n\n /**\n * External event registration API.\n * Called by git hooks, Cursor hooks, CI, or the `aaac-observ record` CLI.\n * Identical wire format to emit(); separated to aid future routing logic.\n */\n registerExternalEvent(event: EmitInput): void {\n const raw: RawEvent = {\n ...event,\n receivedAt: new Date().toISOString(),\n };\n this.handler(raw);\n }\n}\n","/**\n * ExternalEventRegistrar — thin facade implementing the external event\n * registration contract from architecture.md §9.\n *\n * External processes (git hooks, Cursor hooks, CI) call registerEvent() to\n * inject events into the same write pipeline as in-process events.\n */\nimport type { CanonicalLink, Lifecycle, AttrValue } from \"../types/canonical-event.js\";\nimport type { EventCollector } from \"./event-collector.js\";\n\n/** Typed input for external event sources (architecture.md §9). */\nexport interface RawExternalEvent {\n /** Event origin: 'git-hook' | 'cursor-hook' | 'ci' | ... */\n source: string;\n eventType: string;\n lifecycle: Lifecycle;\n spanId?: string;\n parentSpanId?: string;\n attributes: Record<string, AttrValue>;\n links?: CanonicalLink[];\n}\n\n/** Interface contract for external event registration (architecture.md §9). */\nexport interface ExternalEventRegistrar {\n registerEvent(event: RawExternalEvent): void;\n}\n\n/**\n * Default implementation — delegates to EventCollector.registerExternalEvent().\n */\nexport class DefaultExternalRegistrar implements ExternalEventRegistrar {\n constructor(private readonly collector: EventCollector) {}\n\n registerEvent(event: RawExternalEvent): void {\n this.collector.registerExternalEvent(event);\n }\n}\n","/**\n * Validator — checks that a RawEvent has all required fields before normalization.\n * Throws NormalizationError with a descriptive message on failure.\n */\nimport type { RawEvent } from \"../types/canonical-event.js\";\n\nconst VALID_LIFECYCLES = new Set([\"open\", \"close\", \"event\", \"instant\"]);\n\n/** Thrown when a RawEvent fails validation. */\nexport class NormalizationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"NormalizationError\";\n }\n}\n\n/**\n * Validate a RawEvent.\n * Throws NormalizationError listing all violated constraints.\n */\nexport function validateRawEvent(event: RawEvent): void {\n const errors: string[] = [];\n\n if (!event.source || typeof event.source !== \"string\") {\n errors.push(\"'source' must be a non-empty string\");\n }\n if (!event.eventType || typeof event.eventType !== \"string\") {\n errors.push(\"'eventType' must be a non-empty string\");\n }\n if (!event.lifecycle || !VALID_LIFECYCLES.has(event.lifecycle)) {\n errors.push(\n `'lifecycle' must be one of: ${[...VALID_LIFECYCLES].join(\", \")}`\n );\n }\n if (!event.receivedAt || typeof event.receivedAt !== \"string\") {\n errors.push(\"'receivedAt' must be a non-empty string\");\n }\n if (event.attributes === null || typeof event.attributes !== \"object\" || Array.isArray(event.attributes)) {\n errors.push(\"'attributes' must be a plain object\");\n }\n\n if (errors.length > 0) {\n throw new NormalizationError(\n `Invalid RawEvent: ${errors.join(\"; \")}`\n );\n }\n}\n","/**\n * Normalizer — converts RawEvent → CanonicalEvent.\n *\n * Responsibilities (architecture.md §4.1 / §3):\n * - Required-key validation (delegated to validator.ts)\n * - Type coercion: attribute values are coerced to AttrValue\n * - Default values: id, timeUnixNano, spanId, traceId are generated if absent\n * - Link declaration conversion: RawEvent.links → CanonicalEvent.links\n * - Extraction of well-known scalar fields: sessionId, taskId, runId\n *\n * traceId *propagation* (child → parent) is the Correlator's responsibility;\n * the Normalizer only generates a fresh traceId as the default.\n */\nimport type { RawEvent, CanonicalEvent, AttrValue } from \"../types/canonical-event.js\";\nimport { generateId, isoToUnixNano } from \"../types/ids.js\";\nimport { validateRawEvent } from \"./validator.js\";\n\n/** Coerce an unknown attribute value to AttrValue (string | number | boolean). */\nfunction coerceAttrValue(value: unknown): AttrValue {\n if (\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n ) {\n return value;\n }\n return String(value);\n}\n\n/** Coerce all attribute values in the map. */\nfunction coerceAttributes(\n raw: Record<string, unknown>\n): Record<string, AttrValue> {\n const result: Record<string, AttrValue> = {};\n for (const [k, v] of Object.entries(raw)) {\n result[k] = coerceAttrValue(v);\n }\n return result;\n}\n\n/**\n * Normalizer converts a validated RawEvent into a CanonicalEvent.\n * An instance is stateless — the same instance may be reused for many events.\n */\nexport class Normalizer {\n /**\n * Validate and normalise a single RawEvent.\n * Throws NormalizationError on invalid input.\n */\n normalize(raw: RawEvent): CanonicalEvent {\n validateRawEvent(raw);\n\n const id = generateId();\n const timeUnixNano = isoToUnixNano(raw.receivedAt);\n const spanId = raw.spanId ?? generateId();\n const traceId = generateId(); // Correlator propagates the correct traceId later\n\n const attributes = coerceAttributes(\n raw.attributes as Record<string, unknown>\n );\n\n // Extract well-known scalars from attributes\n const sessionId = extractString(attributes, \"session_id\");\n const taskId = extractString(attributes, \"task_id\");\n const runId = extractString(attributes, \"run_id\");\n\n return {\n id,\n timeUnixNano,\n source: raw.source,\n eventType: raw.eventType,\n lifecycle: raw.lifecycle,\n traceId,\n spanId,\n parentSpanId: raw.parentSpanId,\n runId,\n sessionId,\n taskId,\n attributes,\n links: raw.links ?? [],\n };\n }\n}\n\nfunction extractString(\n attrs: Record<string, AttrValue>,\n key: string\n): string | undefined {\n const v = attrs[key];\n return typeof v === \"string\" && v.length > 0 ? v : undefined;\n}\n","/**\n * Correlator — generic, domain-agnostic span correlation.\n *\n * Responsibilities (architecture.md §4.1 / §8):\n * - span lifecycle pairing: tracks 'open' events by spanId; pairs with 'close'\n * - duration computation: stored in the in-memory state so SqliteSink can read\n * startTime when processing the close event\n * - traceId propagation: child spans inherit their parent's traceId\n * - in-memory short-term cache: cacheGet / cacheSet for Enricher use (Phase 1+)\n *\n * No domain logic. The correlator does not know about agent / process / promotion axes.\n */\nimport type { CanonicalEvent } from \"../types/canonical-event.js\";\n\n/** Cached state for an open span. */\ninterface OpenSpanEntry {\n event: CanonicalEvent;\n}\n\n/**\n * Correlator maintains in-memory state across events in the same write session.\n * A single instance should be reused for the lifetime of the write pipeline.\n */\nexport class Correlator {\n /** Open spans awaiting a matching 'close' event (keyed by spanId). */\n private readonly openSpans = new Map<string, OpenSpanEntry>();\n\n /** General-purpose short-term key-value cache (architecture.md §8). */\n private readonly cache = new Map<string, unknown>();\n\n /**\n * Correlate a single CanonicalEvent.\n *\n * Mutates the event's traceId if a parent span is found in the open set.\n * Updates open span state according to the event's lifecycle.\n *\n * Returns the (possibly updated) CanonicalEvent.\n */\n correlate(event: CanonicalEvent): CanonicalEvent {\n let result = { ...event };\n\n // traceId propagation: child events inherit parent's traceId\n if (result.parentSpanId !== undefined) {\n const parentEntry = this.openSpans.get(result.parentSpanId);\n if (parentEntry !== undefined) {\n result = { ...result, traceId: parentEntry.event.traceId };\n }\n }\n\n switch (result.lifecycle) {\n case \"open\":\n this.openSpans.set(result.spanId, { event: result });\n break;\n\n case \"close\": {\n // Duration computation is delegated to the sink (it has the stored\n // start_time and can do the arithmetic in SQL). We only clean up state.\n this.openSpans.delete(result.spanId);\n break;\n }\n\n case \"event\":\n // Span event: no state change needed; traceId already propagated above.\n break;\n\n case \"instant\":\n // Instant span: self-contained; no open state to track.\n break;\n }\n\n return result;\n }\n\n /**\n * Retrieve the currently-open entry for a span (undefined if not open).\n * Useful for SqliteSink to look up start_time without a DB round-trip.\n */\n getOpenSpan(spanId: string): CanonicalEvent | undefined {\n return this.openSpans.get(spanId)?.event;\n }\n\n // ── Generic key-value cache (architecture.md §8) ──────────────────────────\n\n /** Store a value under an arbitrary key (TTL not enforced in Phase 0). */\n cacheSet(key: string, value: unknown): void {\n this.cache.set(key, value);\n }\n\n /** Retrieve a cached value; returns undefined if absent. */\n cacheGet<T = unknown>(key: string): T | undefined {\n return this.cache.get(key) as T | undefined;\n }\n\n /** Remove a cached entry. */\n cacheDel(key: string): void {\n this.cache.delete(key);\n }\n\n /** Return the number of currently-open spans (useful for testing). */\n get openSpanCount(): number {\n return this.openSpans.size;\n }\n}\n","/**\n * Cache key helpers for the Correlator's in-memory short-term store.\n * architecture.md §8.2\n */\n\nexport const keys = {\n /** Per-run context: run:{id} */\n run: (id: string): string => `run:${id}`,\n /** Latest span in a trace: trace:{id}:latest */\n traceLatest: (id: string): string => `trace:${id}:latest`,\n /** Per-session context: session:{id} */\n session: (id: string): string => `session:${id}`,\n /** Tool call tracking: toolcall:{id} */\n toolCall: (id: string): string => `toolcall:${id}`,\n /** Request tracking: request:{id} */\n request: (id: string): string => `request:${id}`,\n} as const;\n","/**\n * R7 — Artifact ID resolution for process.edit events.\n *\n * shift-left-event-mapping.md §5.1:\n * \"R7: artifact_id 未解決 → file_path → artifact-lookup (§3.3) — longest-match glob,\n * 未マッチは unknown\"\n *\n * Resolution is via STRUCTURED lookup only (path glob → artifact_id).\n * No natural-language guessing; unresolved → \"unknown\".\n */\nimport type { EnrichRule, ArtifactPattern } from \"../api.js\";\n\n// ── Glob matching ─────────────────────────────────────────────────────────────\n\n/**\n * Convert a glob segment (no \"**\" present) to a regex fragment.\n * Escapes regex metacharacters and replaces glob wildcards * and ?.\n */\nfunction globSegmentToRegex(segment: string): string {\n return segment\n .replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\") // escape regex special chars\n .replace(/\\*/g, \"[^/]*\") // * → any non-separator sequence\n .replace(/\\?/g, \"[^/]\"); // ? → any single non-separator char\n}\n\n// globToRegex: convert a glob pattern to a RegExp.\n// Wildcards: ** = zero-or-more path segments (across slashes)\n// * = zero-or-more non-separator chars\n// ? = exactly one non-separator char\n// Basename-only patterns (no slash in pattern) match the filename anywhere.\n// Algorithm for **: split on \"**/\" boundaries; insert \"zero-or-more segments\"\n// regex between the split parts so src-star-star-foo.ts matches src/foo.ts.\nexport function globToRegex(pattern: string): RegExp {\n // Basename-only patterns (no \"/\") match the last path component anywhere.\n if (!pattern.includes(\"/\")) {\n const base = globSegmentToRegex(pattern);\n return new RegExp(`(?:^|/)${base}$`);\n }\n\n // Split on \"**/\" to handle zero-or-more segment matches.\n // \"src/**/*.ts\" → [\"src/\", \"*.ts\"]\n // \"**/*.md\" → [\"\", \"*.md\"]\n // \"a/b/c.ts\" → [\"a/b/c.ts\"] (no split, no ** present)\n const parts = pattern.split(\"**/\");\n\n let result = \"^\";\n for (let i = 0; i < parts.length; i++) {\n if (i > 0) {\n // At each \"**/\" split boundary: zero-or-more path segments (including none)\n result += \"(?:.*/)?\";\n }\n result += globSegmentToRegex(parts[i]);\n }\n result += \"$\";\n\n return new RegExp(result);\n}\n\n// ── Longest-match lookup ──────────────────────────────────────────────────────\n\n/**\n * Resolve a file path to an artifact_id using longest-match glob.\n *\n * Among all patterns that match `filePath`, the one with the longest pattern\n * string wins (longer = more specific). Ties are broken by declaration order\n * (first wins). Returns \"unknown\" if no pattern matches.\n *\n * shift-left-event-mapping.md §3.3\n */\nexport function lookupArtifact(\n filePath: string,\n patterns: ArtifactPattern[],\n): string {\n let bestArtifactId: string | undefined;\n let bestLen = -1;\n\n for (const entry of patterns) {\n const rx = globToRegex(entry.pattern);\n if (rx.test(filePath)) {\n if (entry.pattern.length > bestLen) {\n bestLen = entry.pattern.length;\n bestArtifactId = entry.artifact_id;\n }\n }\n }\n\n return bestArtifactId ?? \"unknown\";\n}\n\n// ── R7 enrichment rule ────────────────────────────────────────────────────────\n\n/**\n * Create the R7 artifact-resolution enrichment rule.\n *\n * Applies to: process.edit (instant)\n * Action: sets `edit.artifact_id` on the event attributes via lookupArtifact.\n * If no pattern matches, sets `edit.artifact_id = \"unknown\"`.\n * Skips if `edit.artifact_id` is already set to a non-empty, non-\"unknown\" value.\n *\n * The `patterns` list is provided via config/constructor (no hardcoded project values).\n */\nexport function createArtifactRule(patterns: ArtifactPattern[]): EnrichRule {\n return function artifactRule(event, ctx): void {\n if (event.eventType !== \"process.edit\" || event.lifecycle !== \"instant\") {\n return;\n }\n\n const filePath = event.attributes[\"edit.path\"];\n if (typeof filePath !== \"string\" || filePath.length === 0) {\n return; // No path to resolve\n }\n\n // Skip if already fully resolved by the event producer (non-empty, non-\"unknown\")\n const existing = event.attributes[\"edit.artifact_id\"];\n if (\n typeof existing === \"string\" &&\n existing.length > 0 &&\n existing !== \"unknown\"\n ) {\n return;\n }\n\n const artifactId = ctx.api.lookupArtifact(filePath);\n event.attributes[\"edit.artifact_id\"] = artifactId;\n ctx.api.logDebug(\n `R7: resolved artifact_id=${artifactId} for path=${filePath}`,\n );\n };\n}\n","/**\n * R3/R4 — Process parent resolution for process.edit and process.tool events.\n *\n * shift-left-event-mapping.md §5.1:\n * R3: process.edit → process.task (parent)\n * \"session_id の active task スタック + file_path→artifact で一意化\"\n * \"単一 active task なら確定、複数なら artifact 一致で一意化、不能なら ambiguous\"\n * R4: process.tool → process.task (parent)\n * \"単一なら確定、複数なら ambiguous\"\n *\n * All resolution via STRUCTURED keys only:\n * - active task stack per session (in-memory cache)\n * - artifact_id already set by R7 (for disambiguation in R3)\n * - Never guess from natural language or timing\n *\n * Cache schema maintained by this rule:\n * session:{sid}:active_tasks → ActiveTask[] (currently open tasks)\n * session:{sid}:all_task_spans → string[] (all task spanIds, open + closed, for R5)\n * session:{sid}:task_files:{taskSpanId} → string[] (accumulated modified files, for R5)\n */\nimport type { EnrichRule } from \"../api.js\";\n\n/** A currently-open process.task span tracked on the active stack. */\nexport interface ActiveTask {\n spanId: string;\n taskId: string | undefined;\n}\n\n// ── Cache key helpers (exported for cross-axis.ts) ────────────────────────────\n\nexport function activeTasksKey(sessionId: string): string {\n return `session:${sessionId}:active_tasks`;\n}\n\nexport function allTaskSpansKey(sessionId: string): string {\n return `session:${sessionId}:all_task_spans`;\n}\n\nexport function taskFilesKey(sessionId: string, taskSpanId: string): string {\n return `session:${sessionId}:task_files:${taskSpanId}`;\n}\n\n// ── R3/R4 enrichment rule ─────────────────────────────────────────────────────\n\n/**\n * Create the R3/R4 process-parent enrichment rule.\n *\n * Handles three event types:\n * 1. process.task open — push task onto active-task stack\n * 2. process.task close — pop task from active-task stack\n * 3. process.edit instant — resolve parentSpanId/taskId (R3)\n * 4. process.tool instant — resolve parentSpanId/taskId (R4)\n *\n * Resolution algorithm (R3/R4):\n * - If event already has parentSpanId → skip (already resolved)\n * - 0 active tasks → leave provisional (no parentSpanId)\n * - 1 active task → set parentSpanId + taskId (confirmed)\n * - 2+ active tasks (R3) → try to disambiguate by edit.artifact_id match\n * against task's artifact_ids; if still ambiguous → set reconcile.status=ambiguous\n * - 2+ active tasks (R4) → set reconcile.status=ambiguous (no artifact disambig)\n */\nexport function createProcessRule(): EnrichRule {\n return function processRule(event, ctx): void {\n const sid = event.sessionId;\n\n // ── Maintain active-task stack on process.task open/close ─────────────────\n\n if (event.eventType === \"process.task\") {\n if (!sid) return;\n\n if (event.lifecycle === \"open\") {\n const tasks =\n ctx.api.cacheGet<ActiveTask[]>(activeTasksKey(sid)) ?? [];\n tasks.push({ spanId: event.spanId, taskId: event.taskId });\n ctx.api.cacheSet(activeTasksKey(sid), tasks);\n\n // Register span in the all-task list (kept after close for R5)\n const allSpans =\n ctx.api.cacheGet<string[]>(allTaskSpansKey(sid)) ?? [];\n if (!allSpans.includes(event.spanId)) {\n allSpans.push(event.spanId);\n ctx.api.cacheSet(allTaskSpansKey(sid), allSpans);\n }\n // Initialise the file-tracking list for this task (used by R5)\n ctx.api.cacheSet(taskFilesKey(sid, event.spanId), []);\n ctx.api.logDebug(\n `R3/R4: pushed task spanId=${event.spanId} session=${sid}`,\n );\n return;\n }\n\n if (event.lifecycle === \"close\") {\n const tasks =\n ctx.api.cacheGet<ActiveTask[]>(activeTasksKey(sid)) ?? [];\n const updated = tasks.filter((t) => t.spanId !== event.spanId);\n ctx.api.cacheSet(activeTasksKey(sid), updated);\n ctx.api.logDebug(\n `R3/R4: popped task spanId=${event.spanId} session=${sid}`,\n );\n return;\n }\n\n return; // other lifecycles (event, instant) — no action for process.task\n }\n\n // ── R3: process.edit → resolve parent task ────────────────────────────────\n\n if (event.eventType === \"process.edit\" && event.lifecycle === \"instant\") {\n if (event.parentSpanId) return; // already resolved upstream\n if (!sid) return;\n\n const tasks =\n ctx.api.cacheGet<ActiveTask[]>(activeTasksKey(sid)) ?? [];\n\n if (tasks.length === 0) {\n // No open tasks — leave provisional (empty parentSpanId)\n return;\n }\n\n if (tasks.length === 1) {\n // Single active task → confirmed\n event.parentSpanId = tasks[0].spanId;\n event.taskId = tasks[0].taskId;\n\n // Record this file under the task for R5\n const fp = event.attributes[\"edit.path\"];\n if (typeof fp === \"string\" && fp.length > 0) {\n const files =\n ctx.api.cacheGet<string[]>(\n taskFilesKey(sid, tasks[0].spanId),\n ) ?? [];\n if (!files.includes(fp)) {\n files.push(fp);\n ctx.api.cacheSet(taskFilesKey(sid, tasks[0].spanId), files);\n }\n }\n ctx.api.logDebug(\n `R3: resolved parent=${tasks[0].spanId} for process.edit`,\n );\n return;\n }\n\n // Multiple active tasks — try to disambiguate by artifact_id\n // R7 must have already run to set edit.artifact_id\n const artifactId = event.attributes[\"edit.artifact_id\"];\n if (typeof artifactId === \"string\" && artifactId !== \"unknown\") {\n // Look for a task that owns this artifact\n // Task artifact ownership is stored by the task's `task.artifact_id` attribute\n // if it was set at emit time (structured key — no guessing)\n const owner = tasks.find(\n (t) => {\n // Try to look up the task's known artifact from cache\n const taskArtifact = ctx.api.cacheGet<string>(\n `session:${sid}:task_artifact:${t.spanId}`,\n );\n return taskArtifact === artifactId;\n },\n );\n if (owner) {\n event.parentSpanId = owner.spanId;\n event.taskId = owner.taskId;\n const fp = event.attributes[\"edit.path\"];\n if (typeof fp === \"string\" && fp.length > 0) {\n const files =\n ctx.api.cacheGet<string[]>(taskFilesKey(sid, owner.spanId)) ??\n [];\n if (!files.includes(fp)) {\n files.push(fp);\n ctx.api.cacheSet(taskFilesKey(sid, owner.spanId), files);\n }\n }\n ctx.api.logDebug(\n `R3: disambiguated parent=${owner.spanId} by artifact=${artifactId}`,\n );\n return;\n }\n }\n\n // Cannot disambiguate — mark reconcile.status=ambiguous, keep provisional\n event.attributes[\"reconcile.status\"] = \"ambiguous\";\n ctx.api.logDebug(\n `R3: ambiguous (${tasks.length} active tasks, no unique artifact match)`,\n );\n return;\n }\n\n // ── R4: process.tool → resolve parent task ────────────────────────────────\n\n if (event.eventType === \"process.tool\" && event.lifecycle === \"instant\") {\n if (event.parentSpanId) return; // already resolved upstream\n if (!sid) return;\n\n const tasks =\n ctx.api.cacheGet<ActiveTask[]>(activeTasksKey(sid)) ?? [];\n\n if (tasks.length === 0) {\n // No open tasks — leave provisional\n return;\n }\n\n if (tasks.length === 1) {\n // Single active task → confirmed\n event.parentSpanId = tasks[0].spanId;\n event.taskId = tasks[0].taskId;\n ctx.api.logDebug(\n `R4: resolved parent=${tasks[0].spanId} for process.tool`,\n );\n return;\n }\n\n // Multiple tasks, no artifact disambiguation for tools → ambiguous\n event.attributes[\"reconcile.status\"] = \"ambiguous\";\n ctx.api.logDebug(\n `R4: ambiguous (${tasks.length} active tasks)`,\n );\n }\n };\n}\n\n/**\n * Record a task's primary artifact in the cache so that R3 disambiguation can use it.\n * Call this when a process.task open event carries a known artifact_id attribute.\n *\n * This is an optional supplement to the createProcessRule — the main rule will\n * call lookupArtifact on the task's own attributes if available, but the cache\n * entry is more reliable for disambiguation.\n */\nexport function recordTaskArtifact(\n sessionId: string,\n taskSpanId: string,\n artifactId: string,\n cacheSet: (key: string, value: unknown) => void,\n): void {\n cacheSet(`session:${sessionId}:task_artifact:${taskSpanId}`, artifactId);\n}\n","/**\n * R5 — Cross-axis link: process.task → promotion.commit (materializes_as_commit).\n *\n * shift-left-event-mapping.md §5.1:\n * R5: \"on promotion.commit close, addLink materializes_as_commit for each task\n * whose modified_files ∩ committed_files is non-empty\"\n *\n * Correlation key: modified_files (accumulated by R3 on process.edit) ∩ committed_files\n * (attribute on the promotion.commit event).\n *\n * Implementation:\n * - process.ts R3/R4 rule maintains the task file lists:\n * session:{sid}:all_task_spans → string[] (all task spanIds)\n * session:{sid}:task_files:{taskSpanId} → string[] (files modified under that task)\n * - This rule reads those lists on promotion.commit close and calls addLink\n * for each intersecting task. The link is attached to the commit event.\n *\n * architecture.md §5.2:\n * \"事後発見リンクはリンクを発見したイベントの links に追加する\"\n * (Post-discovery links are added to the discovering event's links.)\n * → The materializes_as_commit link is added to the promotion.commit event.\n */\nimport { allTaskSpansKey, taskFilesKey } from \"./process.js\";\nimport type { EnrichRule } from \"../api.js\";\n\n// ── R5 enrichment rule ────────────────────────────────────────────────────────\n\n/**\n * Create the R5 cross-axis enrichment rule.\n *\n * Applies to: promotion.commit (close)\n * Action: for each known task whose accumulated modified_files intersect the\n * committed_files attribute, add a materializes_as_commit link to the\n * promotion.commit event (pointing to the task's spanId).\n *\n * committed_files must be a JSON-encoded string array on the commit event.\n * If absent or unparseable, the rule skips silently (fail-open).\n */\nexport function createCrossAxisRule(): EnrichRule {\n return function crossAxisRule(event, ctx): void {\n if (\n event.eventType !== \"promotion.commit\" ||\n event.lifecycle !== \"close\"\n ) {\n return;\n }\n\n const sid = event.sessionId;\n const committedFilesRaw = event.attributes[\"committed_files\"];\n if (!sid || typeof committedFilesRaw !== \"string\") {\n return;\n }\n\n let committedFiles: string[];\n try {\n const parsed: unknown = JSON.parse(committedFilesRaw);\n if (!Array.isArray(parsed)) {\n ctx.api.logDebug(\n \"R5: committed_files is not an array\",\n committedFilesRaw,\n );\n return;\n }\n committedFiles = parsed.filter((x): x is string => typeof x === \"string\");\n } catch {\n ctx.api.logDebug(\"R5: failed to parse committed_files\", committedFilesRaw);\n return;\n }\n\n if (committedFiles.length === 0) return;\n\n const committedSet = new Set(committedFiles);\n const allTaskSpans =\n ctx.api.cacheGet<string[]>(allTaskSpansKey(sid)) ?? [];\n\n for (const taskSpanId of allTaskSpans) {\n const taskFiles =\n ctx.api.cacheGet<string[]>(taskFilesKey(sid, taskSpanId)) ?? [];\n\n const hasIntersection = taskFiles.some((f) => committedSet.has(f));\n if (hasIntersection) {\n ctx.api.addLink({\n linkType: \"materializes_as_commit\",\n targetSpanId: taskSpanId,\n });\n ctx.api.logDebug(\n `R5: added materializes_as_commit → task=${taskSpanId}`,\n );\n }\n }\n };\n}\n","/**\n * R1/R2 — Promotion change → file → commit correlation.\n *\n * shift-left-event-mapping.md §5.1:\n * R1: promotion.change → promotion.file (parent / contains_change)\n * \"post-commit time, link orphan changes to file\"\n * Correlation key: session_id + file.path, commit time window\n * R2: promotion.file → promotion.commit (parent)\n * \"post-commit time, emitRecord で parent を commit に設定\"\n *\n * Algorithm:\n * 1. On promotion.change (instant): cache the change spanId by (session_id, file.path)\n * 2. On promotion.commit close:\n * - Parse committed_files from attributes (JSON string array)\n * - For each committed file:\n * a. Emit a promotion.file event with parentSpanId=commitSpanId (R2)\n * b. Attach contains_change links to all cached change spanIds for that file (R1)\n * - Clear the change cache for committed files\n *\n * Cache schema:\n * session:{sid}:changes:{filePath} → ChangeRef[]\n */\nimport { generateId } from \"../../types/ids.js\";\nimport type { CanonicalEvent, CanonicalLink } from \"../../types/canonical-event.js\";\nimport type { EnrichRule } from \"../api.js\";\n\n/** Reference to a cached promotion.change event. */\ninterface ChangeRef {\n spanId: string;\n traceId: string;\n}\n\n// ── Cache key helpers ─────────────────────────────────────────────────────────\n\nfunction changesKey(sessionId: string, filePath: string): string {\n return `session:${sessionId}:changes:${filePath}`;\n}\n\n// ── R1/R2 enrichment rule ─────────────────────────────────────────────────────\n\n/**\n * Create the R1/R2 promotion-correlation enrichment rule.\n *\n * Handles two event types:\n * 1. promotion.change instant — cache the change by (session_id, file.path)\n * 2. promotion.commit close — emit promotion.file per committed file (R2),\n * with contains_change links to cached changes (R1)\n *\n * The committed_files attribute on promotion.commit must be a JSON-encoded\n * string array (e.g., '[\"src/foo.ts\",\"README.md\"]').\n * If absent or unparseable, the rule skips silently (fail-open).\n */\nexport function createPromotionRule(): EnrichRule {\n return function promotionRule(event, ctx): void {\n // ── R1a: cache promotion.change by session_id + file.path ─────────────────\n\n if (\n event.eventType === \"promotion.change\" &&\n event.lifecycle === \"instant\"\n ) {\n const sid = event.sessionId;\n const fp = event.attributes[\"file.path\"];\n if (!sid || typeof fp !== \"string\" || fp.length === 0) {\n return;\n }\n\n const key = changesKey(sid, fp);\n const existing = ctx.api.cacheGet<ChangeRef[]>(key) ?? [];\n existing.push({ spanId: event.spanId, traceId: event.traceId });\n ctx.api.cacheSet(key, existing);\n ctx.api.logDebug(\n `R1: cached promotion.change spanId=${event.spanId} file=${fp} session=${sid}`,\n );\n return;\n }\n\n // ── R1/R2: on promotion.commit close, emit promotion.file per file ────────\n\n if (\n event.eventType === \"promotion.commit\" &&\n event.lifecycle === \"close\"\n ) {\n const sid = event.sessionId;\n const committedFilesRaw = event.attributes[\"committed_files\"];\n if (!sid || typeof committedFilesRaw !== \"string\") {\n return;\n }\n\n let committedFiles: string[];\n try {\n const parsed: unknown = JSON.parse(committedFilesRaw);\n if (!Array.isArray(parsed)) {\n ctx.api.logDebug(\"R1/R2: committed_files is not an array\", committedFilesRaw);\n return;\n }\n committedFiles = parsed.filter((x): x is string => typeof x === \"string\");\n } catch {\n ctx.api.logDebug(\"R1/R2: failed to parse committed_files\", committedFilesRaw);\n return;\n }\n\n const commitSpanId = event.spanId;\n const commitTraceId = event.traceId;\n\n for (const fp of committedFiles) {\n const cacheKey = changesKey(sid, fp);\n const changeRefs = ctx.api.cacheGet<ChangeRef[]>(cacheKey) ?? [];\n\n // Build contains_change links from the promotion.file to each cached change\n const links: CanonicalLink[] = changeRefs.map((ref) => ({\n linkType: \"contains_change\",\n targetSpanId: ref.spanId,\n // Omit targetTraceId if same trace as the commit (most common case)\n targetTraceId:\n ref.traceId !== commitTraceId ? ref.traceId : undefined,\n }));\n\n // R2: emit promotion.file with parentSpanId = commitSpanId\n const fileEvent: CanonicalEvent = {\n id: generateId(),\n timeUnixNano: ctx.api.nowUnixNano(),\n source: \"enricher\",\n eventType: \"promotion.file\",\n lifecycle: \"instant\",\n traceId: commitTraceId,\n spanId: generateId(),\n parentSpanId: commitSpanId, // R2: parent = commit\n sessionId: sid,\n runId: event.runId,\n taskId: event.taskId,\n attributes: {\n \"file.path\": fp,\n \"commit.span_id\": commitSpanId,\n },\n links, // R1: contains_change links\n };\n\n ctx.api.emitRecord(fileEvent);\n ctx.api.logDebug(\n `R2: emitted promotion.file file=${fp} commit=${commitSpanId} changes=${links.length}`,\n );\n\n // Clear the change cache for this file — changes have been accounted for\n ctx.api.cacheSet(cacheKey, []);\n }\n }\n };\n}\n","/**\n * Enricher — domain-specific enrichment stage in the write-path pipeline.\n *\n * architecture.md §4.1 (Enricher role) / §5.5 / shift-left-event-mapping.md §5:\n * Positioned between Correlator and the sinks (OtelEmitter + SqliteSink).\n * Applies enrichment rules to each CanonicalEvent:\n * - attribute completion (artifact_id, reconcile.status)\n * - parent resolution (parentSpanId, taskId)\n * - cross-axis link addition (materializes_as_commit, contains_change)\n * - derived event emission (promotion.file)\n *\n * Design:\n * - Reuses the Correlator's in-memory short-term store for cacheGet/cacheSet\n * - fail-open: a rule that throws is logged and execution continues\n * - emitRecord queues derived CanonicalEvents (written to sinks after the\n * current event's enrichment completes; NOT re-enriched to prevent cycles)\n * - dispatch: all registered rules run for every event; each rule checks\n * its own applicability (eventType / lifecycle / field presence)\n */\nimport type { CanonicalEvent } from \"../types/canonical-event.js\";\nimport type { Correlator } from \"../correlate/correlator.js\";\nimport { currentTimeUnixNano } from \"../types/ids.js\";\nimport { lookupArtifact } from \"./rules/artifact.js\";\nimport type { EnrichApi, EnrichContext, EnrichRule, ArtifactPattern } from \"./api.js\";\n\n// ── Public types ──────────────────────────────────────────────────────────────\n\nexport interface EnricherOptions {\n /** Correlator instance — its cache is shared with the Enricher (architecture.md §8). */\n correlator: Correlator;\n /**\n * Artifact path-glob patterns for lookupArtifact.\n * Provided via config / createPipeline options; generated by Phase 4 binding.\n * Pass [] for no artifact resolution (all paths → \"unknown\").\n */\n artifactPatterns?: ArtifactPattern[];\n}\n\n/** Return value of Enricher.enrich(). */\nexport interface EnrichResult {\n /** The (possibly mutated) canonical event ready to write to sinks. */\n enriched: CanonicalEvent;\n /**\n * Derived canonical events queued by rules via api.emitRecord().\n * Caller writes these to sinks after the primary event (no re-enrichment).\n */\n derived: CanonicalEvent[];\n}\n\n// ── Enricher ──────────────────────────────────────────────────────────────────\n\nexport class Enricher {\n private readonly correlator: Correlator;\n private readonly artifactPatterns: ArtifactPattern[];\n private readonly rules: EnrichRule[];\n\n /**\n * @param options Correlator + optional artifact patterns\n * @param rules Enrichment rules to apply in order.\n * Use createDefaultRules() for the standard Phase 5 rule set.\n */\n constructor(options: EnricherOptions, rules: EnrichRule[]) {\n this.correlator = options.correlator;\n this.artifactPatterns = options.artifactPatterns ?? [];\n this.rules = rules;\n }\n\n /**\n * Enrich a single CanonicalEvent.\n *\n * Returns a shallow copy of the event (with mutated fields from rules) and\n * a list of derived events queued by api.emitRecord().\n *\n * fail-open: each rule runs in a try/catch; a throwing rule logs to stderr\n * and does NOT prevent the event from reaching the sinks.\n */\n enrich(event: CanonicalEvent): EnrichResult {\n // Work on a shallow copy so the caller's object is not mutated\n const mutableEvent: CanonicalEvent = {\n ...event,\n attributes: { ...event.attributes },\n links: [...event.links],\n };\n\n const derived: CanonicalEvent[] = [];\n const patterns = this.artifactPatterns;\n const correlator = this.correlator;\n\n const api: EnrichApi = {\n cacheGet: <T>(k: string) => correlator.cacheGet<T>(k),\n cacheSet: (k, v) => correlator.cacheSet(k, v),\n emitRecord: (e) => derived.push(e),\n addLink: (link) => mutableEvent.links.push(link),\n lookupArtifact: (path) => lookupArtifact(path, patterns),\n logDebug: (msg, data?) => {\n // In production, route to a structured logger; for now, use console.debug.\n if (data !== undefined) {\n console.debug(`[Enricher] ${msg}`, data);\n } else {\n console.debug(`[Enricher] ${msg}`);\n }\n },\n nowUnixNano: () => currentTimeUnixNano(),\n };\n\n const ctx: EnrichContext = { event: mutableEvent, api };\n\n for (const rule of this.rules) {\n try {\n rule(mutableEvent, ctx);\n } catch (err) {\n // fail-open: log and continue — recording is never stopped by a rule error\n console.error(\"[Enricher] rule threw (fail-open):\", err);\n }\n }\n\n return { enriched: mutableEvent, derived };\n }\n}\n\n// ── Default rule set (Phase 5) ────────────────────────────────────────────────\n\nimport { createArtifactRule } from \"./rules/artifact.js\";\nimport { createProcessRule } from \"./rules/process.js\";\nimport { createCrossAxisRule } from \"./rules/cross-axis.js\";\nimport { createPromotionRule } from \"./rules/promotion.js\";\n\n/**\n * Build the standard Phase 5 enrichment rule set.\n *\n * Rule ordering:\n * 1. R7 artifact.ts — resolve edit.artifact_id first (R3 disambiguation depends on it)\n * 2. R3/R4 process.ts — maintain active-task stack; resolve process.edit/tool parent\n * 3. R5 cross-axis.ts — materializes_as_commit on promotion.commit close\n * 4. R1/R2 promotion.ts — cache changes; emit promotion.file on commit close\n *\n * @param artifactPatterns Passed to createArtifactRule for R7 lookupArtifact.\n */\nexport function createDefaultRules(\n artifactPatterns: ArtifactPattern[] = [],\n): EnrichRule[] {\n return [\n createArtifactRule(artifactPatterns), // R7\n createProcessRule(), // R3/R4\n createCrossAxisRule(), // R5\n createPromotionRule(), // R1/R2\n ];\n}\n","/**\n * SqliteSink — persists CanonicalEvents to a local SQLite database.\n *\n * architecture.md §10 / §4.1 (SqliteSink role):\n * - canonical_events : append-only INSERT (immutable log)\n * - spans : open→INSERT, close→UPDATE, instant→INSERT\n * - canonical_links : expand event.links into normalised rows\n *\n * Default path: .agent-logs/observability.db\n * Pass ':memory:' for an in-process in-memory database (tests, ephemeral use).\n *\n * Uses the built-in node:sqlite (stable in Node ≥ 24, experimental in 22.5+).\n */\nimport { DatabaseSync } from \"node:sqlite\";\nimport { mkdirSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport type { CanonicalEvent } from \"../types/canonical-event.js\";\nimport { generateId } from \"../types/ids.js\";\n\n/** Default database file location (relative to cwd). */\nexport const DEFAULT_DB_PATH = \".agent-logs/observability.db\";\n\n// ── Schema DDL (mirrors schema.sql exactly) ──────────────────────────────────\n\nconst SCHEMA = `\nCREATE TABLE IF NOT EXISTS canonical_events (\n id TEXT PRIMARY KEY,\n time_unix_nano INTEGER NOT NULL,\n source TEXT NOT NULL,\n event_type TEXT NOT NULL,\n lifecycle TEXT NOT NULL,\n trace_id TEXT,\n span_id TEXT NOT NULL,\n parent_span_id TEXT,\n run_id TEXT,\n session_id TEXT,\n task_id TEXT,\n attributes TEXT NOT NULL DEFAULT '{}'\n);\n\nCREATE INDEX IF NOT EXISTS idx_ce_trace_id ON canonical_events (trace_id);\nCREATE INDEX IF NOT EXISTS idx_ce_span_id ON canonical_events (span_id);\nCREATE INDEX IF NOT EXISTS idx_ce_event_type ON canonical_events (event_type);\nCREATE INDEX IF NOT EXISTS idx_ce_session_id ON canonical_events (session_id);\nCREATE INDEX IF NOT EXISTS idx_ce_time ON canonical_events (time_unix_nano);\n\nCREATE TABLE IF NOT EXISTS spans (\n span_id TEXT PRIMARY KEY,\n trace_id TEXT,\n parent_span_id TEXT,\n event_type TEXT NOT NULL,\n start_time INTEGER,\n end_time INTEGER,\n duration_ns INTEGER,\n status TEXT NOT NULL DEFAULT 'open',\n run_id TEXT,\n session_id TEXT,\n task_id TEXT,\n attributes TEXT NOT NULL DEFAULT '{}'\n);\n\nCREATE INDEX IF NOT EXISTS idx_spans_trace_id ON spans (trace_id);\nCREATE INDEX IF NOT EXISTS idx_spans_parent ON spans (parent_span_id);\nCREATE INDEX IF NOT EXISTS idx_spans_event_type ON spans (event_type);\nCREATE INDEX IF NOT EXISTS idx_spans_session_id ON spans (session_id);\nCREATE INDEX IF NOT EXISTS idx_spans_task_id ON spans (task_id);\nCREATE INDEX IF NOT EXISTS idx_spans_status ON spans (status);\n\nCREATE TABLE IF NOT EXISTS canonical_links (\n id TEXT PRIMARY KEY,\n source_event_id TEXT NOT NULL,\n source_span_id TEXT NOT NULL,\n target_span_id TEXT NOT NULL,\n target_trace_id TEXT,\n link_type TEXT NOT NULL,\n attributes TEXT NOT NULL DEFAULT '{}'\n);\n\nCREATE INDEX IF NOT EXISTS idx_links_source ON canonical_links (source_span_id);\nCREATE INDEX IF NOT EXISTS idx_links_target ON canonical_links (target_span_id);\nCREATE INDEX IF NOT EXISTS idx_links_type ON canonical_links (link_type);\n`;\n\n// ── SqliteSink ───────────────────────────────────────────────────────────────\n\nexport class SqliteSink {\n private readonly db: DatabaseSync;\n\n constructor(dbPath: string = DEFAULT_DB_PATH) {\n if (dbPath !== \":memory:\") {\n const abs = resolve(dbPath);\n mkdirSync(dirname(abs), { recursive: true });\n this.db = new DatabaseSync(abs);\n } else {\n this.db = new DatabaseSync(\":memory:\");\n }\n this.db.exec(SCHEMA);\n }\n\n // ── Public write API ───────────────────────────────────────────────────────\n\n /**\n * Persist a single CanonicalEvent.\n * 1. Append to canonical_events (immutable)\n * 2. Upsert spans materialised view\n * 3. Insert canonical_links rows\n */\n write(event: CanonicalEvent): void {\n this.insertCanonicalEvent(event);\n this.upsertSpan(event);\n if (event.links.length > 0) {\n this.insertLinks(event);\n }\n }\n\n /** Close the database connection. */\n close(): void {\n this.db.close();\n }\n\n /**\n * Expose the underlying DatabaseSync for testing / read-path adapters.\n * Should not be used in production write paths.\n */\n getDb(): DatabaseSync {\n return this.db;\n }\n\n // ── Private helpers ────────────────────────────────────────────────────────\n\n private insertCanonicalEvent(event: CanonicalEvent): void {\n const stmt = this.db.prepare(`\n INSERT INTO canonical_events\n (id, time_unix_nano, source, event_type, lifecycle,\n trace_id, span_id, parent_span_id, run_id, session_id, task_id, attributes)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n stmt.run(\n event.id,\n event.timeUnixNano,\n event.source,\n event.eventType,\n event.lifecycle,\n event.traceId ?? null,\n event.spanId,\n event.parentSpanId ?? null,\n event.runId ?? null,\n event.sessionId ?? null,\n event.taskId ?? null,\n JSON.stringify(event.attributes),\n );\n }\n\n private upsertSpan(event: CanonicalEvent): void {\n switch (event.lifecycle) {\n case \"open\":\n this.db\n .prepare(`\n INSERT INTO spans\n (span_id, trace_id, parent_span_id, event_type, start_time,\n status, run_id, session_id, task_id, attributes)\n VALUES (?, ?, ?, ?, ?, 'open', ?, ?, ?, ?)\n `)\n .run(\n event.spanId,\n event.traceId ?? null,\n event.parentSpanId ?? null,\n event.eventType,\n event.timeUnixNano,\n event.runId ?? null,\n event.sessionId ?? null,\n event.taskId ?? null,\n JSON.stringify(event.attributes),\n );\n break;\n\n case \"close\":\n // duration_ns is computed in SQL so we avoid a round-trip read.\n this.db\n .prepare(`\n UPDATE spans\n SET end_time = ?,\n duration_ns = (? - start_time),\n status = 'closed'\n WHERE span_id = ?\n `)\n .run(event.timeUnixNano, event.timeUnixNano, event.spanId);\n break;\n\n case \"instant\":\n this.db\n .prepare(`\n INSERT OR IGNORE INTO spans\n (span_id, trace_id, parent_span_id, event_type,\n start_time, end_time, duration_ns, status,\n run_id, session_id, task_id, attributes)\n VALUES (?, ?, ?, ?, ?, ?, 0, 'instant', ?, ?, ?, ?)\n `)\n .run(\n event.spanId,\n event.traceId ?? null,\n event.parentSpanId ?? null,\n event.eventType,\n event.timeUnixNano,\n event.timeUnixNano,\n event.runId ?? null,\n event.sessionId ?? null,\n event.taskId ?? null,\n JSON.stringify(event.attributes),\n );\n break;\n\n case \"event\":\n // Span event: belongs to an existing span; no row in spans.\n break;\n }\n }\n\n private insertLinks(event: CanonicalEvent): void {\n const stmt = this.db.prepare(`\n INSERT INTO canonical_links\n (id, source_event_id, source_span_id, target_span_id, target_trace_id, link_type, attributes)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n `);\n for (const link of event.links) {\n stmt.run(\n generateId(),\n event.id,\n event.spanId,\n link.targetSpanId,\n link.targetTraceId ?? null,\n link.linkType,\n JSON.stringify(link.attributes ?? {}),\n );\n }\n }\n}\n","/**\n * OtelEmitter — converts MappedSpans to OTEL Spans and emits them to an\n * OTLP-compatible backend asynchronously (batched).\n *\n * architecture.md §11:\n * - Uses @opentelemetry/sdk-trace-base + @opentelemetry/exporter-trace-otlp-http\n * - fail-open: emit failures are caught, logged, and swallowed.\n * The SQLite write path is never blocked or thrown into.\n * - When OTEL_EXPORTER_OTLP_ENDPOINT is not set, OtelEmitter is a no-op\n * (SqliteSink continues to record regardless).\n */\nimport { BasicTracerProvider, BatchSpanProcessor } from \"@opentelemetry/sdk-trace-base\";\nimport { OTLPTraceExporter } from \"@opentelemetry/exporter-trace-otlp-http\";\nimport { trace, SpanKind, SpanStatusCode, ROOT_CONTEXT, type Tracer } from \"@opentelemetry/api\";\nimport type { MappedSpan } from \"./span-mapper.js\";\nimport type { AttrValue } from \"../types/canonical-event.js\";\n\n// ── Configuration ─────────────────────────────────────────────────────────────\n\nexport interface OtelEmitterOptions {\n /**\n * OTLP HTTP endpoint URL.\n * Defaults to process.env.OTEL_EXPORTER_OTLP_ENDPOINT.\n * If neither is set, OtelEmitter becomes a no-op.\n */\n endpoint?: string;\n /** Service name reported to the OTEL backend. Default: '@aaac/observability'. */\n serviceName?: string;\n}\n\n// ── OtelEmitter ───────────────────────────────────────────────────────────────\n\nexport class OtelEmitter {\n private readonly provider: BasicTracerProvider | null;\n private readonly tracer: Tracer | null;\n private readonly enabled: boolean;\n\n constructor(options: OtelEmitterOptions = {}) {\n const endpoint =\n options.endpoint ?? process.env[\"OTEL_EXPORTER_OTLP_ENDPOINT\"];\n\n if (!endpoint) {\n // No backend configured — fail-open: become a no-op.\n this.provider = null;\n this.tracer = null;\n this.enabled = false;\n return;\n }\n\n const serviceName = options.serviceName ?? \"@aaac/observability\";\n\n const exporter = new OTLPTraceExporter({ url: `${endpoint}/v1/traces` });\n const processor = new BatchSpanProcessor(exporter);\n\n this.provider = new BasicTracerProvider({\n resource: {\n attributes: { \"service.name\": serviceName },\n } as never,\n spanProcessors: [processor],\n });\n\n // Register as global provider so trace.getTracer() uses our provider.\n trace.setGlobalTracerProvider(this.provider);\n this.tracer = this.provider.getTracer(\"@aaac/observability\", \"0.1.0\");\n this.enabled = true;\n }\n\n /**\n * Emit a single MappedSpan to the configured OTLP backend.\n *\n * fail-open: any error is caught, logged to stderr, and swallowed.\n * This method never throws into the caller.\n */\n emit(span: MappedSpan): void {\n if (!this.enabled || this.tracer === null) return;\n\n try {\n this._emitSpan(span);\n } catch (err) {\n // fail-open: log and swallow\n console.error(\"[OtelEmitter] emit error (swallowed):\", err);\n }\n }\n\n /**\n * Emit a batch of MappedSpans.\n * Each span is emitted independently; one failure does not block others.\n */\n emitBatch(spans: MappedSpan[]): void {\n for (const span of spans) {\n this.emit(span);\n }\n }\n\n /**\n * Flush pending spans and shut down the OTEL provider.\n * Returns a promise that resolves when the flush is complete (or on error).\n */\n async shutdown(): Promise<void> {\n if (this.provider === null) return;\n try {\n await this.provider.shutdown();\n } catch (err) {\n console.error(\"[OtelEmitter] shutdown error (swallowed):\", err);\n }\n }\n\n // ── Private helpers ────────────────────────────────────────────────────────\n\n private _emitSpan(mapped: MappedSpan): void {\n if (this.tracer === null) return;\n\n // Build a parent context if parentSpanId is known.\n const parentCtx = mapped.parentSpanId\n ? this._buildParentContext(mapped.traceId, mapped.parentSpanId)\n : ROOT_CONTEXT;\n\n // Convert nanoseconds (bigint) → milliseconds (number) for the SDK.\n const startMs = Number(mapped.startTimeUnixNano / 1_000_000n);\n const endMs = Number(mapped.endTimeUnixNano / 1_000_000n);\n\n const otelSpan = this.tracer.startSpan(\n mapped.name,\n {\n kind: SpanKind.INTERNAL,\n startTime: startMs,\n attributes: convertAttributes(mapped.attributes),\n links: mapped.links.map((l) => ({\n context: {\n traceId: l.targetTraceId ?? mapped.traceId,\n spanId: l.targetSpanId,\n traceFlags: 1,\n },\n attributes: l.attributes,\n })),\n },\n parentCtx,\n );\n\n // Add mid-span events.\n for (const ev of mapped.spanEvents) {\n otelSpan.addEvent(\n ev.name,\n convertAttributes(ev.attributes),\n Number(ev.timeUnixNano / 1_000_000n),\n );\n }\n\n otelSpan.setStatus({ code: SpanStatusCode.OK });\n otelSpan.end(endMs);\n }\n\n private _buildParentContext(traceId: string, spanId: string): typeof ROOT_CONTEXT {\n const spanContext = {\n traceId,\n spanId,\n traceFlags: 1, // SAMPLED\n isRemote: false,\n };\n return trace.setSpanContext(ROOT_CONTEXT, spanContext);\n }\n}\n\n// ── Attribute conversion ───────────────────────────────────────────────────────\n\nfunction convertAttributes(\n attrs: Record<string, AttrValue>,\n): Record<string, string | number | boolean> {\n return attrs as Record<string, string | number | boolean>;\n}\n","/**\n * span-mapper — converts a group of CanonicalEvents sharing the same spanId\n * into a structure ready for OTEL Span emission.\n *\n * architecture.md §11 / §4.1 (OtelEmitter role):\n * - open → Span start_time\n * - close → Span end_time\n * - event → SpanEvent (attached to the span)\n * - instant → start_time = end_time (duration-zero Span)\n *\n * attributes are merged across open / close / event events (open first,\n * then close, then each event in time order — later keys win).\n *\n * parentSpanId → OTEL parent context.\n * CanonicalEvent.links → OTEL SpanLinks (link_type stored as a SpanLink attribute).\n */\nimport type { CanonicalEvent, AttrValue, CanonicalLink } from \"../types/canonical-event.js\";\n\n// ── Public types ──────────────────────────────────────────────────────────────\n\n/** A single SpanEvent (mid-span event attached to the logical span). */\nexport interface MappedSpanEvent {\n name: string;\n timeUnixNano: bigint;\n attributes: Record<string, AttrValue>;\n}\n\n/** A SpanLink pointing to another span, carrying link_type as an attribute. */\nexport interface MappedSpanLink {\n /** Target span identifier. */\n targetSpanId: string;\n /** Target trace identifier (may be same as the span's traceId). */\n targetTraceId?: string;\n /** Attributes including 'link_type' (the semantic relationship). */\n attributes: Record<string, string>;\n}\n\n/**\n * A logical OTEL Span derived from one or more CanonicalEvents.\n * This is an intermediate representation, not coupled to any OTEL SDK type.\n */\nexport interface MappedSpan {\n /** Span identifier. */\n spanId: string;\n /** Trace identifier. */\n traceId: string;\n /** Parent span identifier (undefined for root spans). */\n parentSpanId?: string;\n /** Span name (== eventType of the constituent events). */\n name: string;\n /** Span start time in Unix nanoseconds. */\n startTimeUnixNano: bigint;\n /** Span end time in Unix nanoseconds. */\n endTimeUnixNano: bigint;\n /** Merged attributes from all constituent events. */\n attributes: Record<string, AttrValue>;\n /** Mid-span events (from lifecycle='event' CanonicalEvents). */\n spanEvents: MappedSpanEvent[];\n /** Links to other spans. */\n links: MappedSpanLink[];\n}\n\n// ── Implementation ────────────────────────────────────────────────────────────\n\n/**\n * Map a group of CanonicalEvents (all sharing the same spanId) to a MappedSpan.\n *\n * The caller must ensure all events belong to the same spanId.\n * Events need not be pre-sorted; this function sorts by timeUnixNano internally.\n *\n * Returns undefined if the group is empty.\n */\nexport function mapEventsToSpan(events: CanonicalEvent[]): MappedSpan | undefined {\n if (events.length === 0) return undefined;\n\n // Sort by time so attribute merge order is deterministic.\n const sorted = [...events].sort((a, b) =>\n a.timeUnixNano < b.timeUnixNano ? -1 : a.timeUnixNano > b.timeUnixNano ? 1 : 0,\n );\n\n const first = sorted[0];\n const spanId = first.spanId;\n const traceId = first.traceId;\n const parentSpanId = first.parentSpanId;\n const name = first.eventType;\n\n // Determine start / end times and collect span events.\n let startTimeUnixNano: bigint | undefined;\n let endTimeUnixNano: bigint | undefined;\n const spanEvents: MappedSpanEvent[] = [];\n\n // Merged attributes: open → close → each 'event' in time order (later wins).\n let mergedAttributes: Record<string, AttrValue> = {};\n\n // Collect all links from all constituent events.\n const allLinks: CanonicalLink[] = [];\n\n for (const ev of sorted) {\n switch (ev.lifecycle) {\n case \"open\":\n startTimeUnixNano = ev.timeUnixNano;\n mergedAttributes = { ...mergedAttributes, ...ev.attributes };\n break;\n\n case \"close\":\n endTimeUnixNano = ev.timeUnixNano;\n mergedAttributes = { ...mergedAttributes, ...ev.attributes };\n break;\n\n case \"event\":\n spanEvents.push({\n name: ev.eventType,\n timeUnixNano: ev.timeUnixNano,\n attributes: ev.attributes,\n });\n mergedAttributes = { ...mergedAttributes, ...ev.attributes };\n break;\n\n case \"instant\":\n startTimeUnixNano = ev.timeUnixNano;\n endTimeUnixNano = ev.timeUnixNano;\n mergedAttributes = { ...mergedAttributes, ...ev.attributes };\n break;\n }\n\n allLinks.push(...ev.links);\n }\n\n // Fallback: if we only have 'event' type events (unusual but safe).\n if (startTimeUnixNano === undefined) {\n startTimeUnixNano = first.timeUnixNano;\n }\n if (endTimeUnixNano === undefined) {\n endTimeUnixNano = startTimeUnixNano;\n }\n\n // Convert CanonicalLinks → MappedSpanLinks.\n const links: MappedSpanLink[] = allLinks.map((l) => ({\n targetSpanId: l.targetSpanId,\n targetTraceId: l.targetTraceId,\n attributes: {\n link_type: l.linkType,\n ...(l.attributes ?? {}),\n },\n }));\n\n return {\n spanId,\n traceId,\n parentSpanId,\n name,\n startTimeUnixNano,\n endTimeUnixNano,\n attributes: mergedAttributes,\n spanEvents,\n links,\n };\n}\n\n/**\n * Group an array of CanonicalEvents by spanId, then map each group to a MappedSpan.\n * Useful for batch conversion (e.g. flushing a set of events to OTEL).\n */\nexport function mapAllEventsToSpans(events: CanonicalEvent[]): MappedSpan[] {\n const groups = new Map<string, CanonicalEvent[]>();\n for (const ev of events) {\n const group = groups.get(ev.spanId);\n if (group !== undefined) {\n group.push(ev);\n } else {\n groups.set(ev.spanId, [ev]);\n }\n }\n\n const spans: MappedSpan[] = [];\n for (const group of groups.values()) {\n const mapped = mapEventsToSpan(group);\n if (mapped !== undefined) {\n spans.push(mapped);\n }\n }\n return spans;\n}\n","/**\n * SqliteQueryAdapter — reads the SqliteSink database independently of the\n * write path.\n *\n * architecture.md §12 / §12.3:\n * - Queries the `spans` materialised table for span-level queries.\n * - Queries `canonical_events` for raw event access.\n * - Queries `canonical_links` for link traversal (source/target bidirectional).\n * - Can open the same database file as SqliteSink, or an in-memory DB for tests.\n * - Does NOT touch write-path state (no Correlator, no Normalizer, etc.).\n */\nimport { DatabaseSync } from \"node:sqlite\";\nimport { mkdirSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport type {\n StoredEvent,\n SpanRecord,\n LinkRecord,\n SpanQueryFilter,\n EventQueryFilter,\n LinkDirection,\n} from \"./models.js\";\nimport type { QueryAdapter } from \"./query-adapter.js\";\nimport type { AttrValue } from \"../types/canonical-event.js\";\nimport type { SQLInputValue } from \"node:sqlite\";\nimport { DEFAULT_DB_PATH } from \"../sink/sqlite-sink.js\";\n\n// ── SqliteQueryAdapter ────────────────────────────────────────────────────────\n\nexport class SqliteQueryAdapter implements QueryAdapter {\n private readonly db: DatabaseSync;\n private readonly ownsDb: boolean;\n\n /**\n * @param dbPathOrDb Either a file path string (opens a new connection) or\n * an existing DatabaseSync instance (shared connection,\n * e.g. from SqliteSink.getDb() in tests).\n */\n constructor(dbPathOrDb: string | DatabaseSync = DEFAULT_DB_PATH) {\n if (typeof dbPathOrDb === \"string\") {\n if (dbPathOrDb !== \":memory:\") {\n const abs = resolve(dbPathOrDb);\n mkdirSync(dirname(abs), { recursive: true });\n this.db = new DatabaseSync(abs);\n } else {\n this.db = new DatabaseSync(\":memory:\");\n }\n this.ownsDb = true;\n } else {\n this.db = dbPathOrDb;\n this.ownsDb = false;\n }\n }\n\n // ── Span-level queries ─────────────────────────────────────────────────────\n\n querySpans(filter: SpanQueryFilter): SpanRecord[] {\n const { clauses, params } = buildSpanFilter(filter);\n const where = clauses.length > 0 ? `WHERE ${clauses.join(\" AND \")}` : \"\";\n const limit = filter.limit !== undefined ? `LIMIT ${Number(filter.limit)}` : \"\";\n const sql = `SELECT * FROM spans ${where} ORDER BY start_time ASC ${limit}`;\n const stmt = this.db.prepare(sql);\n stmt.setReadBigInts(true);\n const rows = stmt.all(...(params as SQLInputValue[])) as unknown as RawSpanRow[];\n return rows.map(parseSpanRow);\n }\n\n getSpan(spanId: string): SpanRecord | undefined {\n const stmt = this.db.prepare(\"SELECT * FROM spans WHERE span_id = ?\");\n stmt.setReadBigInts(true);\n const row = stmt.get(spanId) as unknown as RawSpanRow | undefined;\n return row !== undefined ? parseSpanRow(row) : undefined;\n }\n\n getTrace(traceId: string): SpanRecord[] {\n const stmt = this.db.prepare(\n \"SELECT * FROM spans WHERE trace_id = ? ORDER BY start_time ASC\",\n );\n stmt.setReadBigInts(true);\n const rows = stmt.all(traceId) as unknown as RawSpanRow[];\n return rows.map(parseSpanRow);\n }\n\n // ── Event-level queries ────────────────────────────────────────────────────\n\n queryEvents(filter: EventQueryFilter): StoredEvent[] {\n const { clauses, params } = buildEventFilter(filter);\n const where = clauses.length > 0 ? `WHERE ${clauses.join(\" AND \")}` : \"\";\n const limit = filter.limit !== undefined ? `LIMIT ${Number(filter.limit)}` : \"\";\n const sql = `SELECT * FROM canonical_events ${where} ORDER BY time_unix_nano ASC ${limit}`;\n const stmt = this.db.prepare(sql);\n stmt.setReadBigInts(true);\n const rows = stmt.all(...(params as SQLInputValue[])) as unknown as RawEventRow[];\n return rows.map(parseEventRow);\n }\n\n getSpanEvents(spanId: string): StoredEvent[] {\n const stmt = this.db.prepare(\n \"SELECT * FROM canonical_events WHERE span_id = ? ORDER BY time_unix_nano ASC\",\n );\n stmt.setReadBigInts(true);\n const rows = stmt.all(spanId) as unknown as RawEventRow[];\n return rows.map(parseEventRow);\n }\n\n // ── Link traversal ─────────────────────────────────────────────────────────\n\n getLinks(spanId: string, direction: LinkDirection = \"forward\"): LinkRecord[] {\n const rawRows: RawLinkRow[] = [];\n\n if (direction === \"forward\" || direction === \"both\") {\n const stmt = this.db.prepare(\n \"SELECT * FROM canonical_links WHERE source_span_id = ?\",\n );\n rawRows.push(...(stmt.all(spanId) as unknown as RawLinkRow[]));\n }\n\n if (direction === \"reverse\" || direction === \"both\") {\n const stmt = this.db.prepare(\n \"SELECT * FROM canonical_links WHERE target_span_id = ?\",\n );\n rawRows.push(...(stmt.all(spanId) as unknown as RawLinkRow[]));\n }\n\n // Deduplicate by id when direction === \"both\".\n if (direction === \"both\") {\n const seen = new Set<string>();\n return rawRows\n .filter((r) => {\n if (seen.has(r.id)) return false;\n seen.add(r.id);\n return true;\n })\n .map(parseLinkRow);\n }\n\n return rawRows.map(parseLinkRow);\n }\n\n getLinksByType(linkType: string): LinkRecord[] {\n const stmt = this.db.prepare(\n \"SELECT * FROM canonical_links WHERE link_type = ?\",\n );\n const rows = stmt.all(linkType) as unknown as RawLinkRow[];\n return rows.map(parseLinkRow);\n }\n\n // ── Lifecycle ──────────────────────────────────────────────────────────────\n\n close(): void {\n if (this.ownsDb) {\n this.db.close();\n }\n // If we don't own the DB (shared from SqliteSink), do not close it.\n }\n}\n\n// ── Raw row types (as returned by node:sqlite with BigInt mode) ───────────────\n\ninterface RawSpanRow {\n span_id: string;\n trace_id: string | null;\n parent_span_id: string | null;\n event_type: string;\n start_time: bigint | null;\n end_time: bigint | null;\n duration_ns: bigint | null;\n status: string;\n run_id: string | null;\n session_id: string | null;\n task_id: string | null;\n attributes: string;\n}\n\ninterface RawEventRow {\n id: string;\n time_unix_nano: bigint;\n source: string;\n event_type: string;\n lifecycle: string;\n trace_id: string | null;\n span_id: string;\n parent_span_id: string | null;\n run_id: string | null;\n session_id: string | null;\n task_id: string | null;\n attributes: string;\n}\n\ninterface RawLinkRow {\n id: string;\n source_event_id: string;\n source_span_id: string;\n target_span_id: string;\n target_trace_id: string | null;\n link_type: string;\n attributes: string;\n}\n\n// ── Row parsers ───────────────────────────────────────────────────────────────\n\nfunction parseSpanRow(row: RawSpanRow): SpanRecord {\n return {\n spanId: row.span_id,\n traceId: row.trace_id,\n parentSpanId: row.parent_span_id,\n eventType: row.event_type,\n startTime: row.start_time,\n endTime: row.end_time,\n durationNs: row.duration_ns,\n status: row.status,\n runId: row.run_id,\n sessionId: row.session_id,\n taskId: row.task_id,\n attributes: safeParseJson(row.attributes),\n };\n}\n\nfunction parseEventRow(row: RawEventRow): StoredEvent {\n return {\n id: row.id,\n timeUnixNano: row.time_unix_nano,\n source: row.source,\n eventType: row.event_type,\n lifecycle: row.lifecycle as StoredEvent[\"lifecycle\"],\n traceId: row.trace_id ?? \"\",\n spanId: row.span_id,\n parentSpanId: row.parent_span_id ?? undefined,\n runId: row.run_id ?? undefined,\n sessionId: row.session_id ?? undefined,\n taskId: row.task_id ?? undefined,\n attributes: safeParseJson(row.attributes),\n links: [], // links are stored in canonical_links, not inline on events\n };\n}\n\nfunction parseLinkRow(row: RawLinkRow): LinkRecord {\n return {\n id: row.id,\n sourceEventId: row.source_event_id,\n sourceSpanId: row.source_span_id,\n targetSpanId: row.target_span_id,\n targetTraceId: row.target_trace_id,\n linkType: row.link_type,\n attributes: safeParseJson(row.attributes) as Record<string, string>,\n };\n}\n\nfunction safeParseJson(json: string): Record<string, AttrValue> {\n try {\n return JSON.parse(json) as Record<string, AttrValue>;\n } catch {\n return {};\n }\n}\n\n// ── Filter builders ───────────────────────────────────────────────────────────\n\nfunction buildSpanFilter(filter: SpanQueryFilter): { clauses: string[]; params: unknown[] } {\n const clauses: string[] = [];\n const params: unknown[] = [];\n\n if (filter.traceId !== undefined) {\n clauses.push(\"trace_id = ?\");\n params.push(filter.traceId);\n }\n if (filter.spanId !== undefined) {\n clauses.push(\"span_id = ?\");\n params.push(filter.spanId);\n }\n if (filter.eventType !== undefined) {\n clauses.push(\"event_type = ?\");\n params.push(filter.eventType);\n }\n if (filter.taskId !== undefined) {\n clauses.push(\"task_id = ?\");\n params.push(filter.taskId);\n }\n if (filter.fromTimeUnixNano !== undefined) {\n clauses.push(\"start_time >= ?\");\n params.push(filter.fromTimeUnixNano);\n }\n if (filter.toTimeUnixNano !== undefined) {\n clauses.push(\"start_time <= ?\");\n params.push(filter.toTimeUnixNano);\n }\n\n return { clauses, params };\n}\n\nfunction buildEventFilter(filter: EventQueryFilter): { clauses: string[]; params: unknown[] } {\n const clauses: string[] = [];\n const params: unknown[] = [];\n\n if (filter.traceId !== undefined) {\n clauses.push(\"trace_id = ?\");\n params.push(filter.traceId);\n }\n if (filter.spanId !== undefined) {\n clauses.push(\"span_id = ?\");\n params.push(filter.spanId);\n }\n if (filter.eventType !== undefined) {\n clauses.push(\"event_type = ?\");\n params.push(filter.eventType);\n }\n if (filter.taskId !== undefined) {\n clauses.push(\"task_id = ?\");\n params.push(filter.taskId);\n }\n if (filter.fromTimeUnixNano !== undefined) {\n clauses.push(\"time_unix_nano >= ?\");\n params.push(filter.fromTimeUnixNano);\n }\n if (filter.toTimeUnixNano !== undefined) {\n clauses.push(\"time_unix_nano <= ?\");\n params.push(filter.toTimeUnixNano);\n }\n\n return { clauses, params };\n}\n","/**\n * @aaac/observability — Phase 0–5 public API\n *\n * Write pipeline (Phase 5):\n * EventCollector → Normalizer → Correlator → [afterCorrelate?] → Enricher → SqliteSink\n * → OtelEmitter\n *\n * Quick start:\n * import { createPipeline } from '@aaac/observability';\n * const { collector, sink } = createPipeline();\n * collector.emit({ source: 'aaac-runtime', eventType: 'agent.session', lifecycle: 'open', attributes: {} });\n * sink.close();\n */\n\n// ── Types ────────────────────────────────────────────────────────────────────\nexport type {\n AttrValue,\n Lifecycle,\n CanonicalLink,\n RawEvent,\n CanonicalEvent,\n StoredEvent,\n} from \"./types/canonical-event.js\";\n\nexport type { ExecutionEnvelope } from \"./types/execution-envelope.js\";\nexport { generateId, currentTimeUnixNano, isoToUnixNano } from \"./types/ids.js\";\n\n// ── Collector ─────────────────────────────────────────────────────────────────\nexport {\n EventCollector,\n type EmitInput,\n type RawEventHandler,\n} from \"./collector/event-collector.js\";\n\nexport {\n DefaultExternalRegistrar,\n type ExternalEventRegistrar,\n type RawExternalEvent,\n} from \"./collector/external-registrar.js\";\n\n// ── Normalizer ────────────────────────────────────────────────────────────────\nexport { Normalizer } from \"./normalize/normalizer.js\";\nexport { validateRawEvent, NormalizationError } from \"./normalize/validator.js\";\n\n// ── Correlator ────────────────────────────────────────────────────────────────\nexport { Correlator } from \"./correlate/correlator.js\";\nexport { keys as correlateKeys } from \"./correlate/keys.js\";\n\n// ── Enricher (Phase 5) ────────────────────────────────────────────────────────\nexport type {\n ArtifactPattern,\n EnrichApi,\n EnrichContext,\n EnrichRule,\n} from \"./enrich/api.js\";\nexport {\n Enricher,\n createDefaultRules,\n type EnricherOptions,\n type EnrichResult,\n} from \"./enrich/enricher.js\";\nexport {\n createArtifactRule,\n lookupArtifact,\n globToRegex,\n createProcessRule,\n activeTasksKey,\n allTaskSpansKey,\n taskFilesKey,\n recordTaskArtifact,\n type ActiveTask,\n createCrossAxisRule,\n createPromotionRule,\n} from \"./enrich/index.js\";\n\n// ── Sink ──────────────────────────────────────────────────────────────────────\nexport { SqliteSink, DEFAULT_DB_PATH } from \"./sink/sqlite-sink.js\";\n\n// ── OtelEmitter (Phase 2) ──────────────────────────────────────────────────────\nexport { OtelEmitter } from \"./otel/otel-emitter.js\";\nexport type { OtelEmitterOptions } from \"./otel/otel-emitter.js\";\nexport { mapEventsToSpan, mapAllEventsToSpans } from \"./otel/span-mapper.js\";\nexport type { MappedSpan, MappedSpanEvent, MappedSpanLink } from \"./otel/span-mapper.js\";\n\n// ── Query (Phase 3) ───────────────────────────────────────────────────────────\nexport { SqliteQueryAdapter } from \"./query/sqlite-adapter.js\";\nexport type {\n SpanRecord,\n LinkRecord,\n SpanQueryFilter,\n EventQueryFilter,\n LinkDirection,\n} from \"./query/models.js\";\nexport type { QueryAdapter } from \"./query/query-adapter.js\";\n\n// ── Pipeline factory ──────────────────────────────────────────────────────────\n\nimport { EventCollector } from \"./collector/event-collector.js\";\nimport { Normalizer } from \"./normalize/normalizer.js\";\nimport { Correlator } from \"./correlate/correlator.js\";\nimport { SqliteSink, DEFAULT_DB_PATH } from \"./sink/sqlite-sink.js\";\nimport { OtelEmitter } from \"./otel/otel-emitter.js\";\nimport { Enricher, createDefaultRules } from \"./enrich/enricher.js\";\nimport type { CanonicalEvent } from \"./types/canonical-event.js\";\nimport type { OtelEmitterOptions } from \"./otel/otel-emitter.js\";\nimport type { ArtifactPattern, EnrichRule } from \"./enrich/api.js\";\n\nexport interface PipelineOptions {\n /** SQLite database path. Defaults to DEFAULT_DB_PATH. Pass ':memory:' for tests. */\n dbPath?: string;\n /**\n * Optional post-correlate hook — runs before the Enricher.\n * Kept for backward compatibility; prefer using enrichRules for Phase 5+.\n */\n afterCorrelate?: (event: CanonicalEvent) => CanonicalEvent;\n /**\n * Optional OTEL emitter options.\n * If provided (and endpoint is set), events are also emitted to the OTEL backend.\n * fail-open: OTEL failures never block SQLite recording.\n */\n otel?: OtelEmitterOptions;\n /**\n * Artifact path-glob patterns for the Enricher's lookupArtifact (R7).\n * Provided from artifact-contracts declarations (generated by Phase 4 binding).\n * Defaults to [] (all file paths resolve to \"unknown\").\n */\n artifactPatterns?: ArtifactPattern[];\n /**\n * Custom enrichment rule list.\n * Defaults to createDefaultRules(artifactPatterns) — the standard Phase 5 set.\n * Pass [] to disable all enrichment.\n */\n enrichRules?: EnrichRule[];\n}\n\nexport interface Pipeline {\n collector: EventCollector;\n correlator: Correlator;\n sink: SqliteSink;\n otelEmitter: OtelEmitter;\n /** Phase 5 Enricher (Correlator → Enricher → OtelEmitter + SqliteSink). */\n enricher: Enricher;\n}\n\n/**\n * Create a complete write pipeline wired together:\n *\n * EventCollector → Normalizer → Correlator → [afterCorrelate?]\n * → Enricher → SqliteSink\n * → OtelEmitter (if configured, fail-open)\n *\n * Derived events from Enricher.emitRecord() are written to both sinks after\n * the primary event (depth-1; derived events are NOT re-enriched).\n */\nexport function createPipeline(options: PipelineOptions = {}): Pipeline {\n const {\n dbPath = DEFAULT_DB_PATH,\n afterCorrelate,\n otel,\n artifactPatterns = [],\n enrichRules,\n } = options;\n\n const sink = new SqliteSink(dbPath);\n const correlator = new Correlator();\n const normalizer = new Normalizer();\n const otelEmitter = new OtelEmitter(otel ?? {});\n\n const rules = enrichRules ?? createDefaultRules(artifactPatterns);\n const enricher = new Enricher({ correlator, artifactPatterns }, rules);\n\n /** Write a single CanonicalEvent to both sinks (SQLite always first, OTEL fail-open). */\n function sinkEvent(event: CanonicalEvent): void {\n sink.write(event);\n otelEmitter.emit({\n spanId: event.spanId,\n traceId: event.traceId,\n parentSpanId: event.parentSpanId,\n name: event.eventType,\n startTimeUnixNano: event.timeUnixNano,\n endTimeUnixNano: event.timeUnixNano,\n attributes: event.attributes,\n spanEvents: [],\n links: event.links.map((l) => ({\n targetSpanId: l.targetSpanId,\n targetTraceId: l.targetTraceId,\n attributes: { link_type: l.linkType, ...(l.attributes ?? {}) },\n })),\n });\n }\n\n const collector = new EventCollector((raw) => {\n const canonical = normalizer.normalize(raw);\n let correlated = correlator.correlate(canonical);\n\n // Optional post-correlate hook (backward compat; runs before Enricher)\n if (afterCorrelate) {\n correlated = afterCorrelate(correlated);\n }\n\n // Enricher: domain-specific enrichment (Phase 5)\n const { enriched, derived } = enricher.enrich(correlated);\n\n // Write primary event to both sinks\n sinkEvent(enriched);\n\n // Write derived events (from emitRecord) to both sinks\n // Derived events are NOT re-enriched (depth-1 limit prevents cycles)\n for (const d of derived) {\n sinkEvent(d);\n }\n });\n\n return { collector, correlator, sink, otelEmitter, enricher };\n}\n"],"mappings":"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","names":["DatabaseSync","mkdirSync","dirname","resolve","resolve","mkdirSync","dirname","DatabaseSync"]}