@obsunified/collector 2.0.1 → 2.2.0

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 (126) hide show
  1. package/dist/auth.d.ts +1 -1
  2. package/dist/auth.js +1 -1
  3. package/dist/{chunk-UGWDOGGH.js → chunk-KMEZKGSO.js} +22 -10
  4. package/dist/chunk-KMEZKGSO.js.map +1 -0
  5. package/dist/{chunk-XKQTVIRE.js → chunk-ZSF5XP3B.js} +8 -2
  6. package/dist/chunk-ZSF5XP3B.js.map +1 -0
  7. package/dist/index.d.ts +630 -27
  8. package/dist/index.js +5819 -853
  9. package/dist/index.js.map +1 -1
  10. package/dist/{ingest-auth-HFVO1zIp.d.ts → ingest-auth-C5-8Mly5.d.ts} +9 -0
  11. package/dist/{openai-FJYOQ7WO.js → openai-GSJZGFGL.js} +2 -2
  12. package/package.json +5 -5
  13. package/src/__tests__/autosre-closed-loop.test.ts +1275 -0
  14. package/src/analyses/derive.ts +26 -25
  15. package/src/analyses/investigations.ts +8 -8
  16. package/src/analyses/tier0.ts +22 -22
  17. package/src/auth/dashboard-auth.test.ts +791 -0
  18. package/src/auth/dashboard-auth.ts +187 -38
  19. package/src/auth/login-rate-limit.test.ts +129 -0
  20. package/src/auth/login-rate-limit.ts +143 -0
  21. package/src/framework/body-limit.test.ts +165 -0
  22. package/src/framework/collector.ts +355 -115
  23. package/src/framework/env.ts +9 -0
  24. package/src/framework/limits.ts +31 -0
  25. package/src/framework/scheduled-handlers.test.ts +342 -0
  26. package/src/index.ts +59 -1
  27. package/src/lib/action-retention.test.ts +472 -0
  28. package/src/lib/action-retention.ts +420 -0
  29. package/src/lib/actions-store.ts +6 -12
  30. package/src/lib/ai-store/calls.ts +2 -2
  31. package/src/lib/ai-store/sessions.ts +3 -3
  32. package/src/lib/ai-store/spans.ts +1 -1
  33. package/src/lib/ai-store/types.ts +2 -1
  34. package/src/lib/alerts-store.test.ts +153 -0
  35. package/src/lib/alerts-store.ts +122 -21
  36. package/src/lib/analyses-runner.test.ts +75 -0
  37. package/src/lib/analyses-runner.ts +69 -3
  38. package/src/lib/analyses-store-latest.test.ts +61 -0
  39. package/src/lib/analyses-store.ts +17 -10
  40. package/src/lib/autosre-change-correlation.ts +780 -0
  41. package/src/lib/autosre-findings.ts +725 -0
  42. package/src/lib/autosre-ontology.ts +914 -0
  43. package/src/lib/autosre-remediation-gate.ts +1663 -0
  44. package/src/lib/blob-store.ts +1 -1
  45. package/src/lib/eval-cases-store.ts +63 -1
  46. package/src/lib/evidence-references.ts +6 -0
  47. package/src/lib/housekeeping.test.ts +153 -0
  48. package/src/lib/housekeeping.ts +193 -0
  49. package/src/lib/identity-index/action-lookups.ts +6 -6
  50. package/src/lib/identity-index/key-lookups.ts +2 -2
  51. package/src/lib/identity-index/user-lookup.ts +8 -5
  52. package/src/lib/identity-index.test.ts +2 -3
  53. package/src/lib/metrics-store-series.test.ts +99 -0
  54. package/src/lib/metrics-store.test.ts +23 -0
  55. package/src/lib/metrics-store.ts +27 -3
  56. package/src/lib/projects-store.test.ts +25 -0
  57. package/src/lib/projects-store.ts +4 -3
  58. package/src/lib/sql-db-dialect.test.ts +12 -3
  59. package/src/lib/sql-db-postgres.integration.test.ts +1225 -0
  60. package/src/lib/sql-db-postgres.test.ts +314 -0
  61. package/src/lib/sql-db-postgres.ts +392 -54
  62. package/src/lib/sql-db-sqlite-time-windows.test.ts +194 -0
  63. package/src/lib/sql-db.ts +40 -6
  64. package/src/lib/store/ingest.ts +94 -6
  65. package/src/lib/test-utils/action-graph-fixtures.ts +307 -0
  66. package/src/lib/test-utils/node-sqlite-db.ts +188 -0
  67. package/src/lib/webhook-url.test.ts +122 -0
  68. package/src/lib/webhook-url.ts +155 -0
  69. package/src/migrations/041_ingest_key_hash_text.sql +5 -0
  70. package/src/migrations/042_session_replay_project_composite_pk.sql +52 -0
  71. package/src/migrations/043_metric_exemplars_text_timestamps.sql +5 -0
  72. package/src/migrations/044_actions_started_at_index.sql +10 -0
  73. package/src/migrations/045_user_profiles_project_composite_pk.sql +40 -0
  74. package/src/migrations/046_action_graph_project_composite_pk.sql +255 -0
  75. package/src/migrations/047_ai_span_payloads_project_composite_pk.sql +61 -0
  76. package/src/migrations/048_telemetry_spans_project_composite_pk.sql +92 -0
  77. package/src/migrations/049_alert_state_notified.sql +17 -0
  78. package/src/migrations/050_autosre_causal_ontology.sql +216 -0
  79. package/src/migrations/alert-state-notified.test.ts +111 -0
  80. package/src/migrations/parity.test.ts +68 -0
  81. package/src/migrations/project-scoped-keys.test.ts +432 -0
  82. package/src/migrations-postgres/009_projects_and_keys.sql +4 -4
  83. package/src/migrations-postgres/026_obs_dashboard_project_seed.sql +1 -1
  84. package/src/migrations-postgres/035_metric_exemplars.sql +5 -2
  85. package/src/migrations-postgres/041_ingest_key_hash_text.sql +32 -0
  86. package/src/migrations-postgres/042_session_replay_project_composite_pk.sql +16 -0
  87. package/src/migrations-postgres/043_metric_exemplars_text_timestamps.sql +47 -0
  88. package/src/migrations-postgres/044_actions_started_at_index.sql +8 -0
  89. package/src/migrations-postgres/045_user_profiles_project_composite_pk.sql +33 -0
  90. package/src/migrations-postgres/046_action_graph_project_composite_pk.sql +39 -0
  91. package/src/migrations-postgres/047_ai_span_payloads_project_composite_pk.sql +30 -0
  92. package/src/migrations-postgres/048_telemetry_spans_project_composite_pk.sql +37 -0
  93. package/src/migrations-postgres/049_alert_state_notified.sql +10 -0
  94. package/src/migrations-postgres/050_autosre_causal_ontology.sql +216 -0
  95. package/src/migrations-postgres/README.md +58 -5
  96. package/src/otlp/decode/body.test.ts +76 -0
  97. package/src/otlp/decode/body.ts +67 -6
  98. package/src/plugins/_json.ts +21 -0
  99. package/src/plugins/action-graph-processor.ts +6 -12
  100. package/src/plugins/ai-receiver.ts +5 -12
  101. package/src/plugins/ai-span-payloads-processor.ts +1 -2
  102. package/src/plugins/alerts-evaluator-retention.test.ts +170 -0
  103. package/src/plugins/alerts-evaluator.test.ts +464 -3
  104. package/src/plugins/alerts-evaluator.ts +181 -41
  105. package/src/plugins/alerts-routes.ts +10 -6
  106. package/src/plugins/ask-routes.ts +4 -5
  107. package/src/plugins/autosre-routes.ts +1019 -0
  108. package/src/plugins/connected-routes/manifest.ts +13 -1
  109. package/src/plugins/connected-routes.test.ts +106 -0
  110. package/src/plugins/connected-routes.ts +285 -5
  111. package/src/plugins/dashboard-routes.test.ts +120 -0
  112. package/src/plugins/dashboard-routes.ts +11 -2
  113. package/src/plugins/default-span-enrichment.ts +13 -1
  114. package/src/plugins/eval-cases-routes.ts +65 -19
  115. package/src/plugins/evidence-retrieval-routes.ts +300 -4
  116. package/src/plugins/identity-receiver.ts +9 -11
  117. package/src/plugins/json-body.test.ts +91 -0
  118. package/src/plugins/onboarding-routes.test.ts +105 -0
  119. package/src/plugins/onboarding-routes.ts +26 -11
  120. package/src/plugins/projects-routes.ts +2 -1
  121. package/src/plugins/replay-isolation.test.ts +922 -0
  122. package/src/plugins/replay-receiver.ts +8 -8
  123. package/src/plugins/usage-receiver.ts +3 -6
  124. package/dist/chunk-UGWDOGGH.js.map +0 -1
  125. package/dist/chunk-XKQTVIRE.js.map +0 -1
  126. /package/dist/{openai-FJYOQ7WO.js.map → openai-GSJZGFGL.js.map} +0 -0
package/dist/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import * as hono from 'hono';
2
- import { MiddlewareHandler, Hono } from 'hono';
2
+ import { MiddlewareHandler, Hono, Context } from 'hono';
3
3
  import * as hono_types from 'hono/types';
4
4
  import * as _obsunified_types from '@obsunified/types';
5
- import { StoredSpan, TelemetryOverviewOptions, TelemetryOverviewResponse, TelemetryTraceDetailResponse, TelemetryInstrumentationGapsResponse, TelemetryIssueOptions, TelemetryIssueOverviewResponse, TelemetryIssueDetailResponse, UsageEventRecord, UsageOverviewOptions, UsageOverviewResponse, UsageSessionDetailResponse, UsageSessionSummary, AlertRuleInput, AlertRule, AlertStateRow, AlertState, AlertEvaluationsListResponse, AlertComparison, JsonValue, EvidenceReference, EvidenceReferenceContract, Project, IngestKey, IngestKeyWithPlaintext } from '@obsunified/types';
6
- import { b as CollectorEnv, c as CollectorRouteContext, L as Logger, C as ChildSpanRunner } from './ingest-auth-HFVO1zIp.js';
7
- export { a as CollectorApp, d as consoleLogger, e as createIngestAuth } from './ingest-auth-HFVO1zIp.js';
5
+ import { StoredSpan, TelemetryOverviewOptions, TelemetryOverviewResponse, TelemetryTraceDetailResponse, TelemetryInstrumentationGapsResponse, TelemetryIssueOptions, TelemetryIssueOverviewResponse, TelemetryIssueDetailResponse, UsageEventRecord, UsageOverviewOptions, UsageOverviewResponse, UsageSessionDetailResponse, UsageSessionSummary, AlertRuleInput, AlertRule, AlertStateRow, AlertState, AlertEvaluationsListResponse, AlertComparison, ChangeBlastRadiusResult, ContentionCandidate, ChangeEvent, ChangeEventInput, InvestigationFinding, InvestigationFindingInput, OntologyNode, OntologyEdge, CanonicalNodeIdentity, OntologyGraphResponse, RelationshipCertainty, CausalTriple, IncidentInvestigation, RemediationProposal, EvidenceEntityKind, RemediationProposalInput, JsonValue, EvidenceReference, EvidenceReferenceContract, Project, IngestKey, IngestKeyWithPlaintext } from '@obsunified/types';
6
+ import { b as CollectorEnv, L as Logger, C as ChildSpanRunner, c as CollectorRouteContext } from './ingest-auth-C5-8Mly5.js';
7
+ export { a as CollectorApp, d as consoleLogger, e as createIngestAuth } from './ingest-auth-C5-8Mly5.js';
8
8
  import { ActionConfidence } from '@obsunified/types/constants';
9
9
  import { Pool } from 'pg';
10
10
 
@@ -81,6 +81,83 @@ declare class D1Adapter implements SqlDb {
81
81
  }>>;
82
82
  }
83
83
 
84
+ /**
85
+ * RFC 0002 Stage 1 — runner that executes SQL-only Analyses on the existing
86
+ * scheduled handler.
87
+ *
88
+ * runSqlAnalysis — execute one definition's `sql` against D1 and shape the
89
+ * row(s) into an AnalysisResult.
90
+ * runAllDueAnalyses — orchestrator: refresh the in-process registry into
91
+ * analysis_definitions, find which ones are due, run them,
92
+ * persist the results. Per-analysis errors are isolated
93
+ * via Promise.allSettled so one bad SQL doesn't kill the
94
+ * batch.
95
+ *
96
+ * Result shape convention: an Analysis SQL is expected to return a single
97
+ * row with the columns:
98
+ *
99
+ * status TEXT | NULL -- 'ok' | 'warn' | 'critical' | 'unknown'
100
+ * primary_value REAL | NULL
101
+ * baseline_value REAL | NULL
102
+ * payload TEXT | NULL -- JSON-serialized object, optional
103
+ *
104
+ * If `status` is NULL we derive it from the magnitude of delta_pct
105
+ * (>=25% → critical, >=10% → warn, else ok). delta_pct itself is computed
106
+ * here from primary/baseline so individual SQL queries don't have to.
107
+ */
108
+
109
+ /**
110
+ * Orchestrator. Refreshes the analysis_definitions registry from the
111
+ * in-process registry (so newly-derived Tier 1 analyses become visible
112
+ * to the dashboard within one tick), then runs whatever's due.
113
+ *
114
+ * Runs once per project in the `projects` table (falling back to the
115
+ * `default` project when the table is empty or unreadable), so analyses and
116
+ * the alert rules bound to them refresh for every tenant, not just the
117
+ * first one.
118
+ */
119
+ declare function runAllDueAnalyses(ctx: {
120
+ env: CollectorEnv;
121
+ retentionHours: number;
122
+ logger?: Logger;
123
+ tracer?: ChildSpanRunner;
124
+ }, _runtime?: unknown): Promise<{
125
+ ran: number;
126
+ failed: number;
127
+ refreshed: number;
128
+ narrated: number;
129
+ skipped?: number;
130
+ }>;
131
+
132
+ /**
133
+ * Retention for the secondary tables the hourly sweep did not cover. Each
134
+ * rule is tied to an existing contract rather than a new policy:
135
+ *
136
+ * - `alert_evaluations`: `/internal/alerts/evaluations` accepts at most a
137
+ * 720-hour window, so rows older than that are unreachable through the API.
138
+ * - `ask_evidence_events`: feeds `getTopAskedAnalyses`, whose trailing window
139
+ * is 7 days; rows older than 30 days cannot influence it.
140
+ * - `metric_series`: identity rows whose points have all expired and that
141
+ * were created before the retention window. They hold no data; the next
142
+ * ingest of that identity recreates the row.
143
+ * - `evidence_retrieval_refs`: rows carry their own `expires_at` (copied from
144
+ * the signal they reference); expansions follow their ref.
145
+ */
146
+
147
+ interface HousekeepingSummary {
148
+ alert_evaluations: number;
149
+ ask_evidence_events: number;
150
+ metric_series: number;
151
+ evidence_refs: number;
152
+ evidence_ref_expansions: number;
153
+ ontology_edges?: number;
154
+ ontology_nodes?: number;
155
+ change_events?: number;
156
+ incident_investigations?: number;
157
+ investigation_findings?: number;
158
+ remediation_proposals?: number;
159
+ }
160
+
84
161
  interface ServiceMapOptions {
85
162
  projectId: string;
86
163
  hours: number;
@@ -298,6 +375,18 @@ interface CollectorConfig {
298
375
  * a tracing layer at the storage seam.
299
376
  */
300
377
  sqlDb?: SqlDbFactory;
378
+ /**
379
+ * Raw request body cap for `/v1/*` and `/internal/*` in bytes. Defaults
380
+ * to `MAX_BODY_BYTES` from the environment, then 10 MiB. Oversized
381
+ * requests get a 413 before any handler buffers the body.
382
+ */
383
+ maxBodyBytes?: number;
384
+ /**
385
+ * Body cap for the unauthenticated `/auth/*` routes (login/check/logout).
386
+ * Defaults to 16 KiB and is independent of `maxBodyBytes`, so an anonymous
387
+ * client can never make the host buffer an ingest-sized body.
388
+ */
389
+ maxAuthBodyBytes?: number;
301
390
  }
302
391
  declare class CollectorRuntime implements CollectorPluginContext {
303
392
  private readonly spanProcessors;
@@ -327,30 +416,56 @@ declare const createTelemetryCollectorApp: (pluginsOrConfig: CollectorPlugin[] |
327
416
  Bindings: CollectorEnv;
328
417
  }>;
329
418
  /**
330
- * Scheduled handler that purges expired telemetry and usage rows.
331
- * Wire into wrangler.toml: `[triggers] crons = ["0 * * * *"]`
332
- * Source: AgentOwl + DecisionOps
419
+ * Runtime-neutral entry point for a background maintenance task.
420
+ *
421
+ * `run(env)` does the work and needs nothing but a `CollectorEnv`, so any
422
+ * host (Cloudflare cron, a Node `setInterval`, a Kubernetes CronJob hitting
423
+ * a one-shot script) can drive it. `scheduled(event, env, ctx)` is the thin
424
+ * Cloudflare Workers adapter over `run` — the event and context are accepted
425
+ * for signature compatibility with `ExportedHandler.scheduled` but are never
426
+ * consulted, so non-Workers hosts should call `run` directly rather than
427
+ * fabricating placeholder event objects.
333
428
  */
429
+ interface ScheduledTaskHandler<TSummary = void> {
430
+ run(env: CollectorEnv): Promise<TSummary>;
431
+ scheduled(event: ScheduledEvent, env: CollectorEnv, ctx: ExecutionContext): Promise<void>;
432
+ }
433
+ /** Per-table purge counts reported by the retention cleanup task. */
434
+ interface RetentionCleanupSummary {
435
+ spans: number;
436
+ usage: number;
437
+ logs: number;
438
+ ai_calls: number;
439
+ metric_points: number;
440
+ analysis_results: number;
441
+ profile_blobs: number;
442
+ /** RFC 0010 action spine rows removed (trees older than RETENTION_HOURS). */
443
+ actions: number;
444
+ /** Leaf rows removed with their actions: agent_runs, tool_calls,
445
+ * retrieval_events, eval_results and artifacts. */
446
+ action_leaves: number;
447
+ /** Secondary tables (see lib/housekeeping.ts); zeros when that step failed. */
448
+ housekeeping: HousekeepingSummary;
449
+ }
334
450
  declare const createRetentionCleanupHandler: (options?: {
335
451
  logger?: Logger;
336
- }) => {
337
- scheduled(_event: ScheduledEvent, env: CollectorEnv, _ctx: ExecutionContext): Promise<void>;
338
- };
452
+ }) => ScheduledTaskHandler<RetentionCleanupSummary>;
339
453
  /**
340
454
  * RFC 0002 Stage 1 — run any due Analyses. Cheap to call frequently because
341
455
  * `runAllDueAnalyses` filters by `last_run + refreshSeconds < now` before
342
456
  * touching D1.
343
457
  *
344
- * Wire to a per-minute trigger in wrangler.toml: `crons = ["* * * * *"]`.
345
- * Kept distinct from retention so the per-minute tick doesn't sweep every
346
- * retention table every cycle.
458
+ * Wire to a per-minute trigger in wrangler.toml: `crons = ["* * * * *"]`,
459
+ * or call `.run(env)` from a non-Workers host scheduler. Kept distinct from
460
+ * retention so the per-minute tick doesn't sweep every retention table every
461
+ * cycle.
347
462
  */
348
463
  declare const createAnalysesRunHandler: (options?: {
349
464
  logger?: Logger;
350
465
  tracer?: ChildSpanRunner;
351
- }) => {
352
- scheduled(_event: ScheduledEvent, env: CollectorEnv, _ctx: ExecutionContext): Promise<void>;
353
- };
466
+ }) => ScheduledTaskHandler<AnalysesRunSummary | undefined>;
467
+ /** Shape returned by `runAllDueAnalyses` and surfaced via the run handler. */
468
+ type AnalysesRunSummary = Awaited<ReturnType<typeof runAllDueAnalyses>>;
354
469
 
355
470
  declare const actionRoutesPlugin: CollectorPlugin;
356
471
 
@@ -371,15 +486,20 @@ declare const aiReceiverPlugin: CollectorPlugin;
371
486
 
372
487
  declare const aiSpanPayloadsProcessorPlugin: CollectorPlugin;
373
488
 
489
+ /** Resolve a hostname to its addresses (e.g. `dns.lookup(host, { all: true })`). */
490
+ type HostLookup = (hostname: string) => Promise<string[]>;
491
+
374
492
  declare function evaluateAllRules(env: CollectorEnv, options?: {
375
493
  logger?: Logger;
376
494
  tracer?: ChildSpanRunner;
377
495
  concurrency?: number;
378
496
  ruleTimeoutMs?: number;
497
+ hostLookup?: HostLookup;
379
498
  }): Promise<{
380
499
  evaluated: number;
381
500
  fired: number;
382
501
  resolved: number;
502
+ redelivered: number;
383
503
  }>;
384
504
  /**
385
505
  * Scheduled handler suitable for wrangler cron triggers. Dispatches on the
@@ -429,6 +549,8 @@ declare const analysesRoutesPlugin: CollectorPlugin;
429
549
 
430
550
  declare const askRoutesPlugin: CollectorPlugin;
431
551
 
552
+ declare const autosreRoutesPlugin: CollectorPlugin;
553
+
432
554
  declare const botFilterPlugin: CollectorPlugin;
433
555
 
434
556
  type CausalConfidence = ActionConfidence;
@@ -666,18 +788,61 @@ declare const usersQueryRoutesPlugin: CollectorPlugin;
666
788
  declare const utmEnrichmentPlugin: CollectorPlugin;
667
789
 
668
790
  /**
669
- * Creates auth routes and middleware for dashboard password login.
791
+ * Fixed-window failure limiter for `POST /auth/login`. Only failed attempts
792
+ * count; a successful login clears the client's bucket. State is in-memory,
793
+ * so on Cloudflare Workers it is per isolate and on Node per process — it
794
+ * slows a single-origin brute force without any shared storage, and it can
795
+ * never lock an operator out for longer than one window.
796
+ */
797
+ interface LoginRateLimitOptions {
798
+ /** Failed attempts allowed per window before 429. Default 10. */
799
+ maxFailures?: number;
800
+ /** Window length in milliseconds. Default 15 minutes. */
801
+ windowMs?: number;
802
+ /** Upper bound on tracked clients; the oldest bucket is evicted past it. */
803
+ maxClients?: number;
804
+ /** Clock override for tests. */
805
+ now?: () => number;
806
+ }
807
+
808
+ interface DashboardAuthConfig {
809
+ password?: string;
810
+ token?: string;
811
+ /**
812
+ * Failed-login limiter for POST /auth/login (see login-rate-limit.ts).
813
+ * Pass `false` to disable; defaults to 10 failures per 15 minutes.
814
+ * `clientAddress` lets the host supply the connection's peer address;
815
+ * `trustProxyHeaders` honours forwarding headers from a trusted proxy.
816
+ * Without either, Workers use the edge-set client IP and every other
817
+ * runtime has no trustworthy identity: limiting is then skipped (never a
818
+ * shared bucket, which would let one client lock everyone out) and a
819
+ * warning is logged once via `warn` (default `console.warn`).
820
+ */
821
+ loginRateLimit?: (LoginRateLimitOptions & {
822
+ clientAddress?: (c: Context<{
823
+ Bindings: CollectorEnv;
824
+ }>) => string | undefined;
825
+ trustProxyHeaders?: boolean;
826
+ /** Sink for the one-time "limiter inactive" warning. */
827
+ warn?: (message: string) => void;
828
+ }) | false;
829
+ }
830
+ /**
831
+ * Creates auth routes and middleware for dashboard password login and
832
+ * programmatic read/admin bearer token authentication.
670
833
  *
671
834
  * Routes added:
672
835
  * POST /auth/login — accepts { password }, sets session cookie
673
- * GET /auth/check — returns { authenticated: true/false }
836
+ * GET /auth/check — returns { authenticated: true/false } (supports cookie and Bearer token)
674
837
  * POST /auth/logout — clears session cookie
675
838
  *
676
- * The returned middleware validates the session cookie on protected routes.
839
+ * The returned middleware validates either:
840
+ * 1. Authorization: Bearer <token> (programmatic read/admin token for MCP/API)
841
+ * 2. obs_session cookie (human dashboard user session)
842
+ *
843
+ * Ingest keys are rejected on dashboard/internal routes — they are write-only.
677
844
  */
678
- declare function createDashboardAuth(config: {
679
- password: string;
680
- }): {
845
+ declare function createDashboardAuth(config?: DashboardAuthConfig): {
681
846
  middleware: MiddlewareHandler<{
682
847
  Bindings: CollectorEnv;
683
848
  Variables: {
@@ -718,6 +883,107 @@ declare class TailHub {
718
883
  private broadcast;
719
884
  }
720
885
 
886
+ /**
887
+ * Retention for the RFC 0010 action graph (`actions` spine plus the
888
+ * `agent_runs`, `tool_calls`, `retrieval_events`, `eval_results` and
889
+ * `artifacts` leaves).
890
+ *
891
+ * Unlike the telemetry tables these rows carry no `expires_at`; the sweep
892
+ * derives expiry from `actions.started_at` and the configured
893
+ * `RETENTION_HOURS`. The unit of deletion is the causal tree
894
+ * (`project_id`, `root_action_id`): a tree is selected, rechecked and
895
+ * removed as a whole inside one `SqlDb.batch` (a D1 batch or a Postgres
896
+ * transaction), never action by action. A tree is kept whole while any of
897
+ * its actions:
898
+ *
899
+ * - started inside the retention window,
900
+ * - is still in progress (`ended_at IS NULL`),
901
+ * - belongs to an `agent_runs` row whose `status` is `running`,
902
+ * - is referenced by an `eval_cases` row in the same project via
903
+ * `source_action_id`, `source_agent_run_id`, or (through a `tool_calls`
904
+ * row) `source_tool_call_id`.
905
+ *
906
+ * Work is bounded per invocation: each batch packs whole trees up to
907
+ * `maxActionsPerBatch` actions and `maxTreesPerBatch` trees, and the sweep
908
+ * stops after `maxBatches`. A tree with more than `maxActionsPerBatch`
909
+ * actions can never be deleted atomically within those bounds, so it is
910
+ * retained intact and reported as `oversized_trees` (and logged by the
911
+ * handler) instead of being deleted in pieces. Raise `maxActionsPerBatch`
912
+ * to reclaim such trees.
913
+ *
914
+ * Concurrency: every DELETE repeats the eligibility predicate, so a tree
915
+ * that gained a fresh or unfinished action, a `running` run, or an eval
916
+ * case between selection and deletion is left alone. On D1 the batch is a
917
+ * single serialized SQLite transaction, so that recheck is airtight. On
918
+ * Postgres (READ COMMITTED) each statement sees writes committed before it
919
+ * started, so a commit landing between the leaf deletes and the final spine
920
+ * delete of the same tree could leave that tree's spine without its leaves.
921
+ * That needs an ingest for a tree whose newest action is already older than
922
+ * the retention window, inside a sub-second window; it is documented rather
923
+ * than locked against because `SqlDb` has no row-locking primitive.
924
+ *
925
+ * Intentionally **not** deleted here:
926
+ *
927
+ * - `artifacts.storage_ref` blobs — only the metadata row goes; the blob
928
+ * store is not consulted (the collector never wrote those blobs).
929
+ * - `agent_runs` rows whose `id` is not an action in the tree — nothing
930
+ * proves when they started.
931
+ * - `ai_span_payloads.action_id` and `evidence_retrieval_refs` anchors —
932
+ * those tables have their own `expires_at` retention.
933
+ * - `eval_cases`, `eval_runs`, `eval_case_results` — curated fixtures,
934
+ * never retention-bound.
935
+ */
936
+
937
+ interface ActionGraphRetentionOptions {
938
+ /** Retention window; trees whose newest action is older than this expire. */
939
+ retentionHours: number;
940
+ /** Clock override for tests. Defaults to `Date.now`. */
941
+ now?: () => number;
942
+ /**
943
+ * Upper bound on actions deleted per batch/transaction. Also the largest
944
+ * tree the sweep will delete; bigger trees are retained and reported.
945
+ * Defaults to 500.
946
+ */
947
+ maxActionsPerBatch?: number;
948
+ /** Upper bound on trees per batch (bounds statements per transaction). Defaults to 25. */
949
+ maxTreesPerBatch?: number;
950
+ /** Upper bound on batches per invocation. Defaults to 10. */
951
+ maxBatches?: number;
952
+ }
953
+ /** Per-table delete counts for one `purgeExpiredActionGraph` call. */
954
+ interface ActionGraphPurgeSummary {
955
+ actions: number;
956
+ agent_runs: number;
957
+ tool_calls: number;
958
+ retrieval_events: number;
959
+ eval_results: number;
960
+ artifacts: number;
961
+ /** Whole trees deleted. */
962
+ trees: number;
963
+ /** Batches (transactions) executed. */
964
+ batches: number;
965
+ /** `true` when `maxBatches` was hit while deletable trees remained. */
966
+ truncated: boolean;
967
+ /**
968
+ * Expired, otherwise eligible trees retained because they hold more than
969
+ * `maxActionsPerBatch` actions. Capped at `OVERSIZED_REPORT_LIMIT`.
970
+ */
971
+ oversized_trees: number;
972
+ /** Identities of the reported oversized trees, for operators. */
973
+ oversized: Array<{
974
+ project_id: string;
975
+ root_action_id: string;
976
+ actions: number;
977
+ }>;
978
+ }
979
+ /** Cutoff as the same ISO text shape the collector writes to `started_at`. */
980
+ declare const actionRetentionCutoff: (retentionHours: number, nowMs?: number) => string;
981
+ /**
982
+ * Purge expired action trees. Safe to call repeatedly: a second call right
983
+ * after the first finds no deletable trees and changes nothing.
984
+ */
985
+ declare function purgeExpiredActionGraph(db: SqlDb, options: ActionGraphRetentionOptions): Promise<ActionGraphPurgeSummary>;
986
+
721
987
  type ActionRecord = Omit<ActionRef, "projectId"> & {
722
988
  projectId: string;
723
989
  };
@@ -756,10 +1022,15 @@ declare class ActionStore {
756
1022
  listArtifactsByAction(projectId: string, actionId: string, limit?: number): Promise<ArtifactRef[]>;
757
1023
  }
758
1024
 
1025
+ interface AlertsStoreOptions {
1026
+ /** Permit webhook targets on loopback / private / link-local hosts. */
1027
+ allowPrivateWebhookTargets?: boolean;
1028
+ }
759
1029
  declare function compareValue(value: number, threshold: number, comparison: AlertComparison): boolean;
760
1030
  declare class AlertsStore {
761
1031
  private readonly db;
762
- constructor(db: SqlDb);
1032
+ private readonly options;
1033
+ constructor(db: SqlDb, options?: AlertsStoreOptions);
763
1034
  validateInput(input: AlertRuleInput): void;
764
1035
  listRules(projectId: string): Promise<AlertRule[]>;
765
1036
  getRule(id: string, projectId: string): Promise<AlertRule | null>;
@@ -767,8 +1038,24 @@ declare class AlertsStore {
767
1038
  updateRule(id: string, projectId: string, patch: Partial<AlertRuleInput>): Promise<AlertRule | null>;
768
1039
  deleteRule(id: string, projectId: string): Promise<boolean>;
769
1040
  listEnabledRules(): Promise<AlertRule[]>;
770
- getState(ruleId: string): Promise<AlertStateRow | null>;
1041
+ getState(ruleId: string, projectId: string): Promise<AlertStateRow | null>;
1042
+ /**
1043
+ * Record a state transition with its notification still outstanding
1044
+ * (`notified = 0`); `markNotified` flips it once a channel accepts the
1045
+ * alert. On a database that has not applied migration 049 the column is
1046
+ * missing: the transition is still written (legacy statement) so alerting
1047
+ * keeps working through a rolling deploy, and the evaluator treats a row
1048
+ * without the column as delivered.
1049
+ */
771
1050
  transitionState(ruleId: string, projectId: string, newState: AlertState, now: string): Promise<void>;
1051
+ /**
1052
+ * Mark the transition `(state, lastStateChange)` as delivered. Scoped to
1053
+ * the exact transition the caller notified for: if the rule changed state
1054
+ * again while the webhook was in flight, the newer transition keeps its
1055
+ * own outstanding flag. Returns false when nothing matched (stale or
1056
+ * already marked). On a pre-049 schema there is nothing to mark.
1057
+ */
1058
+ markNotified(ruleId: string, projectId: string, state: AlertState, lastStateChange: string): Promise<boolean>;
772
1059
  recordEvaluation(ruleId: string, projectId: string, value: number, state: AlertState, notified: boolean): Promise<void>;
773
1060
  listEvaluations(params: {
774
1061
  ruleId: string;
@@ -793,6 +1080,289 @@ declare class AlertsStore {
793
1080
  private evaluateAIRule;
794
1081
  }
795
1082
 
1083
+ declare function recordChangeEvent(db: SqlDb, projectId: string, input: ChangeEventInput): Promise<ChangeEvent>;
1084
+ declare function listChangeEvents(db: SqlDb, options: {
1085
+ projectId: string;
1086
+ environment?: string;
1087
+ serviceName?: string;
1088
+ targetNodeId?: string;
1089
+ kind?: string;
1090
+ since?: string;
1091
+ limit?: number;
1092
+ }): Promise<ChangeEvent[]>;
1093
+ declare function getChangeEvent(db: SqlDb, projectId: string, changeId: string, environment?: string): Promise<ChangeEvent | null>;
1094
+ /**
1095
+ * Indexed cross-trace contention lookup (RFC 0013 § 4.B).
1096
+ * Uses `idx_spans_contention_lookup` (`project_id, environment, target_node_id, start_time, end_time`).
1097
+ * Enforces P1 #5: if the target node is unscoped (`is_scoped_identity = 0`), caps
1098
+ * `relationshipCertainty` at `"inferred"`.
1099
+ */
1100
+ declare function findCrossTraceContention(db: SqlDb, options: {
1101
+ projectId: string;
1102
+ environment: string;
1103
+ targetNodeId: string;
1104
+ excludeTraceId: string;
1105
+ startTime: string;
1106
+ endTime: string;
1107
+ limit?: number;
1108
+ }): Promise<ContentionCandidate[]>;
1109
+ /**
1110
+ * Computes traffic-normalized cohort rate lift, minimum sample gates, shared-degradation
1111
+ * noisy-neighbor contention checks, and code-path overlap for a `change_event`
1112
+ * (RFC 0013 § 4.A, P1 #1, P1 #2).
1113
+ */
1114
+ declare function computeChangeBlastRadius(db: SqlDb, projectId: string, changeId: string, options?: {
1115
+ environment?: string;
1116
+ minComparableSample?: number;
1117
+ }): Promise<ChangeBlastRadiusResult | null>;
1118
+
1119
+ declare function recordInvestigationFinding(db: SqlDb, projectId: string, input: InvestigationFindingInput): Promise<InvestigationFinding>;
1120
+ declare function getInvestigationFinding(db: SqlDb, projectId: string, findingId: string, environment?: string): Promise<InvestigationFinding | null>;
1121
+ declare function searchInvestigationFindings(db: SqlDb, options: {
1122
+ projectId: string;
1123
+ environment?: string;
1124
+ fingerprint?: string;
1125
+ symptomEntityId?: string;
1126
+ rootCauseEntityId?: string;
1127
+ causalVerificationStatus?: string;
1128
+ incidentId?: string;
1129
+ limit?: number;
1130
+ }): Promise<InvestigationFinding[]>;
1131
+ declare class CounterfactualVerificationError extends Error {
1132
+ constructor(message: string);
1133
+ }
1134
+ /**
1135
+ * Verifies a PairedCounterfactualProof in SQL against `eval_runs` and `eval_case_results`
1136
+ * (RFC 0013 § 5.A & P1 #3).
1137
+ *
1138
+ * Requires:
1139
+ * 1. `failingRunId` and all `passingRunIds` belong to `projectId` and ran against `evalCaseId`
1140
+ * with the identical non-null `input_payload_hash`.
1141
+ * 2. `failingRunId` failed (`passed = 0`) and its `failure_fingerprint` matches `finding.fingerprint`.
1142
+ * 3. All `passingRunIds` passed (`passed = 1`) and `passingRunIds.length >= requiredTrials`.
1143
+ * 4. Failing and passing runs have distinct recorded version/commit/prompt lineage.
1144
+ */
1145
+ declare function verifyPairedCounterfactualProof(db: SqlDb, projectId: string, findingId: string, input: {
1146
+ environment?: string;
1147
+ evalCaseId: string;
1148
+ failingRunId: string;
1149
+ passingRunIds: string[];
1150
+ requiredTrials?: number;
1151
+ }): Promise<InvestigationFinding>;
1152
+
1153
+ interface ExtractedSpanLineage {
1154
+ environment: string;
1155
+ namespace: string;
1156
+ serviceName: string | null;
1157
+ serviceVersion: string | null;
1158
+ commitSha: string | null;
1159
+ configDigest: string | null;
1160
+ sourceNode: CanonicalNodeIdentity | null;
1161
+ targetNode: CanonicalNodeIdentity | null;
1162
+ edgePredicate: string | null;
1163
+ errorSignature: string | null;
1164
+ }
1165
+ /**
1166
+ * Extracts runtime lineage (`environment`, `serviceVersion`, `commitSha`, `configDigest`),
1167
+ * canonical source/target `OntologyNode` identities, and `errorSignature` from a span.
1168
+ */
1169
+ declare function extractSpanLineageAndTopology(span: StoredSpan): ExtractedSpanLineage;
1170
+ /**
1171
+ * Upserts environment-scoped `ontology_nodes`, `ontology_edges`, and inferred version-transition
1172
+ * `change_events` from ingested spans.
1173
+ */
1174
+ declare function upsertOntologyFromSpans(db: SqlDb, spans: StoredSpan[], options?: {
1175
+ bypassCache?: boolean;
1176
+ }): Promise<void>;
1177
+ /**
1178
+ * Tarjan's O(V + E) Strongly Connected Components (SCC) condensation algorithm
1179
+ * (RFC 0013 § 1.B & P2 #6). Ensures cyclic service/queue topologies never cause
1180
+ * infinite loops during dependency traversal.
1181
+ */
1182
+ declare function computeTarjanScc(nodes: OntologyNode[], edges: OntologyEdge[]): {
1183
+ annotatedNodes: OntologyNode[];
1184
+ components: Array<{
1185
+ sccId: string;
1186
+ nodeIds: string[];
1187
+ isCyclic: boolean;
1188
+ }>;
1189
+ };
1190
+ interface TraceSpanLocusInput {
1191
+ span_id: string;
1192
+ parent_span_id: string | null;
1193
+ service_name: string | null;
1194
+ span_name: string;
1195
+ span_kind: number;
1196
+ status_code: number;
1197
+ status_message: string | null;
1198
+ start_time: string;
1199
+ end_time: string;
1200
+ duration_ms: number;
1201
+ environment?: string | null;
1202
+ target_node_id?: string | null;
1203
+ error_signature?: string | null;
1204
+ }
1205
+ interface TraceFaultLocusResult {
1206
+ faultLocusSpan: TraceSpanLocusInput | null;
1207
+ faultLocusNodeId: string | null;
1208
+ relationshipCertainty: RelationshipCertainty;
1209
+ causalProofTier: "unverified_locus";
1210
+ causalTriples: CausalTriple[];
1211
+ }
1212
+ /**
1213
+ * Per-trace cycle-guarded DFS fault locus detector (RFC 0013 § 1.B & P1 #1).
1214
+ * Identifies where an error surfaced (`originated_fault_in_span`) while keeping
1215
+ * `causalProofTier: "unverified_locus"` separate from `relationshipCertainty: "exact"`.
1216
+ */
1217
+ declare function localizeTraceFaultLocus(traceId: string, spans: TraceSpanLocusInput[]): TraceFaultLocusResult;
1218
+ /**
1219
+ * Queries `telemetry_spans` for a trace and computes its fault locus.
1220
+ */
1221
+ declare function loadTraceFaultLocusFromDb(db: SqlDb, projectId: string, traceId: string): Promise<TraceFaultLocusResult>;
1222
+ /**
1223
+ * Loads the environment-scoped operational ontology graph with Tarjan SCC
1224
+ * cycle metadata and dependency blackout indicators.
1225
+ */
1226
+ declare function getOntologyGraph(db: SqlDb, options: {
1227
+ projectId: string;
1228
+ environment?: string;
1229
+ }): Promise<OntologyGraphResponse>;
1230
+
1231
+ /**
1232
+ * Opens or deduplicates an active `incident_investigation` (RFC 0013 § 6.A & P2 #7).
1233
+ * Multiple firing alerts or webhook retries for the same `(project_id, environment, target_node_id, symptom_category)`
1234
+ * coalesce into a single active incident.
1235
+ */
1236
+ declare function openOrDeduplicateIncident(db: SqlDb, projectId: string, input: {
1237
+ id?: string;
1238
+ environment?: string;
1239
+ targetNodeId: string;
1240
+ symptomCategory?: string;
1241
+ symptomAnchorKind: EvidenceEntityKind;
1242
+ symptomAnchorId: string;
1243
+ symptomFingerprint?: string | null;
1244
+ latestBundle?: Record<string, unknown> | null;
1245
+ }): Promise<{
1246
+ incident: IncidentInvestigation;
1247
+ deduplicated: boolean;
1248
+ }>;
1249
+ declare function getIncidentInvestigation(db: SqlDb, projectId: string, incidentId: string, environment?: string): Promise<IncidentInvestigation | null>;
1250
+ declare function listIncidentInvestigations(db: SqlDb, options: {
1251
+ projectId: string;
1252
+ environment?: string;
1253
+ status?: string;
1254
+ limit?: number;
1255
+ }): Promise<IncidentInvestigation[]>;
1256
+ /**
1257
+ * Exclusive runner lease acquisition / renewal (RFC 0013 § 6.B & P2 #7).
1258
+ * Prevents competing AutoSRE runners from investigating or remediating the same
1259
+ * incident simultaneously.
1260
+ */
1261
+ declare function acquireIncidentLease(db: SqlDb, projectId: string, incidentId: string, input: {
1262
+ environment?: string;
1263
+ runnerId: string;
1264
+ leaseDurationSeconds?: number;
1265
+ nowIso?: string;
1266
+ }): Promise<{
1267
+ acquired: true;
1268
+ incident: IncidentInvestigation;
1269
+ } | {
1270
+ acquired: false;
1271
+ reason: string;
1272
+ conflictOwnerId: string | null;
1273
+ leaseExpiresAt: string | null;
1274
+ }>;
1275
+ /**
1276
+ * SSRF-safe, HMAC-SHA256 signed AutoSRE incident webhook outbox delivery
1277
+ * (RFC 0013 § 6.A).
1278
+ */
1279
+ declare function dispatchIncidentWebhook(db: SqlDb, projectId: string, incidentId: string, webhook: {
1280
+ url: string;
1281
+ secret?: string;
1282
+ allowPrivateNetworks?: boolean;
1283
+ fetchImpl?: typeof fetch;
1284
+ }): Promise<{
1285
+ delivered: boolean;
1286
+ status?: number;
1287
+ error?: string;
1288
+ }>;
1289
+ declare class RemediationGateError extends Error {
1290
+ readonly statusCode: number;
1291
+ constructor(message: string, statusCode?: number);
1292
+ }
1293
+ /**
1294
+ * Pre-execution remediation proposal gate (RFC 0013 § 6.B & P1 #4).
1295
+ */
1296
+ declare function proposeRemediationAction(db: SqlDb, projectId: string, input: RemediationProposalInput): Promise<RemediationProposal>;
1297
+ declare function getRemediationProposal(db: SqlDb, projectId: string, proposalId: string, environment?: string): Promise<RemediationProposal | null>;
1298
+ declare function listRemediationProposals(db: SqlDb, options: {
1299
+ projectId: string;
1300
+ environment?: string;
1301
+ status?: string;
1302
+ incidentId?: string;
1303
+ findingId?: string;
1304
+ limit?: number;
1305
+ }): Promise<RemediationProposal[]>;
1306
+ /**
1307
+ * Human dashboard session approval for `pending_human_approval` proposals
1308
+ * (RFC 0013 § 6.B & P1 #4). Mints a single-use `grantToken`.
1309
+ */
1310
+ declare function approveRemediationProposal(db: SqlDb, projectId: string, proposalId: string, options: {
1311
+ environment?: string;
1312
+ approvedByUserId: string;
1313
+ }): Promise<RemediationProposal>;
1314
+ declare function rejectRemediationProposal(db: SqlDb, projectId: string, proposalId: string, options: {
1315
+ environment?: string;
1316
+ rejectedByUserId: string;
1317
+ reason?: string;
1318
+ }): Promise<RemediationProposal>;
1319
+ /**
1320
+ * Single-use grant redemption with optimistic `expectedCurrentState` verification
1321
+ * against `ontology_nodes` (RFC 0013 § 6.B & P1 #4).
1322
+ */
1323
+ declare function claimRemediationGrant(db: SqlDb, projectId: string, proposalId: string, options: {
1324
+ environment?: string;
1325
+ grantToken: string;
1326
+ runnerId?: string;
1327
+ nowIso?: string;
1328
+ }): Promise<RemediationProposal>;
1329
+ /**
1330
+ * Records remediation completion, updates `ontology_nodes` to desired state,
1331
+ * and arms the +2m/+5m/+15m post-remediation watchdog on the finding.
1332
+ */
1333
+ declare function completeRemediationProposal(db: SqlDb, projectId: string, proposalId: string, options: {
1334
+ environment?: string;
1335
+ resultingChangeId?: string | null;
1336
+ remediationAgentRunId?: string | null;
1337
+ baselineRequestRatePerMin?: number | null;
1338
+ nowIso?: string;
1339
+ }): Promise<RemediationProposal>;
1340
+ declare function failRemediationProposal(db: SqlDb, projectId: string, proposalId: string, options: {
1341
+ environment?: string;
1342
+ failureReason: string;
1343
+ nowIso?: string;
1344
+ }): Promise<RemediationProposal>;
1345
+ interface WatchdogEvaluationSummary {
1346
+ evaluated: number;
1347
+ recovered: number;
1348
+ inconclusiveLowTraffic: number;
1349
+ stillDegraded: number;
1350
+ timedOutClaims: number;
1351
+ expiredGrants: number;
1352
+ }
1353
+ /**
1354
+ * Post-Remediation Watchdog & Lease/Claim Timeout Evaluator (RFC 0013 § 5.B, § 6.B & P1 #3).
1355
+ *
1356
+ * Strictly separates:
1357
+ * - `recovery_status`: whether the service recovered under comparable traffic (`N >= minComparableRequests`)
1358
+ * - `causal_verification_status`: `"operationally_corroborated"` (never promoted to `"counterfactual_verified"`
1359
+ * by telemetry recovery alone!).
1360
+ */
1361
+ declare function evaluateRemediationWatchdogs(db: SqlDb, options?: {
1362
+ nowIso?: string;
1363
+ minComparableRequests?: number;
1364
+ }): Promise<WatchdogEvaluationSummary>;
1365
+
796
1366
  /**
797
1367
  * Storage seam for opaque blob payloads — replay rrweb chunks and
798
1368
  * pprof profiles.
@@ -801,7 +1371,7 @@ declare class AlertsStore {
801
1371
  * - `R2BlobStore` — Cloudflare R2 binding (default on Workers).
802
1372
  * - `S3BlobStore` (sibling file) — AWS S3 / MinIO / any S3-compatible.
803
1373
  *
804
- * Replay chunks live under `replays/<session_id>/<chunk_id>`; pprof
1374
+ * Replay chunks live under `replays/<project_id>/<session_id>/<chunk_id>`; pprof
805
1375
  * profiles live under `profiles/<service>/<trace_id>/<span_id>`. The
806
1376
  * adapter never sees these paths — they're constructed by the callers
807
1377
  * (`replay-receiver`, `profile-routes`).
@@ -976,6 +1546,10 @@ interface EvalRunCandidate {
976
1546
  modelProvider: string | null;
977
1547
  model: string | null;
978
1548
  modelVersion: string | null;
1549
+ environment?: string | null;
1550
+ serviceVersion?: string | null;
1551
+ commitSha?: string | null;
1552
+ configDigest?: string | null;
979
1553
  }
980
1554
  interface EvalRunSourceCase {
981
1555
  id: string;
@@ -997,6 +1571,12 @@ interface EvalRun {
997
1571
  evalCaseId: string | null;
998
1572
  status: EvalRunStatus;
999
1573
  candidate: EvalRunCandidate;
1574
+ environment?: string;
1575
+ serviceVersion?: string | null;
1576
+ commitSha?: string | null;
1577
+ configDigest?: string | null;
1578
+ inputPayloadHash?: string | null;
1579
+ failureFingerprint?: string | null;
1000
1580
  startedAt: string | null;
1001
1581
  endedAt: string | null;
1002
1582
  totalCount: number;
@@ -1014,6 +1594,12 @@ interface EvalRunInput {
1014
1594
  evalCaseId?: string | null;
1015
1595
  status?: EvalRunStatus;
1016
1596
  candidate?: Partial<EvalRunCandidate>;
1597
+ environment?: string | null;
1598
+ serviceVersion?: string | null;
1599
+ commitSha?: string | null;
1600
+ configDigest?: string | null;
1601
+ inputPayloadHash?: string | null;
1602
+ failureFingerprint?: string | null;
1017
1603
  startedAt?: string | null;
1018
1604
  endedAt?: string | null;
1019
1605
  totalCount?: number;
@@ -1152,7 +1738,7 @@ declare class ProjectsStore {
1152
1738
  ensureDefaultProject(): Promise<void>;
1153
1739
  listKeys(projectId: string): Promise<IngestKey[]>;
1154
1740
  createKey(projectId: string, name: string): Promise<IngestKeyWithPlaintext>;
1155
- revokeKey(keyId: string): Promise<boolean>;
1741
+ revokeKey(keyId: string, projectId: string): Promise<boolean>;
1156
1742
  findByKeyHash(keyHash: string): Promise<{
1157
1743
  projectId: string;
1158
1744
  keyId: string;
@@ -1184,6 +1770,23 @@ declare class PostgresAdapter implements SqlDb {
1184
1770
  };
1185
1771
  }>>;
1186
1772
  }
1773
+ /**
1774
+ * Install `pg` result-type parsers so Postgres rows match the shape D1
1775
+ * returns and the shared stores keep working unchanged:
1776
+ *
1777
+ * - BOOLEAN → 0 / 1 (D1 stores flags as INTEGER; stores compare `=== 1`)
1778
+ * - BIGINT → number (`COUNT(*)`, `SUM(bigint)` would otherwise be strings;
1779
+ * integral values above MAX_SAFE_INTEGER throw)
1780
+ * - NUMERIC → number (`SUM`, `EXTRACT(EPOCH …)`; same overflow rule)
1781
+ * - JSON/JSONB → raw text (stores `JSON.parse` the column themselves)
1782
+ *
1783
+ * Call once per process before issuing queries, e.g.
1784
+ * `configurePostgresTypeParsers(pg.types)` in the Node entrypoint.
1785
+ */
1786
+ interface PostgresTypeParserRegistry {
1787
+ setTypeParser(oid: number, parser: (value: string) => unknown): void;
1788
+ }
1789
+ declare const configurePostgresTypeParsers: (types: PostgresTypeParserRegistry) => void;
1187
1790
 
1188
1791
  /** All built-in plugins in recommended registration order */
1189
1792
  declare const allPlugins: CollectorPlugin[];
@@ -1194,4 +1797,4 @@ declare const _default: hono.Hono<{
1194
1797
  Bindings: CollectorEnv;
1195
1798
  }, hono_types.BlankSchema, "/">;
1196
1799
 
1197
- export { type ActionRecord, ActionStore, type AgentRunRecord, AlertsStore, type ArtifactRecord, type BlobListOptions, type BlobListResult, type BlobObject, type BlobPutOptions, type BlobStore, BlobStoreToR2Adapter, type CollectorAuthConfig, type CollectorConfig, CollectorEnv, type CollectorPlugin, CollectorRouteContext, CollectorRuntime, D1Adapter, type EvalCase, type EvalCaseInput, type EvalCaseListOptions, type EvalCaseSourceLinks, EvalCaseSourceNotFoundError, type EvalCaseSourceType, EvalCasesStore, type EvalResultRecord, type EvalRun, type EvalRunCandidate, type EvalRunInput, type EvalRunSourceCase, type EvalRunStatus, type EvalRunsListOptions, Logger, MetricsStore, PostgresAdapter, type PostgresAdapterOptions, ProjectsStore, R2BlobStore, type RetrievalEventRecord, S3BlobStore, type S3BlobStoreOptions, type SpanProcessorPlugin, type SqlDb, type SqlDbFactory, type SqlStatement, type TailEvent, TailHub, type TailKind, type ToolCallRecord, type UsageEventProcessorPlugin, actionRoutesPlugin, aiReceiverPlugin, aiSpanPayloadsProcessorPlugin, alertsRoutesPlugin, allPlugins, analysesRoutesPlugin, askRoutesPlugin, botFilterPlugin, compareValue, connectedRoutesPlugin, createAlertEvaluatorHandler, createAnalysesRunHandler, createDashboardAuth, createDefaultCollectorApp, createRetentionCleanupHandler, createTelemetryCollectorApp, dashboardRoutesPlugin, _default as default, defaultSpanEnrichmentPlugin, evalCasesRoutesPlugin, evaluateAllRules, evidenceRetrievalRoutesPlugin, genAiNormalizerPlugin, identityReceiverPlugin, isEvalCaseSourceType, isEvalRunStatus, issueEnrichmentPlugin, issueInsightsPlugin, logsReceiverPlugin, metricsReceiverPlugin, onboardingRoutesPlugin, otlpReceiverPlugin, platformRoutesPlugin, profileRoutesPlugin, projectsRoutesPlugin, queryRoutesPlugin, redactionProcessorPlugin, replayQueryRoutesPlugin, replayReceiverPlugin, tailRoutesPlugin, timelineRoutesPlugin, uaEnrichmentPlugin, usageNormalizationPlugin, usagePrivacyPlugin, usageQueryRoutesPlugin, usageReceiverPlugin, usersQueryRoutesPlugin, utmEnrichmentPlugin };
1800
+ export { type ActionGraphPurgeSummary, type ActionGraphRetentionOptions, type ActionRecord, ActionStore, type AgentRunRecord, AlertsStore, type AnalysesRunSummary, type ArtifactRecord, type BlobListOptions, type BlobListResult, type BlobObject, type BlobPutOptions, type BlobStore, BlobStoreToR2Adapter, type CollectorAuthConfig, type CollectorConfig, CollectorEnv, type CollectorPlugin, CollectorRouteContext, CollectorRuntime, CounterfactualVerificationError, D1Adapter, type DashboardAuthConfig, type EvalCase, type EvalCaseInput, type EvalCaseListOptions, type EvalCaseSourceLinks, EvalCaseSourceNotFoundError, type EvalCaseSourceType, EvalCasesStore, type EvalResultRecord, type EvalRun, type EvalRunCandidate, type EvalRunInput, type EvalRunSourceCase, type EvalRunStatus, type EvalRunsListOptions, type HostLookup, Logger, MetricsStore, PostgresAdapter, type PostgresAdapterOptions, type PostgresTypeParserRegistry, ProjectsStore, R2BlobStore, RemediationGateError, type RetentionCleanupSummary, type RetrievalEventRecord, S3BlobStore, type S3BlobStoreOptions, type ScheduledTaskHandler, type SpanProcessorPlugin, type SqlDb, type SqlDbFactory, type SqlStatement, type TailEvent, TailHub, type TailKind, type ToolCallRecord, type UsageEventProcessorPlugin, type WatchdogEvaluationSummary, acquireIncidentLease, actionRetentionCutoff, actionRoutesPlugin, aiReceiverPlugin, aiSpanPayloadsProcessorPlugin, alertsRoutesPlugin, allPlugins, analysesRoutesPlugin, approveRemediationProposal, askRoutesPlugin, autosreRoutesPlugin, botFilterPlugin, claimRemediationGrant, compareValue, completeRemediationProposal, computeChangeBlastRadius, computeTarjanScc, configurePostgresTypeParsers, connectedRoutesPlugin, createAlertEvaluatorHandler, createAnalysesRunHandler, createDashboardAuth, createDefaultCollectorApp, createRetentionCleanupHandler, createTelemetryCollectorApp, dashboardRoutesPlugin, _default as default, defaultSpanEnrichmentPlugin, dispatchIncidentWebhook, evalCasesRoutesPlugin, evaluateAllRules, evaluateRemediationWatchdogs, evidenceRetrievalRoutesPlugin, extractSpanLineageAndTopology, failRemediationProposal, findCrossTraceContention, genAiNormalizerPlugin, getChangeEvent, getIncidentInvestigation, getInvestigationFinding, getOntologyGraph, getRemediationProposal, identityReceiverPlugin, isEvalCaseSourceType, isEvalRunStatus, issueEnrichmentPlugin, issueInsightsPlugin, listChangeEvents, listIncidentInvestigations, listRemediationProposals, loadTraceFaultLocusFromDb, localizeTraceFaultLocus, logsReceiverPlugin, metricsReceiverPlugin, onboardingRoutesPlugin, openOrDeduplicateIncident, otlpReceiverPlugin, platformRoutesPlugin, profileRoutesPlugin, projectsRoutesPlugin, proposeRemediationAction, purgeExpiredActionGraph, queryRoutesPlugin, recordChangeEvent, recordInvestigationFinding, redactionProcessorPlugin, rejectRemediationProposal, replayQueryRoutesPlugin, replayReceiverPlugin, searchInvestigationFindings, tailRoutesPlugin, timelineRoutesPlugin, uaEnrichmentPlugin, upsertOntologyFromSpans, usageNormalizationPlugin, usagePrivacyPlugin, usageQueryRoutesPlugin, usageReceiverPlugin, usersQueryRoutesPlugin, utmEnrichmentPlugin, verifyPairedCounterfactualProof };