@graphorin/server 0.5.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +9 -9
  3. package/dist/app.d.ts +5 -5
  4. package/dist/app.d.ts.map +1 -1
  5. package/dist/app.js +11 -10
  6. package/dist/app.js.map +1 -1
  7. package/dist/commentary/audit-bridge.d.ts +2 -2
  8. package/dist/commentary/audit-bridge.js +3 -3
  9. package/dist/commentary/audit-bridge.js.map +1 -1
  10. package/dist/commentary/built-in-patterns.js +1 -1
  11. package/dist/commentary/built-in-patterns.js.map +1 -1
  12. package/dist/commentary/sanitizer.js +2 -2
  13. package/dist/commentary/sanitizer.js.map +1 -1
  14. package/dist/commentary/types.d.ts +5 -5
  15. package/dist/consolidator/daemon.d.ts +1 -1
  16. package/dist/consolidator/daemon.js.map +1 -1
  17. package/dist/health/checks.js.map +1 -1
  18. package/dist/health/routes.d.ts +2 -2
  19. package/dist/health/routes.js.map +1 -1
  20. package/dist/index.d.ts +7 -8
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +9 -8
  23. package/dist/index.js.map +1 -1
  24. package/dist/internal/ids.js +1 -1
  25. package/dist/internal/ids.js.map +1 -1
  26. package/dist/lifecycle/pre-bind.js.map +1 -1
  27. package/dist/metrics/catalog.js +1 -1
  28. package/dist/metrics/catalog.js.map +1 -1
  29. package/dist/metrics/registry.d.ts +2 -2
  30. package/dist/metrics/registry.js +1 -1
  31. package/dist/metrics/registry.js.map +1 -1
  32. package/dist/middleware/auth.d.ts +1 -1
  33. package/dist/middleware/auth.js +1 -1
  34. package/dist/middleware/auth.js.map +1 -1
  35. package/dist/middleware/cors.js.map +1 -1
  36. package/dist/middleware/csrf.js.map +1 -1
  37. package/dist/middleware/idempotency.d.ts +1 -1
  38. package/dist/middleware/idempotency.js +36 -23
  39. package/dist/middleware/idempotency.js.map +1 -1
  40. package/dist/middleware/rate-limit.js.map +1 -1
  41. package/dist/middleware/request-state.js.map +1 -1
  42. package/dist/middleware/scope.js.map +1 -1
  43. package/dist/package.js +6 -0
  44. package/dist/package.js.map +1 -0
  45. package/dist/registry/index.d.ts +2 -2
  46. package/dist/registry/index.js +1 -1
  47. package/dist/registry/index.js.map +1 -1
  48. package/dist/replay/routes.js +1 -1
  49. package/dist/replay/routes.js.map +1 -1
  50. package/dist/routes/agents.js +1 -1
  51. package/dist/routes/agents.js.map +1 -1
  52. package/dist/routes/audit.d.ts +1 -1
  53. package/dist/routes/audit.js.map +1 -1
  54. package/dist/routes/auth.js.map +1 -1
  55. package/dist/routes/health.js +1 -1
  56. package/dist/routes/health.js.map +1 -1
  57. package/dist/routes/sessions.js.map +1 -1
  58. package/dist/routes/tokens.js.map +1 -1
  59. package/dist/routes/workflows.js +42 -8
  60. package/dist/routes/workflows.js.map +1 -1
  61. package/dist/runtime/run-state.d.ts +3 -3
  62. package/dist/runtime/run-state.js +3 -3
  63. package/dist/runtime/run-state.js.map +1 -1
  64. package/dist/sse/events.js +1 -1
  65. package/dist/sse/events.js.map +1 -1
  66. package/dist/triggers/daemon.js.map +1 -1
  67. package/dist/triggers/routes.js.map +1 -1
  68. package/dist/ws/dispatcher.js.map +1 -1
  69. package/dist/ws/subjects.d.ts +1 -1
  70. package/dist/ws/subjects.d.ts.map +1 -1
  71. package/dist/ws/subjects.js +3 -3
  72. package/dist/ws/subjects.js.map +1 -1
  73. package/dist/ws/ticket.d.ts +1 -1
  74. package/dist/ws/ticket.js +1 -1
  75. package/dist/ws/ticket.js.map +1 -1
  76. package/dist/ws/upgrade.d.ts.map +1 -1
  77. package/dist/ws/upgrade.js +7 -5
  78. package/dist/ws/upgrade.js.map +1 -1
  79. package/package.json +16 -16
@@ -1 +1 @@
1
- {"version":3,"file":"audit-bridge.js","names":["tail: Promise<unknown>","target: DeliveryCommentarySink | undefined"],"sources":["../../src/commentary/audit-bridge.ts"],"sourcesContent":["/**\n * IP-21: bridge the delivery-commentary sanitizer's audit decisions into the\n * tamper-evident audit log. `createWsDispatcher` ran the sanitizer without a\n * `sink`, so every documented sanitization decision what was wrapped or\n * stripped on the wire, with before/after digests and the matched-pattern\n * bucket was silently dropped instead of landing in the audit chain.\n *\n * The WS dispatcher is constructed before the audit DB is unsealed (the DB\n * opens during `start()`), so the server hands the dispatcher a\n * {@link LateBoundCommentarySink} and installs the real audit-writing target\n * via {@link bridgeCommentaryToAudit} once the DB exists. Mirrors the\n * `bridgeAuthToAudit` / `bridgeSecretsToAudit` pattern in `@graphorin/security`.\n *\n * @packageDocumentation\n */\n\nimport { type AuditDb, type AuditEntryInput, appendAudit } from '@graphorin/security/audit';\n\nimport type { DeliveryCommentaryDecision, DeliveryCommentarySink } from './types.js';\n\n/**\n * Audit action recorded for a delivery-commentary sanitization decision.\n *\n * @stable\n */\nexport const COMMENTARY_AUDIT_ACTION = 'delivery:commentary:sanitized';\n\n/**\n * Translate a sanitizer decision into an audit entry. The digests + matched\n * pattern bucket land in `metadata`; raw payloads never do (the sanitizer only\n * ever exposes SHA-256s of the before/after bodies).\n */\nexport function commentaryDecisionToAuditInput(\n decision: DeliveryCommentaryDecision,\n): AuditEntryInput {\n return {\n actor: { kind: 'system', id: 'graphorin/server' },\n action: COMMENTARY_AUDIT_ACTION,\n target: `${decision.transport}:${decision.eventType}`,\n decision: 'success',\n metadata: {\n policy: decision.policy,\n applied: decision.applied,\n boundary: decision.boundary,\n reasons: decision.reasons,\n ...(decision.matchedPattern !== undefined ? { matchedPattern: decision.matchedPattern } : {}),\n sha256OfBefore: decision.sha256OfBefore,\n sha256OfAfter: decision.sha256OfAfter,\n },\n };\n}\n\n/**\n * A commentary sink that also exposes a `drain()` so callers (and tests) can\n * await any in-flight audit writes.\n *\n * @stable\n */\nexport interface CommentaryAuditSink extends DeliveryCommentarySink {\n /** Resolve once every queued audit write has settled. */\n readonly drain: () => Promise<void>;\n}\n\n/**\n * Build a commentary sink that appends each sanitization decision to the audit\n * log. Writes serialise through `appendAudit` so concurrent decisions never\n * race on `seq`; a failed write is isolated from the wire `onWriteError`\n * (default: a console warning) runs instead of throwing.\n *\n * @stable\n */\nexport function bridgeCommentaryToAudit(\n db: AuditDb,\n onWriteError: (\n decision: DeliveryCommentaryDecision,\n error: unknown,\n ) => void = defaultOnWriteError,\n): CommentaryAuditSink {\n let tail: Promise<unknown> = Promise.resolve();\n return {\n onDecision(decision) {\n const input = commentaryDecisionToAuditInput(decision);\n tail = tail\n .then(() => appendAudit(db, input))\n .catch((error) => onWriteError(decision, error));\n },\n drain: async () => {\n await tail;\n },\n };\n}\n\nfunction defaultOnWriteError(_decision: DeliveryCommentaryDecision, error: unknown): void {\n console.warn(\n `[graphorin/server] WARN: failed to write a delivery-commentary audit entry: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n}\n\n/**\n * A {@link DeliveryCommentarySink} whose real target is installed later. The WS\n * dispatcher is created before the audit DB opens (IP-21); the server hands it\n * this forwarding sink and calls {@link LateBoundCommentarySink.bind} once the\n * audit-writing sink exists. Decisions emitted before binding are dropped the\n * dispatcher only sanitizes once it is live (after `start()`, by which point\n * the audit DB, if configured, has opened and bound).\n *\n * @stable\n */\nexport interface LateBoundCommentarySink extends DeliveryCommentarySink {\n /** Install the real sink. Replaces any previously-bound target. */\n bind(target: DeliveryCommentarySink): void;\n}\n\n/**\n * @stable\n */\nexport function createLateBoundCommentarySink(): LateBoundCommentarySink {\n let target: DeliveryCommentarySink | undefined;\n return {\n bind(next) {\n target = next;\n },\n onDecision(decision) {\n // Contract: a sink never throws into the wire. The sanitizer also guards,\n // but be defensive in case a bound target misbehaves.\n try {\n target?.onDecision(decision);\n } catch {\n // swallow\n }\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAyBA,MAAa,0BAA0B;;;;;;AAOvC,SAAgB,+BACd,UACiB;AACjB,QAAO;EACL,OAAO;GAAE,MAAM;GAAU,IAAI;GAAoB;EACjD,QAAQ;EACR,QAAQ,GAAG,SAAS,UAAU,GAAG,SAAS;EAC1C,UAAU;EACV,UAAU;GACR,QAAQ,SAAS;GACjB,SAAS,SAAS;GAClB,UAAU,SAAS;GACnB,SAAS,SAAS;GAClB,GAAI,SAAS,mBAAmB,SAAY,EAAE,gBAAgB,SAAS,gBAAgB,GAAG,EAAE;GAC5F,gBAAgB,SAAS;GACzB,eAAe,SAAS;GACzB;EACF;;;;;;;;;;AAsBH,SAAgB,wBACd,IACA,eAGY,qBACS;CACrB,IAAIA,OAAyB,QAAQ,SAAS;AAC9C,QAAO;EACL,WAAW,UAAU;GACnB,MAAM,QAAQ,+BAA+B,SAAS;AACtD,UAAO,KACJ,WAAW,YAAY,IAAI,MAAM,CAAC,CAClC,OAAO,UAAU,aAAa,UAAU,MAAM,CAAC;;EAEpD,OAAO,YAAY;AACjB,SAAM;;EAET;;AAGH,SAAS,oBAAoB,WAAuC,OAAsB;AACxF,SAAQ,KACN,+EACE,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,GAEzD;;;;;AAqBH,SAAgB,gCAAyD;CACvE,IAAIC;AACJ,QAAO;EACL,KAAK,MAAM;AACT,YAAS;;EAEX,WAAW,UAAU;AAGnB,OAAI;AACF,YAAQ,WAAW,SAAS;WACtB;;EAIX"}
1
+ {"version":3,"file":"audit-bridge.js","names":["tail: Promise<unknown>","target: DeliveryCommentarySink | undefined"],"sources":["../../src/commentary/audit-bridge.ts"],"sourcesContent":["/**\n * IP-21: bridge the delivery-commentary sanitizer's audit decisions into the\n * tamper-evident audit log. `createWsDispatcher` ran the sanitizer without a\n * `sink`, so every documented sanitization decision - what was wrapped or\n * stripped on the wire, with before/after digests and the matched-pattern\n * bucket - was silently dropped instead of landing in the audit chain.\n *\n * The WS dispatcher is constructed before the audit DB is unsealed (the DB\n * opens during `start()`), so the server hands the dispatcher a\n * {@link LateBoundCommentarySink} and installs the real audit-writing target\n * via {@link bridgeCommentaryToAudit} once the DB exists. Mirrors the\n * `bridgeAuthToAudit` / `bridgeSecretsToAudit` pattern in `@graphorin/security`.\n *\n * @packageDocumentation\n */\n\nimport { type AuditDb, type AuditEntryInput, appendAudit } from '@graphorin/security/audit';\n\nimport type { DeliveryCommentaryDecision, DeliveryCommentarySink } from './types.js';\n\n/**\n * Audit action recorded for a delivery-commentary sanitization decision.\n *\n * @stable\n */\nexport const COMMENTARY_AUDIT_ACTION = 'delivery:commentary:sanitized';\n\n/**\n * Translate a sanitizer decision into an audit entry. The digests + matched\n * pattern bucket land in `metadata`; raw payloads never do (the sanitizer only\n * ever exposes SHA-256s of the before/after bodies).\n */\nexport function commentaryDecisionToAuditInput(\n decision: DeliveryCommentaryDecision,\n): AuditEntryInput {\n return {\n actor: { kind: 'system', id: 'graphorin/server' },\n action: COMMENTARY_AUDIT_ACTION,\n target: `${decision.transport}:${decision.eventType}`,\n decision: 'success',\n metadata: {\n policy: decision.policy,\n applied: decision.applied,\n boundary: decision.boundary,\n reasons: decision.reasons,\n ...(decision.matchedPattern !== undefined ? { matchedPattern: decision.matchedPattern } : {}),\n sha256OfBefore: decision.sha256OfBefore,\n sha256OfAfter: decision.sha256OfAfter,\n },\n };\n}\n\n/**\n * A commentary sink that also exposes a `drain()` so callers (and tests) can\n * await any in-flight audit writes.\n *\n * @stable\n */\nexport interface CommentaryAuditSink extends DeliveryCommentarySink {\n /** Resolve once every queued audit write has settled. */\n readonly drain: () => Promise<void>;\n}\n\n/**\n * Build a commentary sink that appends each sanitization decision to the audit\n * log. Writes serialise through `appendAudit` so concurrent decisions never\n * race on `seq`; a failed write is isolated from the wire - `onWriteError`\n * (default: a console warning) runs instead of throwing.\n *\n * @stable\n */\nexport function bridgeCommentaryToAudit(\n db: AuditDb,\n onWriteError: (\n decision: DeliveryCommentaryDecision,\n error: unknown,\n ) => void = defaultOnWriteError,\n): CommentaryAuditSink {\n let tail: Promise<unknown> = Promise.resolve();\n return {\n onDecision(decision) {\n const input = commentaryDecisionToAuditInput(decision);\n tail = tail\n .then(() => appendAudit(db, input))\n .catch((error) => onWriteError(decision, error));\n },\n drain: async () => {\n await tail;\n },\n };\n}\n\nfunction defaultOnWriteError(_decision: DeliveryCommentaryDecision, error: unknown): void {\n console.warn(\n `[graphorin/server] WARN: failed to write a delivery-commentary audit entry: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n}\n\n/**\n * A {@link DeliveryCommentarySink} whose real target is installed later. The WS\n * dispatcher is created before the audit DB opens (IP-21); the server hands it\n * this forwarding sink and calls {@link LateBoundCommentarySink.bind} once the\n * audit-writing sink exists. Decisions emitted before binding are dropped - the\n * dispatcher only sanitizes once it is live (after `start()`, by which point\n * the audit DB, if configured, has opened and bound).\n *\n * @stable\n */\nexport interface LateBoundCommentarySink extends DeliveryCommentarySink {\n /** Install the real sink. Replaces any previously-bound target. */\n bind(target: DeliveryCommentarySink): void;\n}\n\n/**\n * @stable\n */\nexport function createLateBoundCommentarySink(): LateBoundCommentarySink {\n let target: DeliveryCommentarySink | undefined;\n return {\n bind(next) {\n target = next;\n },\n onDecision(decision) {\n // Contract: a sink never throws into the wire. The sanitizer also guards,\n // but be defensive in case a bound target misbehaves.\n try {\n target?.onDecision(decision);\n } catch {\n // swallow\n }\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAyBA,MAAa,0BAA0B;;;;;;AAOvC,SAAgB,+BACd,UACiB;AACjB,QAAO;EACL,OAAO;GAAE,MAAM;GAAU,IAAI;GAAoB;EACjD,QAAQ;EACR,QAAQ,GAAG,SAAS,UAAU,GAAG,SAAS;EAC1C,UAAU;EACV,UAAU;GACR,QAAQ,SAAS;GACjB,SAAS,SAAS;GAClB,UAAU,SAAS;GACnB,SAAS,SAAS;GAClB,GAAI,SAAS,mBAAmB,SAAY,EAAE,gBAAgB,SAAS,gBAAgB,GAAG,EAAE;GAC5F,gBAAgB,SAAS;GACzB,eAAe,SAAS;GACzB;EACF;;;;;;;;;;AAsBH,SAAgB,wBACd,IACA,eAGY,qBACS;CACrB,IAAIA,OAAyB,QAAQ,SAAS;AAC9C,QAAO;EACL,WAAW,UAAU;GACnB,MAAM,QAAQ,+BAA+B,SAAS;AACtD,UAAO,KACJ,WAAW,YAAY,IAAI,MAAM,CAAC,CAClC,OAAO,UAAU,aAAa,UAAU,MAAM,CAAC;;EAEpD,OAAO,YAAY;AACjB,SAAM;;EAET;;AAGH,SAAS,oBAAoB,WAAuC,OAAsB;AACxF,SAAQ,KACN,+EACE,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,GAEzD;;;;;AAqBH,SAAgB,gCAAyD;CACvE,IAAIC;AACJ,QAAO;EACL,KAAK,MAAM;AACT,YAAS;;EAEX,WAAW,UAAU;AAGnB,OAAI;AACF,YAAQ,WAAW,SAAS;WACtB;;EAIX"}
@@ -11,7 +11,7 @@ const DEFAULT_DELIVERY_COMMENTARY_PATTERNS = Object.freeze([
11
11
  Object.freeze({
12
12
  reason: "tool.call.start-payload-signature",
13
13
  regex: /\{\s*"type"\s*:\s*"tool\.call\.start"[\s\S]*?"toolName"\s*:\s*"[^"]+"[\s\S]*?\}/g,
14
- description: "JSON-encoded `tool.call.start` event with a `toolName` field leaks the internal tool dispatch."
14
+ description: "JSON-encoded `tool.call.start` event with a `toolName` field - leaks the internal tool dispatch."
15
15
  }),
16
16
  Object.freeze({
17
17
  reason: "tool.call.delta-payload-signature",
@@ -1 +1 @@
1
- {"version":3,"file":"built-in-patterns.js","names":["DEFAULT_DELIVERY_COMMENTARY_PATTERNS: ReadonlyArray<DeliveryCommentaryPattern>","DEFAULT_APPLY_TO_EVENTS: ReadonlyArray<string>"],"sources":["../../src/commentary/built-in-patterns.ts"],"sourcesContent":["/**\n * Default pattern catalogue for the delivery-layer commentary-phase\n * trace sanitization applied by the WebSocket dispatcher (`@graphorin/server/ws`)\n * and the SSE event-emission boundary (`@graphorin/server/sse`).\n *\n * The catalogue is structurally identical to the session-output\n * sanitization catalogue exposed by `@graphorin/sessions/commentary`;\n * they live in two packages so the server's delivery layer does not\n * pull a hard dependency on the session module while still providing\n * defense-in-depth across the storage-write boundary (Phase 11) and\n * the wire-emission boundary (this module). Deployments that want a\n * single source of truth across both layers can pass the sessions\n * catalogue through {@link import('./types.js').DeliveryCommentaryConfig}.patterns.\n *\n * @packageDocumentation\n */\n\nimport type { DeliveryCommentaryPattern } from './types.js';\n\n/**\n * The framework-shipped catalogue. Snapshot bytes-equal across the\n * `ws` / `sse` / `rest` transports; idempotent on a single payload\n * (the wrap envelope itself is not matched by any pattern, so a\n * second pass over a previously-sanitized payload is a no-op).\n *\n * @stable\n */\nexport const DEFAULT_DELIVERY_COMMENTARY_PATTERNS: ReadonlyArray<DeliveryCommentaryPattern> =\n Object.freeze([\n Object.freeze({\n reason: 'tool.call.start-payload-signature',\n regex: /\\{\\s*\"type\"\\s*:\\s*\"tool\\.call\\.start\"[\\s\\S]*?\"toolName\"\\s*:\\s*\"[^\"]+\"[\\s\\S]*?\\}/g,\n description:\n 'JSON-encoded `tool.call.start` event with a `toolName` field leaks the internal tool dispatch.',\n }),\n Object.freeze({\n reason: 'tool.call.delta-payload-signature',\n regex: /\\{\\s*\"type\"\\s*:\\s*\"tool\\.call\\.delta\"[\\s\\S]*?\"argsDelta\"[\\s\\S]*?\\}/g,\n description: 'JSON-encoded `tool.call.delta` event with streaming `argsDelta`.',\n }),\n Object.freeze({\n reason: 'tool.call.end-payload-signature',\n regex: /\\{\\s*\"type\"\\s*:\\s*\"tool\\.call\\.end\"[\\s\\S]*?\"finalArgs\"[\\s\\S]*?\\}/g,\n description: 'JSON-encoded `tool.call.end` event carrying the final args payload.',\n }),\n Object.freeze({\n reason: 'tool.execute.end-payload-signature',\n regex: /\\{\\s*\"type\"\\s*:\\s*\"tool\\.execute\\.end\"[\\s\\S]*?\"result\"\\s*:\\s*[\\s\\S]*?\\}/g,\n description: 'JSON-encoded `tool.execute.end` event with the raw `result` payload.',\n }),\n Object.freeze({\n reason: 'agent.fanout-event-signature',\n regex: /\\{\\s*\"type\"\\s*:\\s*\"agent\\.fanout\\.(?:spawned|merged)\"[\\s\\S]*?\\}/g,\n description: 'JSON-encoded `agent.fanout.spawned` / `agent.fanout.merged` event.',\n }),\n Object.freeze({\n reason: 'context.compacted-event-signature',\n regex: /\\{\\s*\"type\"\\s*:\\s*\"context\\.compacted\"[\\s\\S]*?\"originalTokens\"[\\s\\S]*?\\}/g,\n description: 'JSON-encoded `context.compacted` event with token deltas.',\n }),\n Object.freeze({\n reason: 'agent.model.fellback-event-signature',\n regex: /\\{\\s*\"type\"\\s*:\\s*\"agent\\.model\\.fellback\"[\\s\\S]*?\"fromModel\"[\\s\\S]*?\\}/g,\n description: 'JSON-encoded `agent.model.fellback` event with fallback chain detail.',\n }),\n ]);\n\n/**\n * Default whitelist of `event.type` strings the dispatcher walks\n * through the sanitizer. Extension is opt-in via\n * {@link import('./types.js').DeliveryCommentaryConfig}.applyToEvents.\n *\n * @stable\n */\nexport const DEFAULT_APPLY_TO_EVENTS: ReadonlyArray<string> = Object.freeze([\n 'tool.execute.end',\n 'tool.execute.error',\n 'text.delta',\n]);\n"],"mappings":";;;;;;;;;AA2BA,MAAaA,uCACX,OAAO,OAAO;CACZ,OAAO,OAAO;EACZ,QAAQ;EACR,OAAO;EACP,aACE;EACH,CAAC;CACF,OAAO,OAAO;EACZ,QAAQ;EACR,OAAO;EACP,aAAa;EACd,CAAC;CACF,OAAO,OAAO;EACZ,QAAQ;EACR,OAAO;EACP,aAAa;EACd,CAAC;CACF,OAAO,OAAO;EACZ,QAAQ;EACR,OAAO;EACP,aAAa;EACd,CAAC;CACF,OAAO,OAAO;EACZ,QAAQ;EACR,OAAO;EACP,aAAa;EACd,CAAC;CACF,OAAO,OAAO;EACZ,QAAQ;EACR,OAAO;EACP,aAAa;EACd,CAAC;CACF,OAAO,OAAO;EACZ,QAAQ;EACR,OAAO;EACP,aAAa;EACd,CAAC;CACH,CAAC;;;;;;;;AASJ,MAAaC,0BAAiD,OAAO,OAAO;CAC1E;CACA;CACA;CACD,CAAC"}
1
+ {"version":3,"file":"built-in-patterns.js","names":["DEFAULT_DELIVERY_COMMENTARY_PATTERNS: ReadonlyArray<DeliveryCommentaryPattern>","DEFAULT_APPLY_TO_EVENTS: ReadonlyArray<string>"],"sources":["../../src/commentary/built-in-patterns.ts"],"sourcesContent":["/**\n * Default pattern catalogue for the delivery-layer commentary-phase\n * trace sanitization applied by the WebSocket dispatcher (`@graphorin/server/ws`)\n * and the SSE event-emission boundary (`@graphorin/server/sse`).\n *\n * The catalogue is structurally identical to the session-output\n * sanitization catalogue exposed by `@graphorin/sessions/commentary`;\n * they live in two packages so the server's delivery layer does not\n * pull a hard dependency on the session module while still providing\n * defense-in-depth across the storage-write boundary (Phase 11) and\n * the wire-emission boundary (this module). Deployments that want a\n * single source of truth across both layers can pass the sessions\n * catalogue through {@link import('./types.js').DeliveryCommentaryConfig}.patterns.\n *\n * @packageDocumentation\n */\n\nimport type { DeliveryCommentaryPattern } from './types.js';\n\n/**\n * The framework-shipped catalogue. Snapshot bytes-equal across the\n * `ws` / `sse` / `rest` transports; idempotent on a single payload\n * (the wrap envelope itself is not matched by any pattern, so a\n * second pass over a previously-sanitized payload is a no-op).\n *\n * @stable\n */\nexport const DEFAULT_DELIVERY_COMMENTARY_PATTERNS: ReadonlyArray<DeliveryCommentaryPattern> =\n Object.freeze([\n Object.freeze({\n reason: 'tool.call.start-payload-signature',\n regex: /\\{\\s*\"type\"\\s*:\\s*\"tool\\.call\\.start\"[\\s\\S]*?\"toolName\"\\s*:\\s*\"[^\"]+\"[\\s\\S]*?\\}/g,\n description:\n 'JSON-encoded `tool.call.start` event with a `toolName` field - leaks the internal tool dispatch.',\n }),\n Object.freeze({\n reason: 'tool.call.delta-payload-signature',\n regex: /\\{\\s*\"type\"\\s*:\\s*\"tool\\.call\\.delta\"[\\s\\S]*?\"argsDelta\"[\\s\\S]*?\\}/g,\n description: 'JSON-encoded `tool.call.delta` event with streaming `argsDelta`.',\n }),\n Object.freeze({\n reason: 'tool.call.end-payload-signature',\n regex: /\\{\\s*\"type\"\\s*:\\s*\"tool\\.call\\.end\"[\\s\\S]*?\"finalArgs\"[\\s\\S]*?\\}/g,\n description: 'JSON-encoded `tool.call.end` event carrying the final args payload.',\n }),\n Object.freeze({\n reason: 'tool.execute.end-payload-signature',\n regex: /\\{\\s*\"type\"\\s*:\\s*\"tool\\.execute\\.end\"[\\s\\S]*?\"result\"\\s*:\\s*[\\s\\S]*?\\}/g,\n description: 'JSON-encoded `tool.execute.end` event with the raw `result` payload.',\n }),\n Object.freeze({\n reason: 'agent.fanout-event-signature',\n regex: /\\{\\s*\"type\"\\s*:\\s*\"agent\\.fanout\\.(?:spawned|merged)\"[\\s\\S]*?\\}/g,\n description: 'JSON-encoded `agent.fanout.spawned` / `agent.fanout.merged` event.',\n }),\n Object.freeze({\n reason: 'context.compacted-event-signature',\n regex: /\\{\\s*\"type\"\\s*:\\s*\"context\\.compacted\"[\\s\\S]*?\"originalTokens\"[\\s\\S]*?\\}/g,\n description: 'JSON-encoded `context.compacted` event with token deltas.',\n }),\n Object.freeze({\n reason: 'agent.model.fellback-event-signature',\n regex: /\\{\\s*\"type\"\\s*:\\s*\"agent\\.model\\.fellback\"[\\s\\S]*?\"fromModel\"[\\s\\S]*?\\}/g,\n description: 'JSON-encoded `agent.model.fellback` event with fallback chain detail.',\n }),\n ]);\n\n/**\n * Default whitelist of `event.type` strings the dispatcher walks\n * through the sanitizer. Extension is opt-in via\n * {@link import('./types.js').DeliveryCommentaryConfig}.applyToEvents.\n *\n * @stable\n */\nexport const DEFAULT_APPLY_TO_EVENTS: ReadonlyArray<string> = Object.freeze([\n 'tool.execute.end',\n 'tool.execute.error',\n 'text.delta',\n]);\n"],"mappings":";;;;;;;;;AA2BA,MAAaA,uCACX,OAAO,OAAO;CACZ,OAAO,OAAO;EACZ,QAAQ;EACR,OAAO;EACP,aACE;EACH,CAAC;CACF,OAAO,OAAO;EACZ,QAAQ;EACR,OAAO;EACP,aAAa;EACd,CAAC;CACF,OAAO,OAAO;EACZ,QAAQ;EACR,OAAO;EACP,aAAa;EACd,CAAC;CACF,OAAO,OAAO;EACZ,QAAQ;EACR,OAAO;EACP,aAAa;EACd,CAAC;CACF,OAAO,OAAO;EACZ,QAAQ;EACR,OAAO;EACP,aAAa;EACd,CAAC;CACF,OAAO,OAAO;EACZ,QAAQ;EACR,OAAO;EACP,aAAa;EACd,CAAC;CACF,OAAO,OAAO;EACZ,QAAQ;EACR,OAAO;EACP,aAAa;EACd,CAAC;CACH,CAAC;;;;;;;;AASJ,MAAaC,0BAAiD,OAAO,OAAO;CAC1E;CACA;CACA;CACD,CAAC"}
@@ -8,14 +8,14 @@ import { createHash } from "node:crypto";
8
8
  * pattern catalogue to the JSON-stringified payload; matched
9
9
  * fragments are wrapped in the configured envelope (default) or
10
10
  * stripped (per the `'strip'` policy) or passed through (per the
11
- * `'pass-through'` policy operator opt-out for trusted
11
+ * `'pass-through'` policy - operator opt-out for trusted
12
12
  * deployments).
13
13
  *
14
14
  * The sanitizer runs AFTER the `ServerMessage` Zod-validation pass
15
15
  * in `@graphorin/protocol` and BEFORE the message hits the wire (so
16
16
  * the protocol schema is unchanged; the sanitization is a pure
17
17
  * transform applied to validated payloads). It is bytes-equal across
18
- * the WS / SSE / REST transports the same input + same policy
18
+ * the WS / SSE / REST transports - the same input + same policy
19
19
  * produces the same output regardless of which transport invoked
20
20
  * the sanitizer.
21
21
  *
@@ -1 +1 @@
1
- {"version":3,"file":"sanitizer.js","names":["policy: DeliveryCommentaryPolicy","applyToEvents: ReadonlyArray<string>","patterns: ReadonlyArray<DeliveryCommentaryPattern>","sink: DeliveryCommentarySink | undefined","decision: DeliveryCommentaryDecision","next: unknown[]","next: Record<string, unknown>","next: string[]","out: { kind: 'plain' | 'wrapped'; text: string }[]"],"sources":["../../src/commentary/sanitizer.ts"],"sourcesContent":["/**\n * Stateless delivery-layer commentary-phase trace sanitizer. Walks\n * outbound `ServerMessage` event frames and applies the configured\n * pattern catalogue to the JSON-stringified payload; matched\n * fragments are wrapped in the configured envelope (default) or\n * stripped (per the `'strip'` policy) or passed through (per the\n * `'pass-through'` policy — operator opt-out for trusted\n * deployments).\n *\n * The sanitizer runs AFTER the `ServerMessage` Zod-validation pass\n * in `@graphorin/protocol` and BEFORE the message hits the wire (so\n * the protocol schema is unchanged; the sanitization is a pure\n * transform applied to validated payloads). It is bytes-equal across\n * the WS / SSE / REST transports — the same input + same policy\n * produces the same output regardless of which transport invoked\n * the sanitizer.\n *\n * @packageDocumentation\n */\n\nimport { createHash } from 'node:crypto';\nimport type { ServerEventFrame } from '@graphorin/protocol';\n\nimport {\n DEFAULT_APPLY_TO_EVENTS,\n DEFAULT_DELIVERY_COMMENTARY_PATTERNS,\n} from './built-in-patterns.js';\nimport type {\n DeliveryCommentaryConfig,\n DeliveryCommentaryDecision,\n DeliveryCommentaryPattern,\n DeliveryCommentaryPolicy,\n DeliveryCommentaryReason,\n DeliveryCommentarySink,\n DeliveryCommentaryTransport,\n} from './types.js';\n\nconst DEFAULT_OPEN = '<<<commentary>>>';\nconst DEFAULT_CLOSE = '<<</commentary>>>';\n\n/**\n * Public surface returned by {@link createDeliveryCommentarySanitizer}.\n *\n * @stable\n */\nexport interface DeliveryCommentarySanitizer {\n readonly policy: DeliveryCommentaryPolicy;\n readonly applyToEvents: ReadonlyArray<string>;\n readonly patterns: ReadonlyArray<DeliveryCommentaryPattern>;\n /**\n * Sanitize the payload of a single `event` frame. Returns the\n * (possibly-replaced) frame; emits an audit decision via the\n * configured sink when the sanitizer mutated the payload.\n *\n * The frame is returned unchanged when:\n * - the policy is `'pass-through'`,\n * - the event type is not in `applyToEvents`,\n * - or no pattern matched the JSON-stringified payload.\n */\n sanitize(frame: ServerEventFrame, transport: DeliveryCommentaryTransport): ServerEventFrame;\n}\n\n/**\n * Build a stateless delivery-layer sanitizer. Tests can swap the\n * `sink` for an in-memory recorder; production wires the\n * `@graphorin/security/audit` `appendAudit` helper.\n *\n * @stable\n */\nexport function createDeliveryCommentarySanitizer(\n config: DeliveryCommentaryConfig = {},\n): DeliveryCommentarySanitizer {\n const policy: DeliveryCommentaryPolicy = config.policy ?? 'wrap';\n const applyToEvents: ReadonlyArray<string> = config.applyToEvents ?? DEFAULT_APPLY_TO_EVENTS;\n const applyToSet = new Set(applyToEvents);\n const patterns: ReadonlyArray<DeliveryCommentaryPattern> =\n config.patterns ?? DEFAULT_DELIVERY_COMMENTARY_PATTERNS;\n const open = config.wrapOpen ?? DEFAULT_OPEN;\n const close = config.wrapClose ?? DEFAULT_CLOSE;\n const sink: DeliveryCommentarySink | undefined = config.sink;\n\n function sanitize(\n frame: ServerEventFrame,\n transport: DeliveryCommentaryTransport,\n ): ServerEventFrame {\n if (policy === 'pass-through') return frame;\n if (!applyToSet.has(frame.type)) return frame;\n const before = JSON.stringify(frame.payload ?? null);\n const allReasons = new Set<DeliveryCommentaryReason>();\n let mutated = false;\n const next = walkAndSanitize(frame.payload, (text) => {\n const out = applyToText(text, patterns, policy, open, close);\n for (const reason of out.reasons) allReasons.add(reason);\n if (out.transformed !== text) mutated = true;\n return out.transformed;\n });\n if (!mutated) return frame;\n const after = JSON.stringify(next ?? null);\n const decision: DeliveryCommentaryDecision = {\n transport,\n boundary: 'event-emission',\n policy,\n applied: true,\n reasons: [...allReasons],\n matchedPattern: [...allReasons][0],\n sha256OfBefore: sha256(before),\n sha256OfAfter: sha256(after),\n eventType: frame.type,\n };\n try {\n sink?.onDecision(decision);\n } catch {\n // Sink errors must never block the wire — swallow + continue.\n }\n return Object.freeze({ ...frame, payload: next });\n }\n\n return Object.freeze({\n policy,\n applyToEvents,\n patterns,\n sanitize,\n });\n}\n\ninterface ApplyResult {\n readonly transformed: string;\n readonly reasons: ReadonlyArray<DeliveryCommentaryReason>;\n}\n\n/**\n * Recursively walks any JSON-shaped value (`null`, primitive, array,\n * object) and applies the supplied transform to every string leaf.\n * Returns a structurally-cloned copy when at least one leaf was\n * rewritten; returns the original input by reference otherwise.\n */\nfunction walkAndSanitize(value: unknown, transform: (s: string) => string): unknown {\n if (typeof value === 'string') {\n const next = transform(value);\n return next === value ? value : next;\n }\n if (Array.isArray(value)) {\n let mutated = false;\n const next: unknown[] = new Array(value.length);\n for (let i = 0; i < value.length; i += 1) {\n const child = walkAndSanitize(value[i], transform);\n if (child !== value[i]) mutated = true;\n next[i] = child;\n }\n return mutated ? next : value;\n }\n if (value !== null && typeof value === 'object') {\n let mutated = false;\n const next: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(value as Record<string, unknown>)) {\n const child = walkAndSanitize(val, transform);\n if (child !== val) mutated = true;\n next[key] = child;\n }\n return mutated ? next : value;\n }\n return value;\n}\n\nfunction applyToText(\n text: string,\n patterns: ReadonlyArray<DeliveryCommentaryPattern>,\n policy: DeliveryCommentaryPolicy,\n open: string,\n close: string,\n): ApplyResult {\n // Split the body into already-wrapped + plain segments so we never\n // re-scan inside an existing wrap envelope. This makes the sanitizer\n // idempotent on the same payload (a second pass produces bytes-equal\n // output, satisfying the cross-cut composition contract with\n // Phase 11 session-output sanitization + Phase 11 replay\n // sanitization).\n const segments = splitByWrapEnvelope(text, open, close);\n const matchedReasons = new Set<DeliveryCommentaryReason>();\n const next: string[] = [];\n for (const segment of segments) {\n if (segment.kind === 'wrapped') {\n next.push(segment.text);\n continue;\n }\n let segText = segment.text;\n for (const pattern of patterns) {\n const regex = freshRegex(pattern.regex);\n let didMatch = false;\n segText = segText.replace(regex, (match) => {\n didMatch = true;\n if (policy === 'strip') return '';\n return `${open}${match}${close}`;\n });\n if (didMatch) matchedReasons.add(pattern.reason);\n }\n next.push(segText);\n }\n return {\n transformed: next.join(''),\n reasons: [...matchedReasons],\n };\n}\n\nfunction freshRegex(re: RegExp): RegExp {\n return new RegExp(re.source, re.flags);\n}\n\nfunction splitByWrapEnvelope(\n text: string,\n open: string,\n close: string,\n): ReadonlyArray<{ readonly kind: 'plain' | 'wrapped'; readonly text: string }> {\n const out: { kind: 'plain' | 'wrapped'; text: string }[] = [];\n let cursor = 0;\n while (cursor < text.length) {\n const openAt = text.indexOf(open, cursor);\n if (openAt < 0) {\n out.push({ kind: 'plain', text: text.slice(cursor) });\n break;\n }\n if (openAt > cursor) {\n out.push({ kind: 'plain', text: text.slice(cursor, openAt) });\n }\n const closeAt = text.indexOf(close, openAt + open.length);\n if (closeAt < 0) {\n out.push({ kind: 'plain', text: text.slice(openAt) });\n break;\n }\n out.push({ kind: 'wrapped', text: text.slice(openAt, closeAt + close.length) });\n cursor = closeAt + close.length;\n }\n return out;\n}\n\nfunction sha256(value: string): string {\n return createHash('sha256').update(value, 'utf8').digest('hex');\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"sanitizer.js","names":["policy: DeliveryCommentaryPolicy","applyToEvents: ReadonlyArray<string>","patterns: ReadonlyArray<DeliveryCommentaryPattern>","sink: DeliveryCommentarySink | undefined","decision: DeliveryCommentaryDecision","next: unknown[]","next: Record<string, unknown>","next: string[]","out: { kind: 'plain' | 'wrapped'; text: string }[]"],"sources":["../../src/commentary/sanitizer.ts"],"sourcesContent":["/**\n * Stateless delivery-layer commentary-phase trace sanitizer. Walks\n * outbound `ServerMessage` event frames and applies the configured\n * pattern catalogue to the JSON-stringified payload; matched\n * fragments are wrapped in the configured envelope (default) or\n * stripped (per the `'strip'` policy) or passed through (per the\n * `'pass-through'` policy - operator opt-out for trusted\n * deployments).\n *\n * The sanitizer runs AFTER the `ServerMessage` Zod-validation pass\n * in `@graphorin/protocol` and BEFORE the message hits the wire (so\n * the protocol schema is unchanged; the sanitization is a pure\n * transform applied to validated payloads). It is bytes-equal across\n * the WS / SSE / REST transports - the same input + same policy\n * produces the same output regardless of which transport invoked\n * the sanitizer.\n *\n * @packageDocumentation\n */\n\nimport { createHash } from 'node:crypto';\nimport type { ServerEventFrame } from '@graphorin/protocol';\n\nimport {\n DEFAULT_APPLY_TO_EVENTS,\n DEFAULT_DELIVERY_COMMENTARY_PATTERNS,\n} from './built-in-patterns.js';\nimport type {\n DeliveryCommentaryConfig,\n DeliveryCommentaryDecision,\n DeliveryCommentaryPattern,\n DeliveryCommentaryPolicy,\n DeliveryCommentaryReason,\n DeliveryCommentarySink,\n DeliveryCommentaryTransport,\n} from './types.js';\n\nconst DEFAULT_OPEN = '<<<commentary>>>';\nconst DEFAULT_CLOSE = '<<</commentary>>>';\n\n/**\n * Public surface returned by {@link createDeliveryCommentarySanitizer}.\n *\n * @stable\n */\nexport interface DeliveryCommentarySanitizer {\n readonly policy: DeliveryCommentaryPolicy;\n readonly applyToEvents: ReadonlyArray<string>;\n readonly patterns: ReadonlyArray<DeliveryCommentaryPattern>;\n /**\n * Sanitize the payload of a single `event` frame. Returns the\n * (possibly-replaced) frame; emits an audit decision via the\n * configured sink when the sanitizer mutated the payload.\n *\n * The frame is returned unchanged when:\n * - the policy is `'pass-through'`,\n * - the event type is not in `applyToEvents`,\n * - or no pattern matched the JSON-stringified payload.\n */\n sanitize(frame: ServerEventFrame, transport: DeliveryCommentaryTransport): ServerEventFrame;\n}\n\n/**\n * Build a stateless delivery-layer sanitizer. Tests can swap the\n * `sink` for an in-memory recorder; production wires the\n * `@graphorin/security/audit` `appendAudit` helper.\n *\n * @stable\n */\nexport function createDeliveryCommentarySanitizer(\n config: DeliveryCommentaryConfig = {},\n): DeliveryCommentarySanitizer {\n const policy: DeliveryCommentaryPolicy = config.policy ?? 'wrap';\n const applyToEvents: ReadonlyArray<string> = config.applyToEvents ?? DEFAULT_APPLY_TO_EVENTS;\n const applyToSet = new Set(applyToEvents);\n const patterns: ReadonlyArray<DeliveryCommentaryPattern> =\n config.patterns ?? DEFAULT_DELIVERY_COMMENTARY_PATTERNS;\n const open = config.wrapOpen ?? DEFAULT_OPEN;\n const close = config.wrapClose ?? DEFAULT_CLOSE;\n const sink: DeliveryCommentarySink | undefined = config.sink;\n\n function sanitize(\n frame: ServerEventFrame,\n transport: DeliveryCommentaryTransport,\n ): ServerEventFrame {\n if (policy === 'pass-through') return frame;\n if (!applyToSet.has(frame.type)) return frame;\n const before = JSON.stringify(frame.payload ?? null);\n const allReasons = new Set<DeliveryCommentaryReason>();\n let mutated = false;\n const next = walkAndSanitize(frame.payload, (text) => {\n const out = applyToText(text, patterns, policy, open, close);\n for (const reason of out.reasons) allReasons.add(reason);\n if (out.transformed !== text) mutated = true;\n return out.transformed;\n });\n if (!mutated) return frame;\n const after = JSON.stringify(next ?? null);\n const decision: DeliveryCommentaryDecision = {\n transport,\n boundary: 'event-emission',\n policy,\n applied: true,\n reasons: [...allReasons],\n matchedPattern: [...allReasons][0],\n sha256OfBefore: sha256(before),\n sha256OfAfter: sha256(after),\n eventType: frame.type,\n };\n try {\n sink?.onDecision(decision);\n } catch {\n // Sink errors must never block the wire - swallow + continue.\n }\n return Object.freeze({ ...frame, payload: next });\n }\n\n return Object.freeze({\n policy,\n applyToEvents,\n patterns,\n sanitize,\n });\n}\n\ninterface ApplyResult {\n readonly transformed: string;\n readonly reasons: ReadonlyArray<DeliveryCommentaryReason>;\n}\n\n/**\n * Recursively walks any JSON-shaped value (`null`, primitive, array,\n * object) and applies the supplied transform to every string leaf.\n * Returns a structurally-cloned copy when at least one leaf was\n * rewritten; returns the original input by reference otherwise.\n */\nfunction walkAndSanitize(value: unknown, transform: (s: string) => string): unknown {\n if (typeof value === 'string') {\n const next = transform(value);\n return next === value ? value : next;\n }\n if (Array.isArray(value)) {\n let mutated = false;\n const next: unknown[] = new Array(value.length);\n for (let i = 0; i < value.length; i += 1) {\n const child = walkAndSanitize(value[i], transform);\n if (child !== value[i]) mutated = true;\n next[i] = child;\n }\n return mutated ? next : value;\n }\n if (value !== null && typeof value === 'object') {\n let mutated = false;\n const next: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(value as Record<string, unknown>)) {\n const child = walkAndSanitize(val, transform);\n if (child !== val) mutated = true;\n next[key] = child;\n }\n return mutated ? next : value;\n }\n return value;\n}\n\nfunction applyToText(\n text: string,\n patterns: ReadonlyArray<DeliveryCommentaryPattern>,\n policy: DeliveryCommentaryPolicy,\n open: string,\n close: string,\n): ApplyResult {\n // Split the body into already-wrapped + plain segments so we never\n // re-scan inside an existing wrap envelope. This makes the sanitizer\n // idempotent on the same payload (a second pass produces bytes-equal\n // output, satisfying the cross-cut composition contract with\n // Phase 11 session-output sanitization + Phase 11 replay\n // sanitization).\n const segments = splitByWrapEnvelope(text, open, close);\n const matchedReasons = new Set<DeliveryCommentaryReason>();\n const next: string[] = [];\n for (const segment of segments) {\n if (segment.kind === 'wrapped') {\n next.push(segment.text);\n continue;\n }\n let segText = segment.text;\n for (const pattern of patterns) {\n const regex = freshRegex(pattern.regex);\n let didMatch = false;\n segText = segText.replace(regex, (match) => {\n didMatch = true;\n if (policy === 'strip') return '';\n return `${open}${match}${close}`;\n });\n if (didMatch) matchedReasons.add(pattern.reason);\n }\n next.push(segText);\n }\n return {\n transformed: next.join(''),\n reasons: [...matchedReasons],\n };\n}\n\nfunction freshRegex(re: RegExp): RegExp {\n return new RegExp(re.source, re.flags);\n}\n\nfunction splitByWrapEnvelope(\n text: string,\n open: string,\n close: string,\n): ReadonlyArray<{ readonly kind: 'plain' | 'wrapped'; readonly text: string }> {\n const out: { kind: 'plain' | 'wrapped'; text: string }[] = [];\n let cursor = 0;\n while (cursor < text.length) {\n const openAt = text.indexOf(open, cursor);\n if (openAt < 0) {\n out.push({ kind: 'plain', text: text.slice(cursor) });\n break;\n }\n if (openAt > cursor) {\n out.push({ kind: 'plain', text: text.slice(cursor, openAt) });\n }\n const closeAt = text.indexOf(close, openAt + open.length);\n if (closeAt < 0) {\n out.push({ kind: 'plain', text: text.slice(openAt) });\n break;\n }\n out.push({ kind: 'wrapped', text: text.slice(openAt, closeAt + close.length) });\n cursor = closeAt + close.length;\n }\n return out;\n}\n\nfunction sha256(value: string): string {\n return createHash('sha256').update(value, 'utf8').digest('hex');\n}\n"],"mappings":"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"}
@@ -10,7 +10,7 @@
10
10
  * boundary in `@graphorin/sessions` can pass that package's
11
11
  * `BUILT_IN_COMMENTARY_PATTERNS` through {@link DeliveryCommentaryConfig}.patterns;
12
12
  * the shape is bytes-equal across the two layers (the
13
- * defense-in-depth posture is the load-bearing property see
13
+ * defense-in-depth posture is the load-bearing property - see
14
14
  * Phase 11 sanitizer for the storage-write boundary, this module
15
15
  * for the wire-emission boundary).
16
16
  *
@@ -21,11 +21,11 @@
21
21
  * sanitizer in `@graphorin/sessions/commentary` so the two layers
22
22
  * are bytes-equal on idempotent re-application.
23
23
  *
24
- * - `'wrap'` (default) wraps the matched fragment in a
24
+ * - `'wrap'` (default) - wraps the matched fragment in a
25
25
  * `<<<commentary>>>...<<</commentary>>>` envelope so downstream
26
26
  * consumers can choose to render or hide based on context.
27
- * - `'strip'` removes the matched fragment entirely.
28
- * - `'pass-through'` disables the sanitization (operator opt-in
27
+ * - `'strip'` - removes the matched fragment entirely.
28
+ * - `'pass-through'` - disables the sanitization (operator opt-in
29
29
  * for trusted deployments).
30
30
  *
31
31
  * @stable
@@ -79,7 +79,7 @@ interface DeliveryCommentaryDecision {
79
79
  }
80
80
  /**
81
81
  * Single audit + counter sink consumed by the sanitizer when a
82
- * decision fires. Wiring is optional operators that do not need
82
+ * decision fires. Wiring is optional - operators that do not need
83
83
  * audit telemetry can skip the sink and the sanitizer becomes a
84
84
  * pure transform.
85
85
  *
@@ -6,7 +6,7 @@ import * as _graphorin_triggers0 from "@graphorin/triggers";
6
6
  * Lifecycle wrapper around an externally-built `Consolidator`. Phase
7
7
  * 14c wires the runtime into `beforeStart` / `beforeShutdown` so the
8
8
  * background pipeline starts and stops cleanly with the daemon. The
9
- * adapter does not own the consolidator construction operators
9
+ * adapter does not own the consolidator construction - operators
10
10
  * supply the instance through the `createServer({ consolidator })`
11
11
  * option so the framework stays decoupled from the heavy
12
12
  * `@graphorin/memory` package.
@@ -1 +1 @@
1
- {"version":3,"file":"daemon.js","names":[],"sources":["../../src/consolidator/daemon.ts"],"sourcesContent":["/**\n * Lifecycle wrapper around an externally-built `Consolidator`. Phase\n * 14c wires the runtime into `beforeStart` / `beforeShutdown` so the\n * background pipeline starts and stops cleanly with the daemon. The\n * adapter does not own the consolidator construction operators\n * supply the instance through the `createServer({ consolidator })`\n * option so the framework stays decoupled from the heavy\n * `@graphorin/memory` package.\n *\n * @packageDocumentation\n */\n\n/**\n * Structurally-typed view of the `@graphorin/memory` Consolidator\n * surface. Importing the full type would force a hard dependency on\n * `@graphorin/memory`; the structural subset captured here is enough\n * for the lifecycle integration + the `/v1/health` aggregator.\n *\n * @stable\n */\nexport interface ConsolidatorLike {\n start(): Promise<void>;\n stop(): Promise<void>;\n status(): Promise<ConsolidatorStatusLike>;\n setTier?(tier: string): Promise<void>;\n pause?(): Promise<void>;\n resume?(): Promise<void>;\n drainDlq?(): Promise<number>;\n /**\n * Register the consolidator's cron / idle triggers with the server's\n * triggers scheduler so background consolidation actually fires (MCON-4).\n * Called from `beforeStart` when both a consolidator and a triggers daemon\n * are configured. Optional so a consolidator without a `defaultScope` (or a\n * custom surface) simply opts out.\n */\n registerWithScheduler?(scheduler: import('@graphorin/triggers').Scheduler): Promise<unknown>;\n}\n\n/**\n * Subset of `ConsolidatorStatus` the server health endpoint and the\n * Prometheus metrics consume. The full struct lives in\n * `@graphorin/memory/consolidator`.\n *\n * @stable\n */\nexport interface ConsolidatorStatusLike {\n readonly tier: string;\n readonly running: boolean;\n readonly paused: boolean;\n readonly queueDepth: number;\n readonly dlqSize: number;\n readonly deferredRuns: number;\n readonly emptyExtractions: number;\n readonly budget: {\n readonly tokensUsedToday: number;\n readonly costUsedToday: number;\n readonly tokensRemaining: number;\n readonly costRemaining: number;\n readonly resetAt: string;\n };\n}\n\n/**\n * @stable\n */\nexport interface CreateConsolidatorDaemonOptions {\n readonly consolidator: ConsolidatorLike;\n /** Hard timeout on `consolidator.stop()`. Defaults to 10 s. */\n readonly stopTimeoutMs?: number;\n readonly warn?: (message: string) => void;\n}\n\n/**\n * @stable\n */\nexport interface ConsolidatorDaemon {\n start(): Promise<void>;\n stop(): Promise<void>;\n status(): Promise<ConsolidatorStatusLike>;\n readonly consolidator: ConsolidatorLike;\n}\n\n/**\n * @stable\n */\nexport function createConsolidatorDaemon(\n options: CreateConsolidatorDaemonOptions,\n): ConsolidatorDaemon {\n const stopTimeoutMs = options.stopTimeoutMs ?? 10_000;\n const warn = options.warn ?? ((m: string) => process.stderr.write(`${m}\\n`));\n let started = false;\n\n return {\n get consolidator() {\n return options.consolidator;\n },\n async start() {\n if (started) return;\n started = true;\n await options.consolidator.start();\n },\n async stop() {\n if (!started) return;\n started = false;\n try {\n await withTimeout(options.consolidator.stop(), stopTimeoutMs);\n } catch (err) {\n warn(\n `[graphorin/server] consolidator daemon: stop() exceeded ${stopTimeoutMs}ms; force-aborting (${describeError(err)}).`,\n );\n }\n },\n async status() {\n return options.consolidator.status();\n },\n };\n}\n\nasync function withTimeout<T>(p: Promise<T>, ms: number): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n reject(new Error(`timeout after ${ms}ms`));\n }, ms);\n p.then(\n (value) => {\n clearTimeout(timer);\n resolve(value);\n },\n (err) => {\n clearTimeout(timer);\n reject(err);\n },\n );\n });\n}\n\nfunction describeError(err: unknown): string {\n if (err === null || err === undefined) return 'unknown';\n if (err instanceof Error) return err.message;\n return String(err);\n}\n"],"mappings":";;;;AAqFA,SAAgB,yBACd,SACoB;CACpB,MAAM,gBAAgB,QAAQ,iBAAiB;CAC/C,MAAM,OAAO,QAAQ,UAAU,MAAc,QAAQ,OAAO,MAAM,GAAG,EAAE,IAAI;CAC3E,IAAI,UAAU;AAEd,QAAO;EACL,IAAI,eAAe;AACjB,UAAO,QAAQ;;EAEjB,MAAM,QAAQ;AACZ,OAAI,QAAS;AACb,aAAU;AACV,SAAM,QAAQ,aAAa,OAAO;;EAEpC,MAAM,OAAO;AACX,OAAI,CAAC,QAAS;AACd,aAAU;AACV,OAAI;AACF,UAAM,YAAY,QAAQ,aAAa,MAAM,EAAE,cAAc;YACtD,KAAK;AACZ,SACE,2DAA2D,cAAc,sBAAsB,cAAc,IAAI,CAAC,IACnH;;;EAGL,MAAM,SAAS;AACb,UAAO,QAAQ,aAAa,QAAQ;;EAEvC;;AAGH,eAAe,YAAe,GAAe,IAAwB;AACnE,QAAO,IAAI,SAAY,SAAS,WAAW;EACzC,MAAM,QAAQ,iBAAiB;AAC7B,0BAAO,IAAI,MAAM,iBAAiB,GAAG,IAAI,CAAC;KACzC,GAAG;AACN,IAAE,MACC,UAAU;AACT,gBAAa,MAAM;AACnB,WAAQ,MAAM;MAEf,QAAQ;AACP,gBAAa,MAAM;AACnB,UAAO,IAAI;IAEd;GACD;;AAGJ,SAAS,cAAc,KAAsB;AAC3C,KAAI,QAAQ,QAAQ,QAAQ,OAAW,QAAO;AAC9C,KAAI,eAAe,MAAO,QAAO,IAAI;AACrC,QAAO,OAAO,IAAI"}
1
+ {"version":3,"file":"daemon.js","names":[],"sources":["../../src/consolidator/daemon.ts"],"sourcesContent":["/**\n * Lifecycle wrapper around an externally-built `Consolidator`. Phase\n * 14c wires the runtime into `beforeStart` / `beforeShutdown` so the\n * background pipeline starts and stops cleanly with the daemon. The\n * adapter does not own the consolidator construction - operators\n * supply the instance through the `createServer({ consolidator })`\n * option so the framework stays decoupled from the heavy\n * `@graphorin/memory` package.\n *\n * @packageDocumentation\n */\n\n/**\n * Structurally-typed view of the `@graphorin/memory` Consolidator\n * surface. Importing the full type would force a hard dependency on\n * `@graphorin/memory`; the structural subset captured here is enough\n * for the lifecycle integration + the `/v1/health` aggregator.\n *\n * @stable\n */\nexport interface ConsolidatorLike {\n start(): Promise<void>;\n stop(): Promise<void>;\n status(): Promise<ConsolidatorStatusLike>;\n setTier?(tier: string): Promise<void>;\n pause?(): Promise<void>;\n resume?(): Promise<void>;\n drainDlq?(): Promise<number>;\n /**\n * Register the consolidator's cron / idle triggers with the server's\n * triggers scheduler so background consolidation actually fires (MCON-4).\n * Called from `beforeStart` when both a consolidator and a triggers daemon\n * are configured. Optional so a consolidator without a `defaultScope` (or a\n * custom surface) simply opts out.\n */\n registerWithScheduler?(scheduler: import('@graphorin/triggers').Scheduler): Promise<unknown>;\n}\n\n/**\n * Subset of `ConsolidatorStatus` the server health endpoint and the\n * Prometheus metrics consume. The full struct lives in\n * `@graphorin/memory/consolidator`.\n *\n * @stable\n */\nexport interface ConsolidatorStatusLike {\n readonly tier: string;\n readonly running: boolean;\n readonly paused: boolean;\n readonly queueDepth: number;\n readonly dlqSize: number;\n readonly deferredRuns: number;\n readonly emptyExtractions: number;\n readonly budget: {\n readonly tokensUsedToday: number;\n readonly costUsedToday: number;\n readonly tokensRemaining: number;\n readonly costRemaining: number;\n readonly resetAt: string;\n };\n}\n\n/**\n * @stable\n */\nexport interface CreateConsolidatorDaemonOptions {\n readonly consolidator: ConsolidatorLike;\n /** Hard timeout on `consolidator.stop()`. Defaults to 10 s. */\n readonly stopTimeoutMs?: number;\n readonly warn?: (message: string) => void;\n}\n\n/**\n * @stable\n */\nexport interface ConsolidatorDaemon {\n start(): Promise<void>;\n stop(): Promise<void>;\n status(): Promise<ConsolidatorStatusLike>;\n readonly consolidator: ConsolidatorLike;\n}\n\n/**\n * @stable\n */\nexport function createConsolidatorDaemon(\n options: CreateConsolidatorDaemonOptions,\n): ConsolidatorDaemon {\n const stopTimeoutMs = options.stopTimeoutMs ?? 10_000;\n const warn = options.warn ?? ((m: string) => process.stderr.write(`${m}\\n`));\n let started = false;\n\n return {\n get consolidator() {\n return options.consolidator;\n },\n async start() {\n if (started) return;\n started = true;\n await options.consolidator.start();\n },\n async stop() {\n if (!started) return;\n started = false;\n try {\n await withTimeout(options.consolidator.stop(), stopTimeoutMs);\n } catch (err) {\n warn(\n `[graphorin/server] consolidator daemon: stop() exceeded ${stopTimeoutMs}ms; force-aborting (${describeError(err)}).`,\n );\n }\n },\n async status() {\n return options.consolidator.status();\n },\n };\n}\n\nasync function withTimeout<T>(p: Promise<T>, ms: number): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n reject(new Error(`timeout after ${ms}ms`));\n }, ms);\n p.then(\n (value) => {\n clearTimeout(timer);\n resolve(value);\n },\n (err) => {\n clearTimeout(timer);\n reject(err);\n },\n );\n });\n}\n\nfunction describeError(err: unknown): string {\n if (err === null || err === undefined) return 'unknown';\n if (err instanceof Error) return err.message;\n return String(err);\n}\n"],"mappings":";;;;AAqFA,SAAgB,yBACd,SACoB;CACpB,MAAM,gBAAgB,QAAQ,iBAAiB;CAC/C,MAAM,OAAO,QAAQ,UAAU,MAAc,QAAQ,OAAO,MAAM,GAAG,EAAE,IAAI;CAC3E,IAAI,UAAU;AAEd,QAAO;EACL,IAAI,eAAe;AACjB,UAAO,QAAQ;;EAEjB,MAAM,QAAQ;AACZ,OAAI,QAAS;AACb,aAAU;AACV,SAAM,QAAQ,aAAa,OAAO;;EAEpC,MAAM,OAAO;AACX,OAAI,CAAC,QAAS;AACd,aAAU;AACV,OAAI;AACF,UAAM,YAAY,QAAQ,aAAa,MAAM,EAAE,cAAc;YACtD,KAAK;AACZ,SACE,2DAA2D,cAAc,sBAAsB,cAAc,IAAI,CAAC,IACnH;;;EAGL,MAAM,SAAS;AACb,UAAO,QAAQ,aAAa,QAAQ;;EAEvC;;AAGH,eAAe,YAAe,GAAe,IAAwB;AACnE,QAAO,IAAI,SAAY,SAAS,WAAW;EACzC,MAAM,QAAQ,iBAAiB;AAC7B,0BAAO,IAAI,MAAM,iBAAiB,GAAG,IAAI,CAAC;KACzC,GAAG;AACN,IAAE,MACC,UAAU;AACT,gBAAa,MAAM;AACnB,WAAQ,MAAM;MAEf,QAAQ;AACP,gBAAa,MAAM;AACnB,UAAO,IAAI;IAEd;GACD;;AAGJ,SAAS,cAAc,KAAsB;AAC3C,KAAI,QAAQ,QAAQ,QAAQ,OAAW,QAAO;AAC9C,KAAI,eAAe,MAAO,QAAO,IAAI;AACrC,QAAO,OAAO,IAAI"}
@@ -1 +1 @@
1
- {"version":3,"file":"checks.js","names":["checks: {\n -readonly [K in keyof HealthChecks]?: HealthChecks[K];\n }","status","overall: HealthStatus","worst: HealthRollup"],"sources":["../../src/health/checks.ts"],"sourcesContent":["/**\n * Per-subsystem health checks aggregated by `/v1/health`. Every check\n * resolves to `'ok' | 'warn' | 'fail'`; the aggregator promotes the\n * worst status to the top-level rollup.\n *\n * The serialized shape is **flat**: each check carries its detail\n * fields directly on the same object as the `status` discriminator\n * — no nested `detail` wrapper — matching the documented response\n * contract exactly.\n *\n * @packageDocumentation\n */\n\nimport type { GraphorinSqliteStore } from '@graphorin/store-sqlite';\nimport { readWalSize } from '@graphorin/store-sqlite';\nimport type { ConsolidatorDaemon } from '../consolidator/daemon.js';\nimport type { TriggersDaemon } from '../triggers/daemon.js';\n\n/** @stable */\nexport interface ReplayBufferProbe {\n /** Total events buffered across all subjects. Aggregated by the caller. */\n readonly eventsBuffered: number;\n readonly subscribers?: number;\n readonly subscriptions?: number;\n}\n\n/** @stable */\nexport type HealthStatus = 'ok' | 'warn' | 'fail';\n\n/** @stable */\nexport type HealthRollup = 'ok' | 'degraded' | 'failing';\n\n/**\n * Common discriminator carried on every per-subsystem check entry.\n * Concrete shapes extend this with subsystem-specific fields per the\n * documented contract.\n *\n * @stable\n */\nexport interface BaseHealthCheck {\n readonly status: HealthStatus;\n readonly message?: string;\n}\n\n/** @stable */\nexport interface StorageCheck extends BaseHealthCheck {\n readonly walSizeBytes: number;\n readonly warnThresholdBytes: number;\n readonly lastCheckpointAt?: string;\n}\n\n/** @stable */\nexport interface EmbedderCheck extends BaseHealthCheck {\n readonly modelLoaded: boolean;\n readonly model?: string;\n}\n\n/** @stable */\nexport interface SecretsCheck extends BaseHealthCheck {\n readonly activeStore: string;\n}\n\n/** @stable */\nexport interface EncryptionCheck extends BaseHealthCheck {\n readonly enabled: boolean;\n readonly peerDepInstalled: boolean;\n}\n\n/** @stable */\nexport interface ConsolidatorCheck extends BaseHealthCheck {\n readonly tier: string;\n readonly running: boolean;\n readonly paused: boolean;\n readonly queueDepth: number;\n readonly dlqSize: number;\n readonly budgetRemaining: {\n readonly tokens: number;\n readonly costUsd: number;\n };\n}\n\n/** @stable */\nexport interface TriggersCheck extends BaseHealthCheck {\n readonly running: boolean;\n readonly active: number;\n readonly disabled: number;\n readonly deferred: number;\n readonly lastFireAt?: string;\n}\n\n/** @stable */\nexport interface ReplayBufferCheck extends BaseHealthCheck {\n readonly eventsBuffered: number;\n readonly subscribers?: number;\n readonly subscriptions?: number;\n}\n\n/** @stable */\nexport type HealthCheck =\n | StorageCheck\n | EmbedderCheck\n | SecretsCheck\n | EncryptionCheck\n | ConsolidatorCheck\n | TriggersCheck\n | ReplayBufferCheck;\n\n/** @stable */\nexport interface HealthChecks {\n readonly storage?: StorageCheck;\n readonly embedder?: EmbedderCheck;\n readonly secrets?: SecretsCheck;\n readonly encryption?: EncryptionCheck;\n readonly consolidator?: ConsolidatorCheck;\n readonly triggers?: TriggersCheck;\n readonly replayBuffer?: ReplayBufferCheck;\n}\n\n/** @stable */\nexport interface HealthSummary {\n readonly status: HealthRollup;\n readonly checks: HealthChecks;\n}\n\n/** @stable */\nexport interface HealthCheckOptions {\n readonly store?: GraphorinSqliteStore;\n readonly triggers?: TriggersDaemon;\n readonly consolidator?: ConsolidatorDaemon;\n readonly replayBuffer?: ReplayBufferProbe;\n readonly secretsActive?: string;\n readonly encryptionEnabled?: boolean;\n readonly cipherPeerInstalled?: boolean;\n readonly embedderModel?: string;\n readonly embedderLoaded?: boolean;\n /** Highest acceptable WAL size in bytes before warning. Default 50 MB. */\n readonly walWarnThresholdBytes?: number;\n}\n\nconst DEFAULT_WAL_WARN_THRESHOLD_BYTES = 50 * 1024 * 1024;\n\n/**\n * Build the aggregate health summary from runtime probes.\n *\n * @stable\n */\nexport async function collectHealth(options: HealthCheckOptions): Promise<HealthSummary> {\n const checks: {\n -readonly [K in keyof HealthChecks]?: HealthChecks[K];\n } = {};\n\n if (options.store !== undefined) {\n const walSize = readWalSizeSafely(options.store);\n const threshold = options.walWarnThresholdBytes ?? DEFAULT_WAL_WARN_THRESHOLD_BYTES;\n const status: HealthStatus = walSize > threshold ? 'warn' : 'ok';\n checks.storage = {\n status,\n walSizeBytes: walSize,\n warnThresholdBytes: threshold,\n };\n }\n\n if (options.embedderLoaded !== undefined) {\n checks.embedder = {\n status: options.embedderLoaded ? 'ok' : 'warn',\n modelLoaded: options.embedderLoaded,\n ...(options.embedderModel !== undefined ? { model: options.embedderModel } : {}),\n };\n }\n\n if (options.secretsActive !== undefined) {\n checks.secrets = {\n status: 'ok',\n activeStore: options.secretsActive,\n };\n }\n\n if (options.encryptionEnabled !== undefined) {\n // IP-1: a server that is serving this health endpoint with\n // encryption enabled has already opened the store WITH its key at\n // boot (createServer threads the resolved passphrase into\n // createSqliteStore and fails fast otherwise) — that successful\n // keyed open is the factual basis of this check. 'fail' is\n // reserved for an explicit negative cipher-peer probe; the old\n // default blamed a missing peer dep on every encrypted boot.\n const peerDepInstalled = options.cipherPeerInstalled ?? true;\n checks.encryption = {\n status: options.encryptionEnabled && peerDepInstalled === false ? 'fail' : 'ok',\n enabled: options.encryptionEnabled,\n peerDepInstalled,\n };\n }\n\n if (options.consolidator !== undefined) {\n try {\n const status = await options.consolidator.status();\n const overall: HealthStatus = status.dlqSize > 0 ? 'warn' : 'ok';\n checks.consolidator = {\n status: status.paused ? 'warn' : overall,\n tier: status.tier,\n running: status.running,\n paused: status.paused,\n queueDepth: status.queueDepth,\n dlqSize: status.dlqSize,\n budgetRemaining: {\n tokens: status.budget.tokensRemaining,\n costUsd: status.budget.costRemaining,\n },\n };\n } catch (err) {\n checks.consolidator = {\n status: 'fail',\n tier: 'unknown',\n running: false,\n paused: false,\n queueDepth: 0,\n dlqSize: 0,\n budgetRemaining: { tokens: 0, costUsd: 0 },\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n\n if (options.triggers !== undefined) {\n try {\n const status = await options.triggers.status();\n checks.triggers = {\n status: 'ok',\n running: status.running,\n active: status.active,\n disabled: status.disabled,\n deferred: status.deferred,\n ...(status.lastFireAt !== undefined ? { lastFireAt: status.lastFireAt } : {}),\n };\n } catch (err) {\n checks.triggers = {\n status: 'fail',\n running: false,\n active: 0,\n disabled: 0,\n deferred: 0,\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n\n if (options.replayBuffer !== undefined) {\n const probe = options.replayBuffer;\n checks.replayBuffer = {\n status: 'ok',\n eventsBuffered: probe.eventsBuffered,\n ...(probe.subscribers !== undefined ? { subscribers: probe.subscribers } : {}),\n ...(probe.subscriptions !== undefined ? { subscriptions: probe.subscriptions } : {}),\n };\n }\n\n const status = rollup(checks);\n return Object.freeze({ status, checks: Object.freeze(checks) as HealthChecks });\n}\n\n/**\n * Promote the worst per-check status to the rollup label.\n *\n * @stable\n */\nexport function rollup(checks: HealthChecks): HealthRollup {\n let worst: HealthRollup = 'ok';\n for (const value of Object.values(checks)) {\n if (value === undefined) continue;\n if (value.status === 'fail') return 'failing';\n if (value.status === 'warn') worst = 'degraded';\n }\n return worst;\n}\n\nfunction readWalSizeSafely(store: GraphorinSqliteStore): number {\n try {\n return readWalSize(store.connection);\n } catch {\n return 0;\n }\n}\n"],"mappings":";;;AA2IA,MAAM,mCAAmC,KAAK,OAAO;;;;;;AAOrD,eAAsB,cAAc,SAAqD;CACvF,MAAMA,SAEF,EAAE;AAEN,KAAI,QAAQ,UAAU,QAAW;EAC/B,MAAM,UAAU,kBAAkB,QAAQ,MAAM;EAChD,MAAM,YAAY,QAAQ,yBAAyB;AAEnD,SAAO,UAAU;GACf,QAF2B,UAAU,YAAY,SAAS;GAG1D,cAAc;GACd,oBAAoB;GACrB;;AAGH,KAAI,QAAQ,mBAAmB,OAC7B,QAAO,WAAW;EAChB,QAAQ,QAAQ,iBAAiB,OAAO;EACxC,aAAa,QAAQ;EACrB,GAAI,QAAQ,kBAAkB,SAAY,EAAE,OAAO,QAAQ,eAAe,GAAG,EAAE;EAChF;AAGH,KAAI,QAAQ,kBAAkB,OAC5B,QAAO,UAAU;EACf,QAAQ;EACR,aAAa,QAAQ;EACtB;AAGH,KAAI,QAAQ,sBAAsB,QAAW;EAQ3C,MAAM,mBAAmB,QAAQ,uBAAuB;AACxD,SAAO,aAAa;GAClB,QAAQ,QAAQ,qBAAqB,qBAAqB,QAAQ,SAAS;GAC3E,SAAS,QAAQ;GACjB;GACD;;AAGH,KAAI,QAAQ,iBAAiB,OAC3B,KAAI;EACF,MAAMC,WAAS,MAAM,QAAQ,aAAa,QAAQ;EAClD,MAAMC,UAAwBD,SAAO,UAAU,IAAI,SAAS;AAC5D,SAAO,eAAe;GACpB,QAAQA,SAAO,SAAS,SAAS;GACjC,MAAMA,SAAO;GACb,SAASA,SAAO;GAChB,QAAQA,SAAO;GACf,YAAYA,SAAO;GACnB,SAASA,SAAO;GAChB,iBAAiB;IACf,QAAQA,SAAO,OAAO;IACtB,SAASA,SAAO,OAAO;IACxB;GACF;UACM,KAAK;AACZ,SAAO,eAAe;GACpB,QAAQ;GACR,MAAM;GACN,SAAS;GACT,QAAQ;GACR,YAAY;GACZ,SAAS;GACT,iBAAiB;IAAE,QAAQ;IAAG,SAAS;IAAG;GAC1C,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;GAC1D;;AAIL,KAAI,QAAQ,aAAa,OACvB,KAAI;EACF,MAAMA,WAAS,MAAM,QAAQ,SAAS,QAAQ;AAC9C,SAAO,WAAW;GAChB,QAAQ;GACR,SAASA,SAAO;GAChB,QAAQA,SAAO;GACf,UAAUA,SAAO;GACjB,UAAUA,SAAO;GACjB,GAAIA,SAAO,eAAe,SAAY,EAAE,YAAYA,SAAO,YAAY,GAAG,EAAE;GAC7E;UACM,KAAK;AACZ,SAAO,WAAW;GAChB,QAAQ;GACR,SAAS;GACT,QAAQ;GACR,UAAU;GACV,UAAU;GACV,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;GAC1D;;AAIL,KAAI,QAAQ,iBAAiB,QAAW;EACtC,MAAM,QAAQ,QAAQ;AACtB,SAAO,eAAe;GACpB,QAAQ;GACR,gBAAgB,MAAM;GACtB,GAAI,MAAM,gBAAgB,SAAY,EAAE,aAAa,MAAM,aAAa,GAAG,EAAE;GAC7E,GAAI,MAAM,kBAAkB,SAAY,EAAE,eAAe,MAAM,eAAe,GAAG,EAAE;GACpF;;CAGH,MAAM,SAAS,OAAO,OAAO;AAC7B,QAAO,OAAO,OAAO;EAAE;EAAQ,QAAQ,OAAO,OAAO,OAAO;EAAkB,CAAC;;;;;;;AAQjF,SAAgB,OAAO,QAAoC;CACzD,IAAIE,QAAsB;AAC1B,MAAK,MAAM,SAAS,OAAO,OAAO,OAAO,EAAE;AACzC,MAAI,UAAU,OAAW;AACzB,MAAI,MAAM,WAAW,OAAQ,QAAO;AACpC,MAAI,MAAM,WAAW,OAAQ,SAAQ;;AAEvC,QAAO;;AAGT,SAAS,kBAAkB,OAAqC;AAC9D,KAAI;AACF,SAAO,YAAY,MAAM,WAAW;SAC9B;AACN,SAAO"}
1
+ {"version":3,"file":"checks.js","names":["checks: {\n -readonly [K in keyof HealthChecks]?: HealthChecks[K];\n }","status","overall: HealthStatus","worst: HealthRollup"],"sources":["../../src/health/checks.ts"],"sourcesContent":["/**\n * Per-subsystem health checks aggregated by `/v1/health`. Every check\n * resolves to `'ok' | 'warn' | 'fail'`; the aggregator promotes the\n * worst status to the top-level rollup.\n *\n * The serialized shape is **flat**: each check carries its detail\n * fields directly on the same object as the `status` discriminator\n * - no nested `detail` wrapper - matching the documented response\n * contract exactly.\n *\n * @packageDocumentation\n */\n\nimport type { GraphorinSqliteStore } from '@graphorin/store-sqlite';\nimport { readWalSize } from '@graphorin/store-sqlite';\nimport type { ConsolidatorDaemon } from '../consolidator/daemon.js';\nimport type { TriggersDaemon } from '../triggers/daemon.js';\n\n/** @stable */\nexport interface ReplayBufferProbe {\n /** Total events buffered across all subjects. Aggregated by the caller. */\n readonly eventsBuffered: number;\n readonly subscribers?: number;\n readonly subscriptions?: number;\n}\n\n/** @stable */\nexport type HealthStatus = 'ok' | 'warn' | 'fail';\n\n/** @stable */\nexport type HealthRollup = 'ok' | 'degraded' | 'failing';\n\n/**\n * Common discriminator carried on every per-subsystem check entry.\n * Concrete shapes extend this with subsystem-specific fields per the\n * documented contract.\n *\n * @stable\n */\nexport interface BaseHealthCheck {\n readonly status: HealthStatus;\n readonly message?: string;\n}\n\n/** @stable */\nexport interface StorageCheck extends BaseHealthCheck {\n readonly walSizeBytes: number;\n readonly warnThresholdBytes: number;\n readonly lastCheckpointAt?: string;\n}\n\n/** @stable */\nexport interface EmbedderCheck extends BaseHealthCheck {\n readonly modelLoaded: boolean;\n readonly model?: string;\n}\n\n/** @stable */\nexport interface SecretsCheck extends BaseHealthCheck {\n readonly activeStore: string;\n}\n\n/** @stable */\nexport interface EncryptionCheck extends BaseHealthCheck {\n readonly enabled: boolean;\n readonly peerDepInstalled: boolean;\n}\n\n/** @stable */\nexport interface ConsolidatorCheck extends BaseHealthCheck {\n readonly tier: string;\n readonly running: boolean;\n readonly paused: boolean;\n readonly queueDepth: number;\n readonly dlqSize: number;\n readonly budgetRemaining: {\n readonly tokens: number;\n readonly costUsd: number;\n };\n}\n\n/** @stable */\nexport interface TriggersCheck extends BaseHealthCheck {\n readonly running: boolean;\n readonly active: number;\n readonly disabled: number;\n readonly deferred: number;\n readonly lastFireAt?: string;\n}\n\n/** @stable */\nexport interface ReplayBufferCheck extends BaseHealthCheck {\n readonly eventsBuffered: number;\n readonly subscribers?: number;\n readonly subscriptions?: number;\n}\n\n/** @stable */\nexport type HealthCheck =\n | StorageCheck\n | EmbedderCheck\n | SecretsCheck\n | EncryptionCheck\n | ConsolidatorCheck\n | TriggersCheck\n | ReplayBufferCheck;\n\n/** @stable */\nexport interface HealthChecks {\n readonly storage?: StorageCheck;\n readonly embedder?: EmbedderCheck;\n readonly secrets?: SecretsCheck;\n readonly encryption?: EncryptionCheck;\n readonly consolidator?: ConsolidatorCheck;\n readonly triggers?: TriggersCheck;\n readonly replayBuffer?: ReplayBufferCheck;\n}\n\n/** @stable */\nexport interface HealthSummary {\n readonly status: HealthRollup;\n readonly checks: HealthChecks;\n}\n\n/** @stable */\nexport interface HealthCheckOptions {\n readonly store?: GraphorinSqliteStore;\n readonly triggers?: TriggersDaemon;\n readonly consolidator?: ConsolidatorDaemon;\n readonly replayBuffer?: ReplayBufferProbe;\n readonly secretsActive?: string;\n readonly encryptionEnabled?: boolean;\n readonly cipherPeerInstalled?: boolean;\n readonly embedderModel?: string;\n readonly embedderLoaded?: boolean;\n /** Highest acceptable WAL size in bytes before warning. Default 50 MB. */\n readonly walWarnThresholdBytes?: number;\n}\n\nconst DEFAULT_WAL_WARN_THRESHOLD_BYTES = 50 * 1024 * 1024;\n\n/**\n * Build the aggregate health summary from runtime probes.\n *\n * @stable\n */\nexport async function collectHealth(options: HealthCheckOptions): Promise<HealthSummary> {\n const checks: {\n -readonly [K in keyof HealthChecks]?: HealthChecks[K];\n } = {};\n\n if (options.store !== undefined) {\n const walSize = readWalSizeSafely(options.store);\n const threshold = options.walWarnThresholdBytes ?? DEFAULT_WAL_WARN_THRESHOLD_BYTES;\n const status: HealthStatus = walSize > threshold ? 'warn' : 'ok';\n checks.storage = {\n status,\n walSizeBytes: walSize,\n warnThresholdBytes: threshold,\n };\n }\n\n if (options.embedderLoaded !== undefined) {\n checks.embedder = {\n status: options.embedderLoaded ? 'ok' : 'warn',\n modelLoaded: options.embedderLoaded,\n ...(options.embedderModel !== undefined ? { model: options.embedderModel } : {}),\n };\n }\n\n if (options.secretsActive !== undefined) {\n checks.secrets = {\n status: 'ok',\n activeStore: options.secretsActive,\n };\n }\n\n if (options.encryptionEnabled !== undefined) {\n // IP-1: a server that is serving this health endpoint with\n // encryption enabled has already opened the store WITH its key at\n // boot (createServer threads the resolved passphrase into\n // createSqliteStore and fails fast otherwise) - that successful\n // keyed open is the factual basis of this check. 'fail' is\n // reserved for an explicit negative cipher-peer probe; the old\n // default blamed a missing peer dep on every encrypted boot.\n const peerDepInstalled = options.cipherPeerInstalled ?? true;\n checks.encryption = {\n status: options.encryptionEnabled && peerDepInstalled === false ? 'fail' : 'ok',\n enabled: options.encryptionEnabled,\n peerDepInstalled,\n };\n }\n\n if (options.consolidator !== undefined) {\n try {\n const status = await options.consolidator.status();\n const overall: HealthStatus = status.dlqSize > 0 ? 'warn' : 'ok';\n checks.consolidator = {\n status: status.paused ? 'warn' : overall,\n tier: status.tier,\n running: status.running,\n paused: status.paused,\n queueDepth: status.queueDepth,\n dlqSize: status.dlqSize,\n budgetRemaining: {\n tokens: status.budget.tokensRemaining,\n costUsd: status.budget.costRemaining,\n },\n };\n } catch (err) {\n checks.consolidator = {\n status: 'fail',\n tier: 'unknown',\n running: false,\n paused: false,\n queueDepth: 0,\n dlqSize: 0,\n budgetRemaining: { tokens: 0, costUsd: 0 },\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n\n if (options.triggers !== undefined) {\n try {\n const status = await options.triggers.status();\n checks.triggers = {\n status: 'ok',\n running: status.running,\n active: status.active,\n disabled: status.disabled,\n deferred: status.deferred,\n ...(status.lastFireAt !== undefined ? { lastFireAt: status.lastFireAt } : {}),\n };\n } catch (err) {\n checks.triggers = {\n status: 'fail',\n running: false,\n active: 0,\n disabled: 0,\n deferred: 0,\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n\n if (options.replayBuffer !== undefined) {\n const probe = options.replayBuffer;\n checks.replayBuffer = {\n status: 'ok',\n eventsBuffered: probe.eventsBuffered,\n ...(probe.subscribers !== undefined ? { subscribers: probe.subscribers } : {}),\n ...(probe.subscriptions !== undefined ? { subscriptions: probe.subscriptions } : {}),\n };\n }\n\n const status = rollup(checks);\n return Object.freeze({ status, checks: Object.freeze(checks) as HealthChecks });\n}\n\n/**\n * Promote the worst per-check status to the rollup label.\n *\n * @stable\n */\nexport function rollup(checks: HealthChecks): HealthRollup {\n let worst: HealthRollup = 'ok';\n for (const value of Object.values(checks)) {\n if (value === undefined) continue;\n if (value.status === 'fail') return 'failing';\n if (value.status === 'warn') worst = 'degraded';\n }\n return worst;\n}\n\nfunction readWalSizeSafely(store: GraphorinSqliteStore): number {\n try {\n return readWalSize(store.connection);\n } catch {\n return 0;\n }\n}\n"],"mappings":";;;AA2IA,MAAM,mCAAmC,KAAK,OAAO;;;;;;AAOrD,eAAsB,cAAc,SAAqD;CACvF,MAAMA,SAEF,EAAE;AAEN,KAAI,QAAQ,UAAU,QAAW;EAC/B,MAAM,UAAU,kBAAkB,QAAQ,MAAM;EAChD,MAAM,YAAY,QAAQ,yBAAyB;AAEnD,SAAO,UAAU;GACf,QAF2B,UAAU,YAAY,SAAS;GAG1D,cAAc;GACd,oBAAoB;GACrB;;AAGH,KAAI,QAAQ,mBAAmB,OAC7B,QAAO,WAAW;EAChB,QAAQ,QAAQ,iBAAiB,OAAO;EACxC,aAAa,QAAQ;EACrB,GAAI,QAAQ,kBAAkB,SAAY,EAAE,OAAO,QAAQ,eAAe,GAAG,EAAE;EAChF;AAGH,KAAI,QAAQ,kBAAkB,OAC5B,QAAO,UAAU;EACf,QAAQ;EACR,aAAa,QAAQ;EACtB;AAGH,KAAI,QAAQ,sBAAsB,QAAW;EAQ3C,MAAM,mBAAmB,QAAQ,uBAAuB;AACxD,SAAO,aAAa;GAClB,QAAQ,QAAQ,qBAAqB,qBAAqB,QAAQ,SAAS;GAC3E,SAAS,QAAQ;GACjB;GACD;;AAGH,KAAI,QAAQ,iBAAiB,OAC3B,KAAI;EACF,MAAMC,WAAS,MAAM,QAAQ,aAAa,QAAQ;EAClD,MAAMC,UAAwBD,SAAO,UAAU,IAAI,SAAS;AAC5D,SAAO,eAAe;GACpB,QAAQA,SAAO,SAAS,SAAS;GACjC,MAAMA,SAAO;GACb,SAASA,SAAO;GAChB,QAAQA,SAAO;GACf,YAAYA,SAAO;GACnB,SAASA,SAAO;GAChB,iBAAiB;IACf,QAAQA,SAAO,OAAO;IACtB,SAASA,SAAO,OAAO;IACxB;GACF;UACM,KAAK;AACZ,SAAO,eAAe;GACpB,QAAQ;GACR,MAAM;GACN,SAAS;GACT,QAAQ;GACR,YAAY;GACZ,SAAS;GACT,iBAAiB;IAAE,QAAQ;IAAG,SAAS;IAAG;GAC1C,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;GAC1D;;AAIL,KAAI,QAAQ,aAAa,OACvB,KAAI;EACF,MAAMA,WAAS,MAAM,QAAQ,SAAS,QAAQ;AAC9C,SAAO,WAAW;GAChB,QAAQ;GACR,SAASA,SAAO;GAChB,QAAQA,SAAO;GACf,UAAUA,SAAO;GACjB,UAAUA,SAAO;GACjB,GAAIA,SAAO,eAAe,SAAY,EAAE,YAAYA,SAAO,YAAY,GAAG,EAAE;GAC7E;UACM,KAAK;AACZ,SAAO,WAAW;GAChB,QAAQ;GACR,SAAS;GACT,QAAQ;GACR,UAAU;GACV,UAAU;GACV,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;GAC1D;;AAIL,KAAI,QAAQ,iBAAiB,QAAW;EACtC,MAAM,QAAQ,QAAQ;AACtB,SAAO,eAAe;GACpB,QAAQ;GACR,gBAAgB,MAAM;GACtB,GAAI,MAAM,gBAAgB,SAAY,EAAE,aAAa,MAAM,aAAa,GAAG,EAAE;GAC7E,GAAI,MAAM,kBAAkB,SAAY,EAAE,eAAe,MAAM,eAAe,GAAG,EAAE;GACpF;;CAGH,MAAM,SAAS,OAAO,OAAO;AAC7B,QAAO,OAAO,OAAO;EAAE;EAAQ,QAAQ,OAAO,OAAO,OAAO;EAAkB,CAAC;;;;;;;AAQjF,SAAgB,OAAO,QAAoC;CACzD,IAAIE,QAAsB;AAC1B,MAAK,MAAM,SAAS,OAAO,OAAO,OAAO,EAAE;AACzC,MAAI,UAAU,OAAW;AACzB,MAAI,MAAM,WAAW,OAAQ,QAAO;AACpC,MAAI,MAAM,WAAW,OAAQ,SAAQ;;AAEvC,QAAO;;AAGT,SAAS,kBAAkB,OAAqC;AAC9D,KAAI;AACF,SAAO,YAAY,MAAM,WAAW;SAC9B;AACN,SAAO"}
@@ -48,8 +48,8 @@ interface MetricsRoutesOptions {
48
48
  * replay buffer occupancy) into the registry so the scraped output
49
49
  * reflects the moment of the request.
50
50
  *
51
- * Refresh failures are swallowed a broken probe never blocks a
52
- * Prometheus scrape and surfaced through the optional `onError`
51
+ * Refresh failures are swallowed - a broken probe never blocks a
52
+ * Prometheus scrape - and surfaced through the optional `onError`
53
53
  * sink.
54
54
  */
55
55
  readonly refresh?: () => void | Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"routes.js","names":[],"sources":["../../src/health/routes.ts"],"sourcesContent":["/**\n * Health-family REST routes.\n *\n * GET /health (unauthenticated; liveness)\n * GET /health/secrets (admin scope)\n * GET /metrics (Prometheus exposition)\n *\n * `/health` returns 200 even when the rollup is `'degraded'`; only\n * `'failing'` short-circuits with 503 so liveness probes do not flap\n * on minor degradations (e.g. WAL above the warn threshold).\n *\n * @packageDocumentation\n */\n\nimport { getSecretsStoreStatus } from '@graphorin/security/secrets';\nimport type { Context } from 'hono';\nimport { Hono } from 'hono';\n\nimport type { ServerVariables } from '../internal/context.js';\nimport type { MetricRegistry } from '../metrics/registry.js';\nimport { createScopeMiddleware } from '../middleware/scope.js';\nimport { collectHealth, type HealthCheckOptions } from './checks.js';\n\n/**\n * @stable\n */\nexport interface HealthRouteOptions {\n readonly version: string;\n readonly startedAt: number;\n readonly now?: () => number;\n readonly probes: () => HealthCheckOptions | Promise<HealthCheckOptions>;\n}\n\n/**\n * Public health route (anonymous; mounted before auth middleware).\n * Returns the rollup + per-check breakdown; HTTP 200 even when the\n * rollup is `'degraded'` so liveness probes do not flap on minor\n * degradations. Only `'failing'` short-circuits with 503.\n *\n * @stable\n */\nexport function createExtendedHealthRoutes(\n options: HealthRouteOptions,\n): Hono<{ Variables: ServerVariables }> {\n const app = new Hono<{ Variables: ServerVariables }>();\n const now = options.now ?? Date.now;\n\n app.get('/', async (c) => {\n const probes = await options.probes();\n const summary = await collectHealth(probes);\n const body = {\n status: summary.status,\n version: options.version,\n uptimeSeconds: Math.max(0, Math.floor((now() - options.startedAt) / 1000)),\n checks: summary.checks,\n };\n if (summary.status === 'failing') {\n return c.json(body, 503);\n }\n return c.json(body, 200);\n });\n\n return app;\n}\n\n/**\n * Authed health route. Mounted at `${base}/health/secrets` AFTER the\n * auth middleware so the scope check has a verified token to inspect.\n * Returns the active secrets store + fallback chain + downgrade\n * reason per the secrets capability matrix.\n *\n * @stable\n */\nexport function createSecretsHealthRoutes(): Hono<{ Variables: ServerVariables }> {\n const app = new Hono<{ Variables: ServerVariables }>();\n\n app.get('/', createScopeMiddleware('secrets:read'), (c) => {\n const status = getSecretsStoreStatus();\n if (status === undefined) {\n return c.json(\n {\n active: 'unknown',\n fallbackChain: [] as ReadonlyArray<string>,\n strictMode: false,\n headlessReasons: [] as ReadonlyArray<string>,\n message: 'Secrets store has not been activated yet.',\n },\n 200,\n );\n }\n return c.json(\n {\n active: status.active,\n fallbackChain: [...status.fallbackChain],\n ...(status.downgradedFrom !== undefined ? { downgradedFrom: status.downgradedFrom } : {}),\n ...(status.downgradeReason !== undefined\n ? { downgradeReason: status.downgradeReason }\n : {}),\n strictMode: status.strictMode,\n headlessReasons: [...status.headlessReasons],\n },\n 200,\n );\n });\n\n return app;\n}\n\n/**\n * @stable\n */\nexport interface MetricsRoutesOptions {\n readonly registry: MetricRegistry;\n readonly requireAuth?: boolean;\n /**\n * Optional refresh callback invoked before every scrape. Use it to\n * sample live signals (WAL size, in-flight runs, daemon status,\n * replay buffer occupancy) into the registry so the scraped output\n * reflects the moment of the request.\n *\n * Refresh failures are swallowed a broken probe never blocks a\n * Prometheus scrape and surfaced through the optional `onError`\n * sink.\n */\n readonly refresh?: () => void | Promise<void>;\n readonly onError?: (err: unknown) => void;\n}\n\n/**\n * @stable\n */\nexport function createMetricsRoutes(\n options: MetricsRoutesOptions,\n): Hono<{ Variables: ServerVariables }> {\n const app = new Hono<{ Variables: ServerVariables }>();\n const handler = async (c: Context<{ Variables: ServerVariables }>) => {\n if (options.refresh !== undefined) {\n try {\n await options.refresh();\n } catch (err) {\n options.onError?.(err);\n }\n }\n const body = options.registry.render();\n c.header('Content-Type', options.registry.contentType());\n return c.body(body, 200);\n };\n if (options.requireAuth === true) {\n app.get('/', createScopeMiddleware('admin:metrics:read'), handler);\n } else {\n app.get('/', handler);\n }\n return app;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAyCA,SAAgB,2BACd,SACsC;CACtC,MAAM,MAAM,IAAI,MAAsC;CACtD,MAAM,MAAM,QAAQ,OAAO,KAAK;AAEhC,KAAI,IAAI,KAAK,OAAO,MAAM;EAExB,MAAM,UAAU,MAAM,cADP,MAAM,QAAQ,QAAQ,CACM;EAC3C,MAAM,OAAO;GACX,QAAQ,QAAQ;GAChB,SAAS,QAAQ;GACjB,eAAe,KAAK,IAAI,GAAG,KAAK,OAAO,KAAK,GAAG,QAAQ,aAAa,IAAK,CAAC;GAC1E,QAAQ,QAAQ;GACjB;AACD,MAAI,QAAQ,WAAW,UACrB,QAAO,EAAE,KAAK,MAAM,IAAI;AAE1B,SAAO,EAAE,KAAK,MAAM,IAAI;GACxB;AAEF,QAAO;;;;;;;;;;AAWT,SAAgB,4BAAkE;CAChF,MAAM,MAAM,IAAI,MAAsC;AAEtD,KAAI,IAAI,KAAK,sBAAsB,eAAe,GAAG,MAAM;EACzD,MAAM,SAAS,uBAAuB;AACtC,MAAI,WAAW,OACb,QAAO,EAAE,KACP;GACE,QAAQ;GACR,eAAe,EAAE;GACjB,YAAY;GACZ,iBAAiB,EAAE;GACnB,SAAS;GACV,EACD,IACD;AAEH,SAAO,EAAE,KACP;GACE,QAAQ,OAAO;GACf,eAAe,CAAC,GAAG,OAAO,cAAc;GACxC,GAAI,OAAO,mBAAmB,SAAY,EAAE,gBAAgB,OAAO,gBAAgB,GAAG,EAAE;GACxF,GAAI,OAAO,oBAAoB,SAC3B,EAAE,iBAAiB,OAAO,iBAAiB,GAC3C,EAAE;GACN,YAAY,OAAO;GACnB,iBAAiB,CAAC,GAAG,OAAO,gBAAgB;GAC7C,EACD,IACD;GACD;AAEF,QAAO;;;;;AA0BT,SAAgB,oBACd,SACsC;CACtC,MAAM,MAAM,IAAI,MAAsC;CACtD,MAAM,UAAU,OAAO,MAA+C;AACpE,MAAI,QAAQ,YAAY,OACtB,KAAI;AACF,SAAM,QAAQ,SAAS;WAChB,KAAK;AACZ,WAAQ,UAAU,IAAI;;EAG1B,MAAM,OAAO,QAAQ,SAAS,QAAQ;AACtC,IAAE,OAAO,gBAAgB,QAAQ,SAAS,aAAa,CAAC;AACxD,SAAO,EAAE,KAAK,MAAM,IAAI;;AAE1B,KAAI,QAAQ,gBAAgB,KAC1B,KAAI,IAAI,KAAK,sBAAsB,qBAAqB,EAAE,QAAQ;KAElE,KAAI,IAAI,KAAK,QAAQ;AAEvB,QAAO"}
1
+ {"version":3,"file":"routes.js","names":[],"sources":["../../src/health/routes.ts"],"sourcesContent":["/**\n * Health-family REST routes.\n *\n * GET /health (unauthenticated; liveness)\n * GET /health/secrets (admin scope)\n * GET /metrics (Prometheus exposition)\n *\n * `/health` returns 200 even when the rollup is `'degraded'`; only\n * `'failing'` short-circuits with 503 so liveness probes do not flap\n * on minor degradations (e.g. WAL above the warn threshold).\n *\n * @packageDocumentation\n */\n\nimport { getSecretsStoreStatus } from '@graphorin/security/secrets';\nimport type { Context } from 'hono';\nimport { Hono } from 'hono';\n\nimport type { ServerVariables } from '../internal/context.js';\nimport type { MetricRegistry } from '../metrics/registry.js';\nimport { createScopeMiddleware } from '../middleware/scope.js';\nimport { collectHealth, type HealthCheckOptions } from './checks.js';\n\n/**\n * @stable\n */\nexport interface HealthRouteOptions {\n readonly version: string;\n readonly startedAt: number;\n readonly now?: () => number;\n readonly probes: () => HealthCheckOptions | Promise<HealthCheckOptions>;\n}\n\n/**\n * Public health route (anonymous; mounted before auth middleware).\n * Returns the rollup + per-check breakdown; HTTP 200 even when the\n * rollup is `'degraded'` so liveness probes do not flap on minor\n * degradations. Only `'failing'` short-circuits with 503.\n *\n * @stable\n */\nexport function createExtendedHealthRoutes(\n options: HealthRouteOptions,\n): Hono<{ Variables: ServerVariables }> {\n const app = new Hono<{ Variables: ServerVariables }>();\n const now = options.now ?? Date.now;\n\n app.get('/', async (c) => {\n const probes = await options.probes();\n const summary = await collectHealth(probes);\n const body = {\n status: summary.status,\n version: options.version,\n uptimeSeconds: Math.max(0, Math.floor((now() - options.startedAt) / 1000)),\n checks: summary.checks,\n };\n if (summary.status === 'failing') {\n return c.json(body, 503);\n }\n return c.json(body, 200);\n });\n\n return app;\n}\n\n/**\n * Authed health route. Mounted at `${base}/health/secrets` AFTER the\n * auth middleware so the scope check has a verified token to inspect.\n * Returns the active secrets store + fallback chain + downgrade\n * reason per the secrets capability matrix.\n *\n * @stable\n */\nexport function createSecretsHealthRoutes(): Hono<{ Variables: ServerVariables }> {\n const app = new Hono<{ Variables: ServerVariables }>();\n\n app.get('/', createScopeMiddleware('secrets:read'), (c) => {\n const status = getSecretsStoreStatus();\n if (status === undefined) {\n return c.json(\n {\n active: 'unknown',\n fallbackChain: [] as ReadonlyArray<string>,\n strictMode: false,\n headlessReasons: [] as ReadonlyArray<string>,\n message: 'Secrets store has not been activated yet.',\n },\n 200,\n );\n }\n return c.json(\n {\n active: status.active,\n fallbackChain: [...status.fallbackChain],\n ...(status.downgradedFrom !== undefined ? { downgradedFrom: status.downgradedFrom } : {}),\n ...(status.downgradeReason !== undefined\n ? { downgradeReason: status.downgradeReason }\n : {}),\n strictMode: status.strictMode,\n headlessReasons: [...status.headlessReasons],\n },\n 200,\n );\n });\n\n return app;\n}\n\n/**\n * @stable\n */\nexport interface MetricsRoutesOptions {\n readonly registry: MetricRegistry;\n readonly requireAuth?: boolean;\n /**\n * Optional refresh callback invoked before every scrape. Use it to\n * sample live signals (WAL size, in-flight runs, daemon status,\n * replay buffer occupancy) into the registry so the scraped output\n * reflects the moment of the request.\n *\n * Refresh failures are swallowed - a broken probe never blocks a\n * Prometheus scrape - and surfaced through the optional `onError`\n * sink.\n */\n readonly refresh?: () => void | Promise<void>;\n readonly onError?: (err: unknown) => void;\n}\n\n/**\n * @stable\n */\nexport function createMetricsRoutes(\n options: MetricsRoutesOptions,\n): Hono<{ Variables: ServerVariables }> {\n const app = new Hono<{ Variables: ServerVariables }>();\n const handler = async (c: Context<{ Variables: ServerVariables }>) => {\n if (options.refresh !== undefined) {\n try {\n await options.refresh();\n } catch (err) {\n options.onError?.(err);\n }\n }\n const body = options.registry.render();\n c.header('Content-Type', options.registry.contentType());\n return c.body(body, 200);\n };\n if (options.requireAuth === true) {\n app.get('/', createScopeMiddleware('admin:metrics:read'), handler);\n } else {\n app.get('/', handler);\n }\n return app;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAyCA,SAAgB,2BACd,SACsC;CACtC,MAAM,MAAM,IAAI,MAAsC;CACtD,MAAM,MAAM,QAAQ,OAAO,KAAK;AAEhC,KAAI,IAAI,KAAK,OAAO,MAAM;EAExB,MAAM,UAAU,MAAM,cADP,MAAM,QAAQ,QAAQ,CACM;EAC3C,MAAM,OAAO;GACX,QAAQ,QAAQ;GAChB,SAAS,QAAQ;GACjB,eAAe,KAAK,IAAI,GAAG,KAAK,OAAO,KAAK,GAAG,QAAQ,aAAa,IAAK,CAAC;GAC1E,QAAQ,QAAQ;GACjB;AACD,MAAI,QAAQ,WAAW,UACrB,QAAO,EAAE,KAAK,MAAM,IAAI;AAE1B,SAAO,EAAE,KAAK,MAAM,IAAI;GACxB;AAEF,QAAO;;;;;;;;;;AAWT,SAAgB,4BAAkE;CAChF,MAAM,MAAM,IAAI,MAAsC;AAEtD,KAAI,IAAI,KAAK,sBAAsB,eAAe,GAAG,MAAM;EACzD,MAAM,SAAS,uBAAuB;AACtC,MAAI,WAAW,OACb,QAAO,EAAE,KACP;GACE,QAAQ;GACR,eAAe,EAAE;GACjB,YAAY;GACZ,iBAAiB,EAAE;GACnB,SAAS;GACV,EACD,IACD;AAEH,SAAO,EAAE,KACP;GACE,QAAQ,OAAO;GACf,eAAe,CAAC,GAAG,OAAO,cAAc;GACxC,GAAI,OAAO,mBAAmB,SAAY,EAAE,gBAAgB,OAAO,gBAAgB,GAAG,EAAE;GACxF,GAAI,OAAO,oBAAoB,SAC3B,EAAE,iBAAiB,OAAO,iBAAiB,GAC3C,EAAE;GACN,YAAY,OAAO;GACnB,iBAAiB,CAAC,GAAG,OAAO,gBAAgB;GAC7C,EACD,IACD;GACD;AAEF,QAAO;;;;;AA0BT,SAAgB,oBACd,SACsC;CACtC,MAAM,MAAM,IAAI,MAAsC;CACtD,MAAM,UAAU,OAAO,MAA+C;AACpE,MAAI,QAAQ,YAAY,OACtB,KAAI;AACF,SAAM,QAAQ,SAAS;WAChB,KAAK;AACZ,WAAQ,UAAU,IAAI;;EAG1B,MAAM,OAAO,QAAQ,SAAS,QAAQ;AACtC,IAAE,OAAO,gBAAgB,QAAQ,SAAS,aAAa,CAAC;AACxD,SAAO,EAAE,KAAK,MAAM,IAAI;;AAE1B,KAAI,QAAQ,gBAAgB,KAC1B,KAAI,IAAI,KAAK,sBAAsB,qBAAqB,EAAE,QAAQ;KAElE,KAAI,IAAI,KAAK,QAAQ;AAEvB,QAAO"}
package/dist/index.d.ts CHANGED
@@ -54,35 +54,34 @@ import "./triggers/index.js";
54
54
 
55
55
  //#region src/index.d.ts
56
56
  /**
57
- * `@graphorin/server` standalone server runtime for the Graphorin
57
+ * `@graphorin/server` - standalone server runtime for the Graphorin
58
58
  * framework.
59
59
  *
60
60
  * Phase 14a entry point. Ships:
61
61
  *
62
- * - {@link createServer} the single programmatic entry that wires
62
+ * - {@link createServer} - the single programmatic entry that wires
63
63
  * the Hono app, the auth + scope + idempotency + audit middleware
64
64
  * stack, REST endpoints (agents / workflows / sessions / memory /
65
65
  * skills / mcp / tokens / audit / health), the lifecycle hooks
66
66
  * (`beforeStart` / `onReady` / `beforeShutdown` / `onError`), the
67
67
  * pre-bind secrets-resolution + storage-migration runner, and the
68
68
  * graceful SIGTERM drain.
69
- * - {@link defineConfig} + {@link parseServerConfig} typed
69
+ * - {@link defineConfig} + {@link parseServerConfig} - typed
70
70
  * configuration loader with a Zod-validated schema; missing fields
71
71
  * fall back to documented production-ready defaults.
72
- * - {@link AgentRegistry} + {@link WorkflowRegistry} read/write
72
+ * - {@link AgentRegistry} + {@link WorkflowRegistry} - read/write
73
73
  * registries the route handlers consult to look up user-defined
74
74
  * agents / workflows.
75
- * - {@link RunStateTracker} in-memory bookkeeping for in-flight
75
+ * - {@link RunStateTracker} - in-memory bookkeeping for in-flight
76
76
  * runs (run id, status, AbortController). Phase 14b/c promote the
77
77
  * tracker to a SQLite-backed durable variant.
78
- * - The full middleware factory suite re-exported from
78
+ * - The full middleware factory suite - re-exported from
79
79
  * `@graphorin/server/middleware` so operators can compose a custom
80
80
  * Hono app on top of the same primitives.
81
81
  *
82
82
  * @packageDocumentation
83
83
  */
84
- /** Canonical version constant. Mirrors the `package.json` version. */
85
- declare const VERSION = "0.5.0";
84
+ declare const VERSION: string;
86
85
  //#endregion
87
86
  export { AgentNotFoundError, type AgentRegistration, AgentRegistry, type AgentRoutesDeps, type AgentSummary, type AuditApi, type AuditErrorSink, type AuditMiddlewareOptions, type AuditRoutesDeps, type AuthMiddlewareOptions, type AuthRoutesDeps, type BareEventFrame, type BaseHealthCheck, type BeforeShutdownContext, type BeforeStartContext, ConfigInvalidError, type ConsolidatorCheck, type ConsolidatorDaemon, type ConsolidatorLike, type ConsolidatorStatusLike, type CreateConsolidatorDaemonOptions, type CreateServerOptions, type CreateTriggersDaemonOptions, DEFAULT_APPLY_TO_EVENTS, DEFAULT_DELIVERY_COMMENTARY_PATTERNS, type DeliveryCommentaryConfig, type DeliveryCommentaryDecision, type DeliveryCommentaryPattern, type DeliveryCommentaryPolicy, type DeliveryCommentaryPolicyConfig, type DeliveryCommentaryReason, type DeliveryCommentarySanitizer, type DeliveryCommentarySink, type DeliveryCommentaryTransport, type EmbedderCheck, type EncryptionCheck, type GraphorinServer, GraphorinServerError, GraphorinServerErrorCode, HTTP_REQUEST_AUDIT_ACTION, type HealthCheck, type HealthCheckOptions, type HealthChecks, type HealthRollup, type HealthRouteOptions, type HealthRoutesDeps, type HealthStatus, type HealthSummary, IdempotencyConflictError, IdempotencyKeyRequiredError, type IdempotencyMiddlewareOptions, type IdempotencyRequireKeyMode, type LabelSet, LifecycleDoubleStartError, type LifecycleHooks, LifecycleNotStartedError, type McpApi, type McpRoutesDeps, type MemoryApi, type MemoryRoutesDeps, type MetricKind, MetricRegistry, type MetricsRoutesOptions, MigrationFailedError, type OnErrorContext, type OnReadyContext, type ParsedSubject, type PreBindResult, PrebindEncryptionPeerMissingError, PrebindEncryptionRequiredError, PrebindPepperMissingError, PrebindSecretUnresolvableError, type ReplayApi, type ReplayBuffer, type ReplayBufferCheck, type ReplayBufferOptions, type ReplayBufferProbe, type ReplayBufferSlice, type ReplayMode, type ReplayResponse, type ReplayRoutesDeps, type RequestStateMiddlewareOptions, type RequestToken, RouteHandlerMissingError, type RunDescriptor, type RunHandle, type RunPreBindOptions, type RunStateSnapshot, RunStateTracker, type RunStatus, SERVER_METRIC_NAMES, type ScopeRequirement, type SecretRefString, type SecretsCheck, type SecretsSource, type ServerAgentLike, type ServerConfigInput, ServerConfigSchema, type ServerConfigSpec, type ServerRequestState, type ServerVariables, type ServerWorkflowLike, type SessionApi, type SessionRoutesDeps, ShutdownTimeoutError, type SkillsApi, type SkillsRoutesDeps, type SseRoutesDeps, type StorageCheck, type SubscribeResult, type TokensRoutesDeps, type TriggersCheck, type TriggersDaemon, type TriggersDaemonInput, type TriggersDaemonMetrics, type TriggersDaemonStatus, type TriggersRoutesDeps, VERSION, WorkflowNotFoundError, type WorkflowRegistration, WorkflowRegistry, type WorkflowRoutesDeps, type WorkflowSummary, type WsDispatcher, type WsDispatcherOptions, type WsDispatcherWarning, type WsSubscriberHandle, type WsSubscriptionSnapshot, type WsTicket, type WsTicketConsumeResult, type WsTicketStore, type WsTicketStoreOptions, type WsUpgradeOptions, collectHealth, createAgentRoutes, createAuditMiddleware, createAuditRoutes, createAuthMiddleware, createAuthRoutes, createConsolidatorDaemon, createCorsMiddleware, createCsrfMiddleware, createDeliveryCommentarySanitizer, createExtendedHealthRoutes, createHealthRoutes, createIdempotencyMiddleware, createMcpRoutes, createMemoryRoutes, createMetricsRoutes, createRateLimitMiddleware, createReplayBuffer, createReplayRoutes, createRequestStateMiddleware, createRunRoutes, createScopeMiddleware, createSecretsHealthRoutes, createServer, createServerMetricRegistry, createSessionRoutes, createSkillsRoutes, createSseRoutes, createTokensRoutes, createTriggersDaemon, createTriggersRoutes, createWorkflowRoutes, createWsDispatcher, createWsTicketStore, createWsUpgradeEvents, defaultCatchupPolicy, defineConfig, ensureStoreAuditBinding, parseServerConfig, requiredScopeFor, rollup, runPreBind, tryParseSubject };
88
87
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA8Ba,OAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAgCa"}
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { version } from "./package.js";
1
2
  import { DEFAULT_APPLY_TO_EVENTS, DEFAULT_DELIVERY_COMMENTARY_PATTERNS } from "./commentary/built-in-patterns.js";
2
3
  import { createDeliveryCommentarySanitizer } from "./commentary/sanitizer.js";
3
4
  import "./commentary/index.js";
@@ -51,35 +52,35 @@ import "./triggers/index.js";
51
52
 
52
53
  //#region src/index.ts
53
54
  /**
54
- * `@graphorin/server` standalone server runtime for the Graphorin
55
+ * `@graphorin/server` - standalone server runtime for the Graphorin
55
56
  * framework.
56
57
  *
57
58
  * Phase 14a entry point. Ships:
58
59
  *
59
- * - {@link createServer} the single programmatic entry that wires
60
+ * - {@link createServer} - the single programmatic entry that wires
60
61
  * the Hono app, the auth + scope + idempotency + audit middleware
61
62
  * stack, REST endpoints (agents / workflows / sessions / memory /
62
63
  * skills / mcp / tokens / audit / health), the lifecycle hooks
63
64
  * (`beforeStart` / `onReady` / `beforeShutdown` / `onError`), the
64
65
  * pre-bind secrets-resolution + storage-migration runner, and the
65
66
  * graceful SIGTERM drain.
66
- * - {@link defineConfig} + {@link parseServerConfig} typed
67
+ * - {@link defineConfig} + {@link parseServerConfig} - typed
67
68
  * configuration loader with a Zod-validated schema; missing fields
68
69
  * fall back to documented production-ready defaults.
69
- * - {@link AgentRegistry} + {@link WorkflowRegistry} read/write
70
+ * - {@link AgentRegistry} + {@link WorkflowRegistry} - read/write
70
71
  * registries the route handlers consult to look up user-defined
71
72
  * agents / workflows.
72
- * - {@link RunStateTracker} in-memory bookkeeping for in-flight
73
+ * - {@link RunStateTracker} - in-memory bookkeeping for in-flight
73
74
  * runs (run id, status, AbortController). Phase 14b/c promote the
74
75
  * tracker to a SQLite-backed durable variant.
75
- * - The full middleware factory suite re-exported from
76
+ * - The full middleware factory suite - re-exported from
76
77
  * `@graphorin/server/middleware` so operators can compose a custom
77
78
  * Hono app on top of the same primitives.
78
79
  *
79
80
  * @packageDocumentation
80
81
  */
81
- /** Canonical version constant. Mirrors the `package.json` version. */
82
- const VERSION = "0.5.0";
82
+ /** Canonical version constant, derived from `package.json` at build time. */
83
+ const VERSION = version;
83
84
 
84
85
  //#endregion
85
86
  export { AgentNotFoundError, AgentRegistry, ConfigInvalidError, DEFAULT_APPLY_TO_EVENTS, DEFAULT_DELIVERY_COMMENTARY_PATTERNS, GraphorinServerError, HTTP_REQUEST_AUDIT_ACTION, IdempotencyConflictError, IdempotencyKeyRequiredError, LifecycleDoubleStartError, LifecycleNotStartedError, MetricRegistry, MigrationFailedError, PrebindEncryptionPeerMissingError, PrebindEncryptionRequiredError, PrebindPepperMissingError, PrebindSecretUnresolvableError, RouteHandlerMissingError, RunStateTracker, SERVER_METRIC_NAMES, ServerConfigSchema, ShutdownTimeoutError, VERSION, WorkflowNotFoundError, WorkflowRegistry, collectHealth, createAgentRoutes, createAuditMiddleware, createAuditRoutes, createAuthMiddleware, createAuthRoutes, createConsolidatorDaemon, createCorsMiddleware, createCsrfMiddleware, createDeliveryCommentarySanitizer, createExtendedHealthRoutes, createHealthRoutes, createIdempotencyMiddleware, createMcpRoutes, createMemoryRoutes, createMetricsRoutes, createRateLimitMiddleware, createReplayBuffer, createReplayRoutes, createRequestStateMiddleware, createRunRoutes, createScopeMiddleware, createSecretsHealthRoutes, createServer, createServerMetricRegistry, createSessionRoutes, createSkillsRoutes, createSseRoutes, createTokensRoutes, createTriggersDaemon, createTriggersRoutes, createWorkflowRoutes, createWsDispatcher, createWsTicketStore, createWsUpgradeEvents, defaultCatchupPolicy, defineConfig, ensureStoreAuditBinding, parseServerConfig, requiredScopeFor, rollup, runPreBind, tryParseSubject };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * `@graphorin/server` standalone server runtime for the Graphorin\n * framework.\n *\n * Phase 14a entry point. Ships:\n *\n * - {@link createServer} the single programmatic entry that wires\n * the Hono app, the auth + scope + idempotency + audit middleware\n * stack, REST endpoints (agents / workflows / sessions / memory /\n * skills / mcp / tokens / audit / health), the lifecycle hooks\n * (`beforeStart` / `onReady` / `beforeShutdown` / `onError`), the\n * pre-bind secrets-resolution + storage-migration runner, and the\n * graceful SIGTERM drain.\n * - {@link defineConfig} + {@link parseServerConfig} typed\n * configuration loader with a Zod-validated schema; missing fields\n * fall back to documented production-ready defaults.\n * - {@link AgentRegistry} + {@link WorkflowRegistry} read/write\n * registries the route handlers consult to look up user-defined\n * agents / workflows.\n * - {@link RunStateTracker} in-memory bookkeeping for in-flight\n * runs (run id, status, AbortController). Phase 14b/c promote the\n * tracker to a SQLite-backed durable variant.\n * - The full middleware factory suite re-exported from\n * `@graphorin/server/middleware` so operators can compose a custom\n * Hono app on top of the same primitives.\n *\n * @packageDocumentation\n */\n\n/** Canonical version constant. Mirrors the `package.json` version. */\nexport const VERSION = '0.5.0';\n\nexport {\n type CreateServerOptions,\n createServer,\n ensureStoreAuditBinding,\n type GraphorinServer,\n type TriggersDaemonInput,\n} from './app.js';\nexport {\n createDeliveryCommentarySanitizer,\n DEFAULT_APPLY_TO_EVENTS,\n DEFAULT_DELIVERY_COMMENTARY_PATTERNS,\n type DeliveryCommentaryConfig,\n type DeliveryCommentaryDecision,\n type DeliveryCommentaryPattern,\n type DeliveryCommentaryPolicy,\n type DeliveryCommentaryReason,\n type DeliveryCommentarySanitizer,\n type DeliveryCommentarySink,\n type DeliveryCommentaryTransport,\n} from './commentary/index.js';\nexport {\n type DeliveryCommentaryPolicyConfig,\n defineConfig,\n type IdempotencyRequireKeyMode,\n parseServerConfig,\n type SecretRefString,\n type SecretsSource,\n type ServerConfigInput,\n ServerConfigSchema,\n type ServerConfigSpec,\n} from './config.js';\nexport {\n type ConsolidatorDaemon,\n type ConsolidatorLike,\n type ConsolidatorStatusLike,\n type CreateConsolidatorDaemonOptions,\n createConsolidatorDaemon,\n} from './consolidator/index.js';\nexport * from './errors/index.js';\nexport {\n type BaseHealthCheck,\n type ConsolidatorCheck,\n collectHealth,\n createExtendedHealthRoutes,\n createMetricsRoutes,\n createSecretsHealthRoutes,\n type EmbedderCheck,\n type EncryptionCheck,\n type HealthCheck,\n type HealthCheckOptions,\n type HealthChecks,\n type HealthRollup,\n type HealthRouteOptions,\n type HealthStatus,\n type HealthSummary,\n type MetricsRoutesOptions,\n type ReplayBufferCheck,\n type ReplayBufferProbe,\n rollup,\n type SecretsCheck,\n type StorageCheck,\n type TriggersCheck,\n} from './health/index.js';\nexport type { RequestToken, ServerRequestState, ServerVariables } from './internal/context.js';\nexport {\n type BeforeShutdownContext,\n type BeforeStartContext,\n type LifecycleHooks,\n type OnErrorContext,\n type OnReadyContext,\n type PreBindResult,\n type RunPreBindOptions,\n runPreBind,\n} from './lifecycle/index.js';\nexport {\n createServerMetricRegistry,\n type LabelSet,\n type MetricKind,\n MetricRegistry,\n SERVER_METRIC_NAMES,\n} from './metrics/index.js';\nexport {\n type AuditErrorSink,\n type AuditMiddlewareOptions,\n type AuthMiddlewareOptions,\n createAuditMiddleware,\n createAuthMiddleware,\n createCorsMiddleware,\n createCsrfMiddleware,\n createIdempotencyMiddleware,\n createRateLimitMiddleware,\n createRequestStateMiddleware,\n createScopeMiddleware,\n HTTP_REQUEST_AUDIT_ACTION,\n type IdempotencyMiddlewareOptions,\n type RequestStateMiddlewareOptions,\n type ScopeRequirement,\n} from './middleware/index.js';\nexport {\n type AgentRegistration,\n AgentRegistry,\n type AgentSummary,\n type ServerAgentLike,\n type ServerWorkflowLike,\n type WorkflowRegistration,\n WorkflowRegistry,\n type WorkflowSummary,\n} from './registry/index.js';\nexport {\n createReplayRoutes,\n type ReplayApi,\n type ReplayMode,\n type ReplayResponse,\n type ReplayRoutesDeps,\n} from './replay/index.js';\nexport {\n type AgentRoutesDeps,\n type AuditApi,\n type AuditRoutesDeps,\n type AuthRoutesDeps,\n createAgentRoutes,\n createAuditRoutes,\n createAuthRoutes,\n createHealthRoutes,\n createMcpRoutes,\n createMemoryRoutes,\n createRunRoutes,\n createSessionRoutes,\n createSkillsRoutes,\n createTokensRoutes,\n createWorkflowRoutes,\n type HealthRoutesDeps,\n type McpApi,\n type McpRoutesDeps,\n type MemoryApi,\n type MemoryRoutesDeps,\n type SessionApi,\n type SessionRoutesDeps,\n type SkillsApi,\n type SkillsRoutesDeps,\n type TokensRoutesDeps,\n type WorkflowRoutesDeps,\n} from './routes/index.js';\nexport {\n type RunDescriptor,\n type RunHandle,\n type RunStateSnapshot,\n RunStateTracker,\n type RunStatus,\n} from './runtime/run-state.js';\nexport { createSseRoutes, type SseRoutesDeps } from './sse/index.js';\nexport {\n type CreateTriggersDaemonOptions,\n createTriggersDaemon,\n createTriggersRoutes,\n defaultCatchupPolicy,\n type TriggersDaemon,\n type TriggersDaemonMetrics,\n type TriggersDaemonStatus,\n type TriggersRoutesDeps,\n} from './triggers/index.js';\nexport {\n type BareEventFrame,\n createReplayBuffer,\n createWsDispatcher,\n createWsTicketStore,\n createWsUpgradeEvents,\n type ParsedSubject,\n type ReplayBuffer,\n type ReplayBufferOptions,\n type ReplayBufferSlice,\n requiredScopeFor,\n type SubscribeResult,\n tryParseSubject,\n type WsDispatcher,\n type WsDispatcherOptions,\n type WsDispatcherWarning,\n type WsSubscriberHandle,\n type WsSubscriptionSnapshot,\n type WsTicket,\n type WsTicketConsumeResult,\n type WsTicketStore,\n type WsTicketStoreOptions,\n type WsUpgradeOptions,\n} from './ws/index.js';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,MAAa,UAAU"}
1
+ {"version":3,"file":"index.js","names":["VERSION: string","pkg.version"],"sources":["../src/index.ts"],"sourcesContent":["/**\n * `@graphorin/server` - standalone server runtime for the Graphorin\n * framework.\n *\n * Phase 14a entry point. Ships:\n *\n * - {@link createServer} - the single programmatic entry that wires\n * the Hono app, the auth + scope + idempotency + audit middleware\n * stack, REST endpoints (agents / workflows / sessions / memory /\n * skills / mcp / tokens / audit / health), the lifecycle hooks\n * (`beforeStart` / `onReady` / `beforeShutdown` / `onError`), the\n * pre-bind secrets-resolution + storage-migration runner, and the\n * graceful SIGTERM drain.\n * - {@link defineConfig} + {@link parseServerConfig} - typed\n * configuration loader with a Zod-validated schema; missing fields\n * fall back to documented production-ready defaults.\n * - {@link AgentRegistry} + {@link WorkflowRegistry} - read/write\n * registries the route handlers consult to look up user-defined\n * agents / workflows.\n * - {@link RunStateTracker} - in-memory bookkeeping for in-flight\n * runs (run id, status, AbortController). Phase 14b/c promote the\n * tracker to a SQLite-backed durable variant.\n * - The full middleware factory suite - re-exported from\n * `@graphorin/server/middleware` so operators can compose a custom\n * Hono app on top of the same primitives.\n *\n * @packageDocumentation\n */\n\n/** Canonical version constant, derived from `package.json` at build time. */\nimport pkg from '../package.json' with { type: 'json' };\n\nexport const VERSION: string = pkg.version;\n\nexport {\n type CreateServerOptions,\n createServer,\n ensureStoreAuditBinding,\n type GraphorinServer,\n type TriggersDaemonInput,\n} from './app.js';\nexport {\n createDeliveryCommentarySanitizer,\n DEFAULT_APPLY_TO_EVENTS,\n DEFAULT_DELIVERY_COMMENTARY_PATTERNS,\n type DeliveryCommentaryConfig,\n type DeliveryCommentaryDecision,\n type DeliveryCommentaryPattern,\n type DeliveryCommentaryPolicy,\n type DeliveryCommentaryReason,\n type DeliveryCommentarySanitizer,\n type DeliveryCommentarySink,\n type DeliveryCommentaryTransport,\n} from './commentary/index.js';\nexport {\n type DeliveryCommentaryPolicyConfig,\n defineConfig,\n type IdempotencyRequireKeyMode,\n parseServerConfig,\n type SecretRefString,\n type SecretsSource,\n type ServerConfigInput,\n ServerConfigSchema,\n type ServerConfigSpec,\n} from './config.js';\nexport {\n type ConsolidatorDaemon,\n type ConsolidatorLike,\n type ConsolidatorStatusLike,\n type CreateConsolidatorDaemonOptions,\n createConsolidatorDaemon,\n} from './consolidator/index.js';\nexport * from './errors/index.js';\nexport {\n type BaseHealthCheck,\n type ConsolidatorCheck,\n collectHealth,\n createExtendedHealthRoutes,\n createMetricsRoutes,\n createSecretsHealthRoutes,\n type EmbedderCheck,\n type EncryptionCheck,\n type HealthCheck,\n type HealthCheckOptions,\n type HealthChecks,\n type HealthRollup,\n type HealthRouteOptions,\n type HealthStatus,\n type HealthSummary,\n type MetricsRoutesOptions,\n type ReplayBufferCheck,\n type ReplayBufferProbe,\n rollup,\n type SecretsCheck,\n type StorageCheck,\n type TriggersCheck,\n} from './health/index.js';\nexport type { RequestToken, ServerRequestState, ServerVariables } from './internal/context.js';\nexport {\n type BeforeShutdownContext,\n type BeforeStartContext,\n type LifecycleHooks,\n type OnErrorContext,\n type OnReadyContext,\n type PreBindResult,\n type RunPreBindOptions,\n runPreBind,\n} from './lifecycle/index.js';\nexport {\n createServerMetricRegistry,\n type LabelSet,\n type MetricKind,\n MetricRegistry,\n SERVER_METRIC_NAMES,\n} from './metrics/index.js';\nexport {\n type AuditErrorSink,\n type AuditMiddlewareOptions,\n type AuthMiddlewareOptions,\n createAuditMiddleware,\n createAuthMiddleware,\n createCorsMiddleware,\n createCsrfMiddleware,\n createIdempotencyMiddleware,\n createRateLimitMiddleware,\n createRequestStateMiddleware,\n createScopeMiddleware,\n HTTP_REQUEST_AUDIT_ACTION,\n type IdempotencyMiddlewareOptions,\n type RequestStateMiddlewareOptions,\n type ScopeRequirement,\n} from './middleware/index.js';\nexport {\n type AgentRegistration,\n AgentRegistry,\n type AgentSummary,\n type ServerAgentLike,\n type ServerWorkflowLike,\n type WorkflowRegistration,\n WorkflowRegistry,\n type WorkflowSummary,\n} from './registry/index.js';\nexport {\n createReplayRoutes,\n type ReplayApi,\n type ReplayMode,\n type ReplayResponse,\n type ReplayRoutesDeps,\n} from './replay/index.js';\nexport {\n type AgentRoutesDeps,\n type AuditApi,\n type AuditRoutesDeps,\n type AuthRoutesDeps,\n createAgentRoutes,\n createAuditRoutes,\n createAuthRoutes,\n createHealthRoutes,\n createMcpRoutes,\n createMemoryRoutes,\n createRunRoutes,\n createSessionRoutes,\n createSkillsRoutes,\n createTokensRoutes,\n createWorkflowRoutes,\n type HealthRoutesDeps,\n type McpApi,\n type McpRoutesDeps,\n type MemoryApi,\n type MemoryRoutesDeps,\n type SessionApi,\n type SessionRoutesDeps,\n type SkillsApi,\n type SkillsRoutesDeps,\n type TokensRoutesDeps,\n type WorkflowRoutesDeps,\n} from './routes/index.js';\nexport {\n type RunDescriptor,\n type RunHandle,\n type RunStateSnapshot,\n RunStateTracker,\n type RunStatus,\n} from './runtime/run-state.js';\nexport { createSseRoutes, type SseRoutesDeps } from './sse/index.js';\nexport {\n type CreateTriggersDaemonOptions,\n createTriggersDaemon,\n createTriggersRoutes,\n defaultCatchupPolicy,\n type TriggersDaemon,\n type TriggersDaemonMetrics,\n type TriggersDaemonStatus,\n type TriggersRoutesDeps,\n} from './triggers/index.js';\nexport {\n type BareEventFrame,\n createReplayBuffer,\n createWsDispatcher,\n createWsTicketStore,\n createWsUpgradeEvents,\n type ParsedSubject,\n type ReplayBuffer,\n type ReplayBufferOptions,\n type ReplayBufferSlice,\n requiredScopeFor,\n type SubscribeResult,\n tryParseSubject,\n type WsDispatcher,\n type WsDispatcherOptions,\n type WsDispatcherWarning,\n type WsSubscriberHandle,\n type WsSubscriptionSnapshot,\n type WsTicket,\n type WsTicketConsumeResult,\n type WsTicketStore,\n type WsTicketStoreOptions,\n type WsUpgradeOptions,\n} from './ws/index.js';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,MAAaA,UAAkBC"}
@@ -8,7 +8,7 @@ import { randomUUID } from "node:crypto";
8
8
  */
9
9
  /**
10
10
  * Generate an opaque per-request id. UUID v4 keeps the surface area
11
- * minimal operators that already pin a different scheme (Datadog
11
+ * minimal - operators that already pin a different scheme (Datadog
12
12
  * trace ids, AWS request ids, …) can override the generator on
13
13
  * `createServer({ requestIdGenerator })`.
14
14
  */
@@ -1 +1 @@
1
- {"version":3,"file":"ids.js","names":[],"sources":["../../src/internal/ids.ts"],"sourcesContent":["/**\n * Tiny id helpers shared across middleware + routes.\n *\n * @internal\n */\n\nimport { randomUUID } from 'node:crypto';\n\n/**\n * Generate an opaque per-request id. UUID v4 keeps the surface area\n * minimal operators that already pin a different scheme (Datadog\n * trace ids, AWS request ids, …) can override the generator on\n * `createServer({ requestIdGenerator })`.\n */\nexport function newRequestId(): string {\n return randomUUID();\n}\n"],"mappings":";;;;;;;;;;;;;;AAcA,SAAgB,eAAuB;AACrC,QAAO,YAAY"}
1
+ {"version":3,"file":"ids.js","names":[],"sources":["../../src/internal/ids.ts"],"sourcesContent":["/**\n * Tiny id helpers shared across middleware + routes.\n *\n * @internal\n */\n\nimport { randomUUID } from 'node:crypto';\n\n/**\n * Generate an opaque per-request id. UUID v4 keeps the surface area\n * minimal - operators that already pin a different scheme (Datadog\n * trace ids, AWS request ids, …) can override the generator on\n * `createServer({ requestIdGenerator })`.\n */\nexport function newRequestId(): string {\n return randomUUID();\n}\n"],"mappings":";;;;;;;;;;;;;;AAcA,SAAgB,eAAuB;AACrC,QAAO,YAAY"}
@@ -1 +1 @@
1
- {"version":3,"file":"pre-bind.js","names":["out: { -readonly [K in keyof PreBindResult]?: PreBindResult[K] }"],"sources":["../../src/lifecycle/pre-bind.ts"],"sourcesContent":["/**\n * Pre-bind validation. Runs *before* the HTTP listener attaches so a\n * misconfigured server never accepts a single request.\n *\n * Steps (fail-fast on every failure):\n *\n * 1. Resolve every `*Ref` field on the config through the\n * `@graphorin/security` resolver registry. Missing ref → exit 1.\n * 2. Confirm the server pepper resolved to non-empty bytes.\n * 3. Confirm the encryption peer is loadable when\n * `storage.encryption.enabled === true` (or `audit.enabled === true`).\n * 4. Run pending storage migrations failures bubble up as\n * `MigrationFailedError`.\n *\n * The function is idempotent calling it more than once for the\n * same {@link createSqliteStore} handle re-validates the secrets but\n * leaves the underlying database untouched.\n *\n * @packageDocumentation\n */\n\nimport type { SecretValue } from '@graphorin/security';\n\nimport { resolveSecret } from '@graphorin/security/secrets';\nimport type { GraphorinSqliteStore } from '@graphorin/store-sqlite';\n\nimport type { ServerConfigSpec } from '../config.js';\nimport {\n MigrationFailedError,\n PrebindEncryptionPeerMissingError,\n PrebindEncryptionRequiredError,\n PrebindPepperMissingError,\n PrebindSecretUnresolvableError,\n} from '../errors/index.js';\n\n/**\n * Result returned by {@link runPreBind}. Consumers (`createServer`,\n * tests) consume the resolved pepper + encryption decision when\n * wiring the rest of the server.\n *\n * @stable\n */\nexport interface PreBindResult {\n readonly pepper?: SecretValue;\n readonly auditPath?: string;\n readonly auditPassphrase?: SecretValue;\n readonly storagePassphrase?: SecretValue;\n}\n\n/**\n * @stable\n */\nexport interface RunPreBindOptions {\n readonly config: ServerConfigSpec;\n readonly store: GraphorinSqliteStore;\n /**\n * Optional override for the cipher-peer probe. Tests inject a stub\n * that signals \"missing peer\" without uninstalling the real one.\n */\n readonly probeCipherPeer?: () => Promise<void>;\n}\n\nasync function resolveRef(path: ReadonlyArray<string | number>, raw: string): Promise<SecretValue> {\n try {\n return await resolveSecret(raw);\n } catch (err) {\n throw new PrebindSecretUnresolvableError(path, raw, err);\n }\n}\n\n/**\n * @stable\n */\nexport async function runPreBind(options: RunPreBindOptions): Promise<PreBindResult> {\n const { config, store } = options;\n const out: { -readonly [K in keyof PreBindResult]?: PreBindResult[K] } = {};\n\n if (config.auth.kind === 'token') {\n if (config.auth.pepperRef === undefined || config.auth.pepperRef.length === 0) {\n throw new PrebindPepperMissingError();\n }\n const pepper = await resolveRef(['auth', 'pepperRef'], config.auth.pepperRef);\n const buf = await pepper.useBuffer((b) => Buffer.from(b));\n if (buf.length === 0) {\n throw new PrebindPepperMissingError();\n }\n out.pepper = pepper;\n }\n\n if (config.storage.encryption.enabled) {\n if (config.storage.encryption.passphraseRef === undefined) {\n throw new PrebindEncryptionRequiredError(\n 'storage.encryption.enabled is true but no passphraseRef is configured.',\n );\n }\n out.storagePassphrase = await resolveRef(\n ['storage', 'encryption', 'passphraseRef'],\n config.storage.encryption.passphraseRef,\n );\n if (options.probeCipherPeer !== undefined) {\n try {\n await options.probeCipherPeer();\n } catch (err) {\n throw new PrebindEncryptionPeerMissingError(err);\n }\n }\n }\n\n if (config.audit.enabled) {\n const passphraseRef = config.audit.passphraseRef ?? config.storage.encryption.passphraseRef;\n if (passphraseRef === undefined) {\n throw new PrebindEncryptionRequiredError(\n 'audit.enabled is true but no audit.passphraseRef (or storage.encryption.passphraseRef) is configured. Audit logs are mandatory-encrypted (DEC-124).',\n );\n }\n out.auditPath = config.audit.path ?? deriveAuditPath(config.storage.path);\n out.auditPassphrase = await resolveRef(['audit', 'passphraseRef'], passphraseRef);\n }\n\n try {\n await store.init();\n } catch (err) {\n throw new MigrationFailedError(`Storage migrations failed: ${describeError(err)}`, err);\n }\n\n return Object.freeze(out as PreBindResult);\n}\n\nfunction deriveAuditPath(storagePath: string): string {\n const idx = storagePath.lastIndexOf('/');\n if (idx < 0) return 'audit.db';\n return `${storagePath.slice(0, idx)}/audit.db`;\n}\n\nfunction describeError(err: unknown): string {\n if (err instanceof Error) return err.message;\n return String(err);\n}\n"],"mappings":";;;;AA8DA,eAAe,WAAW,MAAsC,KAAmC;AACjG,KAAI;AACF,SAAO,MAAM,cAAc,IAAI;UACxB,KAAK;AACZ,QAAM,IAAI,+BAA+B,MAAM,KAAK,IAAI;;;;;;AAO5D,eAAsB,WAAW,SAAoD;CACnF,MAAM,EAAE,QAAQ,UAAU;CAC1B,MAAMA,MAAmE,EAAE;AAE3E,KAAI,OAAO,KAAK,SAAS,SAAS;AAChC,MAAI,OAAO,KAAK,cAAc,UAAa,OAAO,KAAK,UAAU,WAAW,EAC1E,OAAM,IAAI,2BAA2B;EAEvC,MAAM,SAAS,MAAM,WAAW,CAAC,QAAQ,YAAY,EAAE,OAAO,KAAK,UAAU;AAE7E,OADY,MAAM,OAAO,WAAW,MAAM,OAAO,KAAK,EAAE,CAAC,EACjD,WAAW,EACjB,OAAM,IAAI,2BAA2B;AAEvC,MAAI,SAAS;;AAGf,KAAI,OAAO,QAAQ,WAAW,SAAS;AACrC,MAAI,OAAO,QAAQ,WAAW,kBAAkB,OAC9C,OAAM,IAAI,+BACR,yEACD;AAEH,MAAI,oBAAoB,MAAM,WAC5B;GAAC;GAAW;GAAc;GAAgB,EAC1C,OAAO,QAAQ,WAAW,cAC3B;AACD,MAAI,QAAQ,oBAAoB,OAC9B,KAAI;AACF,SAAM,QAAQ,iBAAiB;WACxB,KAAK;AACZ,SAAM,IAAI,kCAAkC,IAAI;;;AAKtD,KAAI,OAAO,MAAM,SAAS;EACxB,MAAM,gBAAgB,OAAO,MAAM,iBAAiB,OAAO,QAAQ,WAAW;AAC9E,MAAI,kBAAkB,OACpB,OAAM,IAAI,+BACR,sJACD;AAEH,MAAI,YAAY,OAAO,MAAM,QAAQ,gBAAgB,OAAO,QAAQ,KAAK;AACzE,MAAI,kBAAkB,MAAM,WAAW,CAAC,SAAS,gBAAgB,EAAE,cAAc;;AAGnF,KAAI;AACF,QAAM,MAAM,MAAM;UACX,KAAK;AACZ,QAAM,IAAI,qBAAqB,8BAA8B,cAAc,IAAI,IAAI,IAAI;;AAGzF,QAAO,OAAO,OAAO,IAAqB;;AAG5C,SAAS,gBAAgB,aAA6B;CACpD,MAAM,MAAM,YAAY,YAAY,IAAI;AACxC,KAAI,MAAM,EAAG,QAAO;AACpB,QAAO,GAAG,YAAY,MAAM,GAAG,IAAI,CAAC;;AAGtC,SAAS,cAAc,KAAsB;AAC3C,KAAI,eAAe,MAAO,QAAO,IAAI;AACrC,QAAO,OAAO,IAAI"}
1
+ {"version":3,"file":"pre-bind.js","names":["out: { -readonly [K in keyof PreBindResult]?: PreBindResult[K] }"],"sources":["../../src/lifecycle/pre-bind.ts"],"sourcesContent":["/**\n * Pre-bind validation. Runs *before* the HTTP listener attaches so a\n * misconfigured server never accepts a single request.\n *\n * Steps (fail-fast on every failure):\n *\n * 1. Resolve every `*Ref` field on the config through the\n * `@graphorin/security` resolver registry. Missing ref → exit 1.\n * 2. Confirm the server pepper resolved to non-empty bytes.\n * 3. Confirm the encryption peer is loadable when\n * `storage.encryption.enabled === true` (or `audit.enabled === true`).\n * 4. Run pending storage migrations - failures bubble up as\n * `MigrationFailedError`.\n *\n * The function is idempotent - calling it more than once for the\n * same {@link createSqliteStore} handle re-validates the secrets but\n * leaves the underlying database untouched.\n *\n * @packageDocumentation\n */\n\nimport type { SecretValue } from '@graphorin/security';\n\nimport { resolveSecret } from '@graphorin/security/secrets';\nimport type { GraphorinSqliteStore } from '@graphorin/store-sqlite';\n\nimport type { ServerConfigSpec } from '../config.js';\nimport {\n MigrationFailedError,\n PrebindEncryptionPeerMissingError,\n PrebindEncryptionRequiredError,\n PrebindPepperMissingError,\n PrebindSecretUnresolvableError,\n} from '../errors/index.js';\n\n/**\n * Result returned by {@link runPreBind}. Consumers (`createServer`,\n * tests) consume the resolved pepper + encryption decision when\n * wiring the rest of the server.\n *\n * @stable\n */\nexport interface PreBindResult {\n readonly pepper?: SecretValue;\n readonly auditPath?: string;\n readonly auditPassphrase?: SecretValue;\n readonly storagePassphrase?: SecretValue;\n}\n\n/**\n * @stable\n */\nexport interface RunPreBindOptions {\n readonly config: ServerConfigSpec;\n readonly store: GraphorinSqliteStore;\n /**\n * Optional override for the cipher-peer probe. Tests inject a stub\n * that signals \"missing peer\" without uninstalling the real one.\n */\n readonly probeCipherPeer?: () => Promise<void>;\n}\n\nasync function resolveRef(path: ReadonlyArray<string | number>, raw: string): Promise<SecretValue> {\n try {\n return await resolveSecret(raw);\n } catch (err) {\n throw new PrebindSecretUnresolvableError(path, raw, err);\n }\n}\n\n/**\n * @stable\n */\nexport async function runPreBind(options: RunPreBindOptions): Promise<PreBindResult> {\n const { config, store } = options;\n const out: { -readonly [K in keyof PreBindResult]?: PreBindResult[K] } = {};\n\n if (config.auth.kind === 'token') {\n if (config.auth.pepperRef === undefined || config.auth.pepperRef.length === 0) {\n throw new PrebindPepperMissingError();\n }\n const pepper = await resolveRef(['auth', 'pepperRef'], config.auth.pepperRef);\n const buf = await pepper.useBuffer((b) => Buffer.from(b));\n if (buf.length === 0) {\n throw new PrebindPepperMissingError();\n }\n out.pepper = pepper;\n }\n\n if (config.storage.encryption.enabled) {\n if (config.storage.encryption.passphraseRef === undefined) {\n throw new PrebindEncryptionRequiredError(\n 'storage.encryption.enabled is true but no passphraseRef is configured.',\n );\n }\n out.storagePassphrase = await resolveRef(\n ['storage', 'encryption', 'passphraseRef'],\n config.storage.encryption.passphraseRef,\n );\n if (options.probeCipherPeer !== undefined) {\n try {\n await options.probeCipherPeer();\n } catch (err) {\n throw new PrebindEncryptionPeerMissingError(err);\n }\n }\n }\n\n if (config.audit.enabled) {\n const passphraseRef = config.audit.passphraseRef ?? config.storage.encryption.passphraseRef;\n if (passphraseRef === undefined) {\n throw new PrebindEncryptionRequiredError(\n 'audit.enabled is true but no audit.passphraseRef (or storage.encryption.passphraseRef) is configured. Audit logs are mandatory-encrypted (DEC-124).',\n );\n }\n out.auditPath = config.audit.path ?? deriveAuditPath(config.storage.path);\n out.auditPassphrase = await resolveRef(['audit', 'passphraseRef'], passphraseRef);\n }\n\n try {\n await store.init();\n } catch (err) {\n throw new MigrationFailedError(`Storage migrations failed: ${describeError(err)}`, err);\n }\n\n return Object.freeze(out as PreBindResult);\n}\n\nfunction deriveAuditPath(storagePath: string): string {\n const idx = storagePath.lastIndexOf('/');\n if (idx < 0) return 'audit.db';\n return `${storagePath.slice(0, idx)}/audit.db`;\n}\n\nfunction describeError(err: unknown): string {\n if (err instanceof Error) return err.message;\n return String(err);\n}\n"],"mappings":";;;;AA8DA,eAAe,WAAW,MAAsC,KAAmC;AACjG,KAAI;AACF,SAAO,MAAM,cAAc,IAAI;UACxB,KAAK;AACZ,QAAM,IAAI,+BAA+B,MAAM,KAAK,IAAI;;;;;;AAO5D,eAAsB,WAAW,SAAoD;CACnF,MAAM,EAAE,QAAQ,UAAU;CAC1B,MAAMA,MAAmE,EAAE;AAE3E,KAAI,OAAO,KAAK,SAAS,SAAS;AAChC,MAAI,OAAO,KAAK,cAAc,UAAa,OAAO,KAAK,UAAU,WAAW,EAC1E,OAAM,IAAI,2BAA2B;EAEvC,MAAM,SAAS,MAAM,WAAW,CAAC,QAAQ,YAAY,EAAE,OAAO,KAAK,UAAU;AAE7E,OADY,MAAM,OAAO,WAAW,MAAM,OAAO,KAAK,EAAE,CAAC,EACjD,WAAW,EACjB,OAAM,IAAI,2BAA2B;AAEvC,MAAI,SAAS;;AAGf,KAAI,OAAO,QAAQ,WAAW,SAAS;AACrC,MAAI,OAAO,QAAQ,WAAW,kBAAkB,OAC9C,OAAM,IAAI,+BACR,yEACD;AAEH,MAAI,oBAAoB,MAAM,WAC5B;GAAC;GAAW;GAAc;GAAgB,EAC1C,OAAO,QAAQ,WAAW,cAC3B;AACD,MAAI,QAAQ,oBAAoB,OAC9B,KAAI;AACF,SAAM,QAAQ,iBAAiB;WACxB,KAAK;AACZ,SAAM,IAAI,kCAAkC,IAAI;;;AAKtD,KAAI,OAAO,MAAM,SAAS;EACxB,MAAM,gBAAgB,OAAO,MAAM,iBAAiB,OAAO,QAAQ,WAAW;AAC9E,MAAI,kBAAkB,OACpB,OAAM,IAAI,+BACR,sJACD;AAEH,MAAI,YAAY,OAAO,MAAM,QAAQ,gBAAgB,OAAO,QAAQ,KAAK;AACzE,MAAI,kBAAkB,MAAM,WAAW,CAAC,SAAS,gBAAgB,EAAE,cAAc;;AAGnF,KAAI;AACF,QAAM,MAAM,MAAM;UACX,KAAK;AACZ,QAAM,IAAI,qBAAqB,8BAA8B,cAAc,IAAI,IAAI,IAAI;;AAGzF,QAAO,OAAO,OAAO,IAAqB;;AAG5C,SAAS,gBAAgB,aAA6B;CACpD,MAAM,MAAM,YAAY,YAAY,IAAI;AACxC,KAAI,MAAM,EAAG,QAAO;AACpB,QAAO,GAAG,YAAY,MAAM,GAAG,IAAI,CAAC;;AAGtC,SAAS,cAAc,KAAsB;AAC3C,KAAI,eAAe,MAAO,QAAO,IAAI;AACrC,QAAO,OAAO,IAAI"}
@@ -8,7 +8,7 @@ import { MetricRegistry } from "./registry.js";
8
8
  *
9
9
  * Metric naming follows the documented `graphorin_*` prefix
10
10
  * discipline; label cardinality is bounded by construction (no PII
11
- * labels see DEC-141 cross-cut).
11
+ * labels - see DEC-141 cross-cut).
12
12
  *
13
13
  * @packageDocumentation
14
14
  */
@@ -1 +1 @@
1
- {"version":3,"file":"catalog.js","names":[],"sources":["../../src/metrics/catalog.ts"],"sourcesContent":["/**\n * Canonical metric inventory for the standalone server. Every metric\n * the framework emits is registered here in one place so the\n * exposition output is deterministic across builds.\n *\n * Metric naming follows the documented `graphorin_*` prefix\n * discipline; label cardinality is bounded by construction (no PII\n * labels see DEC-141 cross-cut).\n *\n * @packageDocumentation\n */\n\nimport { MetricRegistry } from './registry.js';\n\n/**\n * @stable\n */\n// IP-15: the catalog lists only metrics the server actually moves. Five\n// previously-registered series `graphorin_tool_calls_total`,\n// `graphorin_provider_tokens_total`, `graphorin_provider_cost_usd_total`,\n// `graphorin_redaction_drops_total` and `graphorin_oauth_tokens_freshness_seconds`\n// had no producer anywhere in the monorepo (per-tool and provider usage live\n// in the agent runtime, not the server; redaction drops in the observability\n// layer; OAuth freshness needs an MCP token store the server does not own). A\n// permanently-empty series is worse than an absent one for dashboards, so they\n// are dropped until a real producer exists rather than advertised inert.\nexport const SERVER_METRIC_NAMES = Object.freeze({\n agentRunsTotal: 'graphorin_agent_runs_total',\n agentRunDuration: 'graphorin_agent_run_duration_seconds',\n storageWalSize: 'graphorin_storage_wal_size_bytes',\n idempotencyCacheHitRatio: 'graphorin_idempotency_cache_hit_ratio',\n triggersFiresTotal: 'graphorin_triggers_fires_total',\n consolidatorQueueDepth: 'graphorin_consolidator_queue_depth',\n consolidatorDlqSize: 'graphorin_consolidator_dlq_size',\n consolidatorBudgetRemainingUsd: 'graphorin_consolidator_budget_remaining_usd',\n consolidatorBudgetRemainingTokens: 'graphorin_consolidator_budget_remaining_tokens',\n replayBufferEvents: 'graphorin_replay_buffer_events',\n inflightRuns: 'graphorin_inflight_runs',\n serverUptime: 'graphorin_server_uptime_seconds',\n buildInfo: 'graphorin_build_info',\n} as const);\n\n/**\n * Build a fully-registered {@link MetricRegistry} ready for the\n * `/v1/metrics` exposition. The returned registry has every metric\n * declared but no samples; the server runtime updates samples\n * incrementally.\n *\n * @stable\n */\nexport function createServerMetricRegistry(): MetricRegistry {\n const registry = new MetricRegistry();\n\n registry.registerCounter(\n SERVER_METRIC_NAMES.agentRunsTotal,\n 'Total agent runs by terminal status.',\n ['status'],\n );\n registry.registerSummary(\n SERVER_METRIC_NAMES.agentRunDuration,\n 'Agent run duration in seconds.',\n [],\n );\n registry.registerGauge(SERVER_METRIC_NAMES.storageWalSize, 'Current SQLite WAL size in bytes.');\n registry.registerGauge(\n SERVER_METRIC_NAMES.idempotencyCacheHitRatio,\n 'Idempotency LRU cache hit ratio in [0, 1].',\n );\n registry.registerCounter(\n SERVER_METRIC_NAMES.triggersFiresTotal,\n 'Total trigger fires by trigger id and outcome.',\n ['trigger_id', 'status'],\n );\n registry.registerGauge(\n SERVER_METRIC_NAMES.consolidatorQueueDepth,\n 'Pending consolidator items by phase.',\n ['phase'],\n );\n registry.registerGauge(\n SERVER_METRIC_NAMES.consolidatorDlqSize,\n 'Number of consolidator runs sitting in the dead-letter queue.',\n );\n registry.registerGauge(\n SERVER_METRIC_NAMES.consolidatorBudgetRemainingUsd,\n 'Consolidator USD budget remaining for the current period.',\n );\n registry.registerGauge(\n SERVER_METRIC_NAMES.consolidatorBudgetRemainingTokens,\n 'Consolidator token budget remaining for the current period.',\n );\n registry.registerGauge(\n SERVER_METRIC_NAMES.replayBufferEvents,\n 'Number of events currently buffered for WebSocket replay.',\n );\n registry.registerGauge(\n SERVER_METRIC_NAMES.inflightRuns,\n 'Number of in-flight agent / workflow runs.',\n );\n registry.registerGauge(SERVER_METRIC_NAMES.serverUptime, 'Server process uptime in seconds.');\n registry.registerGauge(\n SERVER_METRIC_NAMES.buildInfo,\n 'Static `1` gauge carrying graphorin build labels.',\n ['version'],\n );\n\n return registry;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA0BA,MAAa,sBAAsB,OAAO,OAAO;CAC/C,gBAAgB;CAChB,kBAAkB;CAClB,gBAAgB;CAChB,0BAA0B;CAC1B,oBAAoB;CACpB,wBAAwB;CACxB,qBAAqB;CACrB,gCAAgC;CAChC,mCAAmC;CACnC,oBAAoB;CACpB,cAAc;CACd,cAAc;CACd,WAAW;CACZ,CAAU;;;;;;;;;AAUX,SAAgB,6BAA6C;CAC3D,MAAM,WAAW,IAAI,gBAAgB;AAErC,UAAS,gBACP,oBAAoB,gBACpB,wCACA,CAAC,SAAS,CACX;AACD,UAAS,gBACP,oBAAoB,kBACpB,kCACA,EAAE,CACH;AACD,UAAS,cAAc,oBAAoB,gBAAgB,oCAAoC;AAC/F,UAAS,cACP,oBAAoB,0BACpB,6CACD;AACD,UAAS,gBACP,oBAAoB,oBACpB,kDACA,CAAC,cAAc,SAAS,CACzB;AACD,UAAS,cACP,oBAAoB,wBACpB,wCACA,CAAC,QAAQ,CACV;AACD,UAAS,cACP,oBAAoB,qBACpB,gEACD;AACD,UAAS,cACP,oBAAoB,gCACpB,4DACD;AACD,UAAS,cACP,oBAAoB,mCACpB,8DACD;AACD,UAAS,cACP,oBAAoB,oBACpB,4DACD;AACD,UAAS,cACP,oBAAoB,cACpB,6CACD;AACD,UAAS,cAAc,oBAAoB,cAAc,oCAAoC;AAC7F,UAAS,cACP,oBAAoB,WACpB,qDACA,CAAC,UAAU,CACZ;AAED,QAAO"}
1
+ {"version":3,"file":"catalog.js","names":[],"sources":["../../src/metrics/catalog.ts"],"sourcesContent":["/**\n * Canonical metric inventory for the standalone server. Every metric\n * the framework emits is registered here in one place so the\n * exposition output is deterministic across builds.\n *\n * Metric naming follows the documented `graphorin_*` prefix\n * discipline; label cardinality is bounded by construction (no PII\n * labels - see DEC-141 cross-cut).\n *\n * @packageDocumentation\n */\n\nimport { MetricRegistry } from './registry.js';\n\n/**\n * @stable\n */\n// IP-15: the catalog lists only metrics the server actually moves. Five\n// previously-registered series - `graphorin_tool_calls_total`,\n// `graphorin_provider_tokens_total`, `graphorin_provider_cost_usd_total`,\n// `graphorin_redaction_drops_total` and `graphorin_oauth_tokens_freshness_seconds`\n// - had no producer anywhere in the monorepo (per-tool and provider usage live\n// in the agent runtime, not the server; redaction drops in the observability\n// layer; OAuth freshness needs an MCP token store the server does not own). A\n// permanently-empty series is worse than an absent one for dashboards, so they\n// are dropped until a real producer exists rather than advertised inert.\nexport const SERVER_METRIC_NAMES = Object.freeze({\n agentRunsTotal: 'graphorin_agent_runs_total',\n agentRunDuration: 'graphorin_agent_run_duration_seconds',\n storageWalSize: 'graphorin_storage_wal_size_bytes',\n idempotencyCacheHitRatio: 'graphorin_idempotency_cache_hit_ratio',\n triggersFiresTotal: 'graphorin_triggers_fires_total',\n consolidatorQueueDepth: 'graphorin_consolidator_queue_depth',\n consolidatorDlqSize: 'graphorin_consolidator_dlq_size',\n consolidatorBudgetRemainingUsd: 'graphorin_consolidator_budget_remaining_usd',\n consolidatorBudgetRemainingTokens: 'graphorin_consolidator_budget_remaining_tokens',\n replayBufferEvents: 'graphorin_replay_buffer_events',\n inflightRuns: 'graphorin_inflight_runs',\n serverUptime: 'graphorin_server_uptime_seconds',\n buildInfo: 'graphorin_build_info',\n} as const);\n\n/**\n * Build a fully-registered {@link MetricRegistry} ready for the\n * `/v1/metrics` exposition. The returned registry has every metric\n * declared but no samples; the server runtime updates samples\n * incrementally.\n *\n * @stable\n */\nexport function createServerMetricRegistry(): MetricRegistry {\n const registry = new MetricRegistry();\n\n registry.registerCounter(\n SERVER_METRIC_NAMES.agentRunsTotal,\n 'Total agent runs by terminal status.',\n ['status'],\n );\n registry.registerSummary(\n SERVER_METRIC_NAMES.agentRunDuration,\n 'Agent run duration in seconds.',\n [],\n );\n registry.registerGauge(SERVER_METRIC_NAMES.storageWalSize, 'Current SQLite WAL size in bytes.');\n registry.registerGauge(\n SERVER_METRIC_NAMES.idempotencyCacheHitRatio,\n 'Idempotency LRU cache hit ratio in [0, 1].',\n );\n registry.registerCounter(\n SERVER_METRIC_NAMES.triggersFiresTotal,\n 'Total trigger fires by trigger id and outcome.',\n ['trigger_id', 'status'],\n );\n registry.registerGauge(\n SERVER_METRIC_NAMES.consolidatorQueueDepth,\n 'Pending consolidator items by phase.',\n ['phase'],\n );\n registry.registerGauge(\n SERVER_METRIC_NAMES.consolidatorDlqSize,\n 'Number of consolidator runs sitting in the dead-letter queue.',\n );\n registry.registerGauge(\n SERVER_METRIC_NAMES.consolidatorBudgetRemainingUsd,\n 'Consolidator USD budget remaining for the current period.',\n );\n registry.registerGauge(\n SERVER_METRIC_NAMES.consolidatorBudgetRemainingTokens,\n 'Consolidator token budget remaining for the current period.',\n );\n registry.registerGauge(\n SERVER_METRIC_NAMES.replayBufferEvents,\n 'Number of events currently buffered for WebSocket replay.',\n );\n registry.registerGauge(\n SERVER_METRIC_NAMES.inflightRuns,\n 'Number of in-flight agent / workflow runs.',\n );\n registry.registerGauge(SERVER_METRIC_NAMES.serverUptime, 'Server process uptime in seconds.');\n registry.registerGauge(\n SERVER_METRIC_NAMES.buildInfo,\n 'Static `1` gauge carrying graphorin build labels.',\n ['version'],\n );\n\n return registry;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA0BA,MAAa,sBAAsB,OAAO,OAAO;CAC/C,gBAAgB;CAChB,kBAAkB;CAClB,gBAAgB;CAChB,0BAA0B;CAC1B,oBAAoB;CACpB,wBAAwB;CACxB,qBAAqB;CACrB,gCAAgC;CAChC,mCAAmC;CACnC,oBAAoB;CACpB,cAAc;CACd,cAAc;CACd,WAAW;CACZ,CAAU;;;;;;;;;AAUX,SAAgB,6BAA6C;CAC3D,MAAM,WAAW,IAAI,gBAAgB;AAErC,UAAS,gBACP,oBAAoB,gBACpB,wCACA,CAAC,SAAS,CACX;AACD,UAAS,gBACP,oBAAoB,kBACpB,kCACA,EAAE,CACH;AACD,UAAS,cAAc,oBAAoB,gBAAgB,oCAAoC;AAC/F,UAAS,cACP,oBAAoB,0BACpB,6CACD;AACD,UAAS,gBACP,oBAAoB,oBACpB,kDACA,CAAC,cAAc,SAAS,CACzB;AACD,UAAS,cACP,oBAAoB,wBACpB,wCACA,CAAC,QAAQ,CACV;AACD,UAAS,cACP,oBAAoB,qBACpB,gEACD;AACD,UAAS,cACP,oBAAoB,gCACpB,4DACD;AACD,UAAS,cACP,oBAAoB,mCACpB,8DACD;AACD,UAAS,cACP,oBAAoB,oBACpB,4DACD;AACD,UAAS,cACP,oBAAoB,cACpB,6CACD;AACD,UAAS,cAAc,oBAAoB,cAAc,oCAAoC;AAC7F,UAAS,cACP,oBAAoB,WACpB,qDACA,CAAC,UAAU,CACZ;AAED,QAAO"}
@@ -8,7 +8,7 @@
8
8
  * The supported metric kinds are the three the runtime spec calls
9
9
  * out: `counter`, `gauge`, and `summary` (with hard-coded p50/p95
10
10
  * quantiles). Sample rendering follows Prometheus text exposition
11
- * format v0.0.4 every metric block is preceded by `# HELP` + `# TYPE`
11
+ * format v0.0.4 - every metric block is preceded by `# HELP` + `# TYPE`
12
12
  * and labels are quoted-escaped per the spec.
13
13
  *
14
14
  * @packageDocumentation
@@ -35,7 +35,7 @@ declare class MetricRegistry {
35
35
  reset(): void;
36
36
  /**
37
37
  * Render the current snapshot in Prometheus text exposition
38
- * format (v0.0.4). Never throws incomplete sample buckets are
38
+ * format (v0.0.4). Never throws - incomplete sample buckets are
39
39
  * skipped instead of failing the scrape.
40
40
  */
41
41
  render(): string;
@@ -87,7 +87,7 @@ var MetricRegistry = class {
87
87
  }
88
88
  /**
89
89
  * Render the current snapshot in Prometheus text exposition
90
- * format (v0.0.4). Never throws incomplete sample buckets are
90
+ * format (v0.0.4). Never throws - incomplete sample buckets are
91
91
  * skipped instead of failing the scrape.
92
92
  */
93
93
  render() {