@secondlayer/shared 13.11.0 → 13.13.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 (41) hide show
  1. package/dist/src/archive/range-digest.d.ts +125 -0
  2. package/dist/src/archive/semantic-digest-builder.d.ts +125 -0
  3. package/dist/src/coverage/index.d.ts +125 -0
  4. package/dist/src/db/index.d.ts +146 -2
  5. package/dist/src/db/index.js +11 -1
  6. package/dist/src/db/index.js.map +3 -3
  7. package/dist/src/db/queries/chain-reorgs.d.ts +125 -0
  8. package/dist/src/db/queries/chain-reorgs.js +11 -1
  9. package/dist/src/db/queries/chain-reorgs.js.map +3 -3
  10. package/dist/src/db/queries/contracts.d.ts +125 -0
  11. package/dist/src/db/queries/instance.d.ts +125 -0
  12. package/dist/src/db/queries/integrity.d.ts +125 -0
  13. package/dist/src/db/queries/subgraph-gaps.d.ts +125 -0
  14. package/dist/src/db/queries/subgraph-operations.d.ts +125 -0
  15. package/dist/src/db/queries/subgraphs.d.ts +125 -0
  16. package/dist/src/db/queries/sync-scope.d.ts +125 -0
  17. package/dist/src/db/queries/webhook-replays.d.ts +1431 -0
  18. package/dist/src/db/queries/webhook-replays.js +156 -0
  19. package/dist/src/db/queries/webhook-replays.js.map +10 -0
  20. package/dist/src/db/queries/webhooks.d.ts +125 -0
  21. package/dist/src/db/schema.d.ts +136 -2
  22. package/dist/src/index.d.ts +146 -2
  23. package/dist/src/index.js +15 -1
  24. package/dist/src/index.js.map +4 -4
  25. package/dist/src/insights/index.d.ts +142 -3
  26. package/dist/src/insights/index.js +40 -3
  27. package/dist/src/insights/index.js.map +3 -3
  28. package/dist/src/node/local-client.d.ts +125 -0
  29. package/dist/src/queue/listener.js +11 -1
  30. package/dist/src/queue/listener.js.map +3 -3
  31. package/dist/src/schemas/index.js +5 -1
  32. package/dist/src/schemas/index.js.map +3 -3
  33. package/dist/src/schemas/webhooks.d.ts +45 -1
  34. package/dist/src/schemas/webhooks.js +6 -1
  35. package/dist/src/schemas/webhooks.js.map +3 -3
  36. package/migrations/0177_oauth_mcp.ts +122 -0
  37. package/migrations/0178_insight_snooze.ts +22 -0
  38. package/migrations/0179_webhook_replays.ts +56 -0
  39. package/migrations/0180_push.ts +54 -0
  40. package/migrations/0181_push_sent.ts +30 -0
  41. package/package.json +5 -1
@@ -402,6 +402,7 @@ interface InsightsTable {
402
402
  clear_streak: Generated<number>;
403
403
  resolved_at: Date | null;
404
404
  dismissed_at: Date | null;
405
+ snoozed_until: Date | null;
405
406
  }
406
407
  /** "Expected": mutes one rule for one resource until removed. */
407
408
  interface InsightMutesTable {
@@ -895,6 +896,7 @@ interface Database {
895
896
  webhooks: WebhooksTable;
896
897
  webhook_outbox: WebhookOutboxTable;
897
898
  webhook_deliveries: WebhookDeliveriesTable;
899
+ webhook_replays: WebhookReplaysTable;
898
900
  trigger_evaluator_state: TriggerEvaluatorStateTable;
899
901
  decoded_events: DecodedEventsTable;
900
902
  decoder_checkpoints: DecoderCheckpointsTable;
@@ -928,6 +930,12 @@ interface Database {
928
930
  account_balance_alerts: AccountBalanceAlertsTable;
929
931
  webhook_incident_alerts: WebhookIncidentAlertsTable;
930
932
  cli_device_logins: CliDeviceLoginsTable;
933
+ oauth_clients: OauthClientsTable;
934
+ oauth_requests: OauthRequestsTable;
935
+ oauth_codes: OauthCodesTable;
936
+ oauth_grants: OauthGrantsTable;
937
+ oauth_tokens: OauthTokensTable;
938
+ oauth_refresh_retired: OauthRefreshRetiredTable;
931
939
  sentinel_accounts: SentinelAccountsTable;
932
940
  waitlists: WaitlistsTable;
933
941
  waitlist_signups: WaitlistSignupsTable;
@@ -937,6 +945,9 @@ interface Database {
937
945
  insights: InsightsTable;
938
946
  insight_mutes: InsightMutesTable;
939
947
  feedback_tickets: FeedbackTicketsTable;
948
+ push_subscriptions: PushSubscriptionsTable;
949
+ push_prefs: PushPrefsTable;
950
+ push_sent: PushSentTable;
940
951
  }
941
952
  interface ServiceHeartbeatsTable {
942
953
  name: string;
@@ -1029,6 +1040,36 @@ interface WebhookIncidentAlertsTable {
1029
1040
  email_attempts: Generated<number>;
1030
1041
  resolved_at: Date | null;
1031
1042
  }
1043
+ /** One browser/device push subscription; the endpoint is unique. */
1044
+ interface PushSubscriptionsTable {
1045
+ id: Generated<string>;
1046
+ account_id: string;
1047
+ endpoint: string;
1048
+ p256dh: string;
1049
+ auth: string;
1050
+ user_agent: string | null;
1051
+ created_at: Generated<Date>;
1052
+ last_success_at: Date | null;
1053
+ failed_count: Generated<number>;
1054
+ }
1055
+ /** Per-account push toggles. A missing row means the column defaults. */
1056
+ interface PushPrefsTable {
1057
+ account_id: string;
1058
+ webhook_failing: Generated<boolean>;
1059
+ subgraph_stopped: Generated<boolean>;
1060
+ subgraph_not_serving: Generated<boolean>;
1061
+ replay_done: Generated<boolean>;
1062
+ spend_cap: Generated<boolean>;
1063
+ key_repeat: Generated<boolean>;
1064
+ updated_at: Generated<Date>;
1065
+ }
1066
+ /** A push already sent, keyed by what it was about; debounces repeat signals. */
1067
+ interface PushSentTable {
1068
+ account_id: string;
1069
+ key: string;
1070
+ kind: string;
1071
+ sent_at: Generated<Date>;
1072
+ }
1032
1073
  type CliDeviceLoginStatus = "pending" | "approved" | "denied" | "consumed";
1033
1074
  /** The product that started a CLI sign-in; null is `sl login`. */
1034
1075
  type CliDeviceProduct = "sentinel";
@@ -1049,6 +1090,72 @@ interface CliDeviceLoginsTable {
1049
1090
  approved_at: Date | null;
1050
1091
  consumed_at: Date | null;
1051
1092
  }
1093
+ /** An app that registered itself (RFC 7591). Public clients only: no secret. */
1094
+ interface OauthClientsTable {
1095
+ id: string;
1096
+ /** The app's own claim, validated at registration; shown quoted. */
1097
+ name: string;
1098
+ redirect_uris: string[];
1099
+ created_ip: string | null;
1100
+ created_at: Generated<Date>;
1101
+ /** Set when it first gets tokens; never-used clients are purged. */
1102
+ last_used_at: Date | null;
1103
+ }
1104
+ type OauthRequestStatus = "pending" | "approved" | "denied";
1105
+ /** One authorize request waiting for (or answered on) the consent page. */
1106
+ interface OauthRequestsTable {
1107
+ id: string;
1108
+ client_id: string;
1109
+ redirect_uri: string;
1110
+ state: string | null;
1111
+ code_challenge: string;
1112
+ resource: string;
1113
+ status: Generated<OauthRequestStatus>;
1114
+ account_id: string | null;
1115
+ created_at: Generated<Date>;
1116
+ expires_at: Date;
1117
+ answered_at: Date | null;
1118
+ }
1119
+ /** A one-time authorization code, bound to its client, redirect, PKCE
1120
+ * challenge, resource and account. */
1121
+ interface OauthCodesTable {
1122
+ code_hash: string;
1123
+ client_id: string;
1124
+ account_id: string;
1125
+ redirect_uri: string;
1126
+ code_challenge: string;
1127
+ resource: string;
1128
+ /** The grant its one use made, so a replay can revoke it. */
1129
+ grant_id: string | null;
1130
+ created_at: Generated<Date>;
1131
+ expires_at: Date;
1132
+ used_at: Date | null;
1133
+ }
1134
+ /** What a person allowed one app to do: use the hosted MCP as them. */
1135
+ interface OauthGrantsTable {
1136
+ id: Generated<string>;
1137
+ account_id: string;
1138
+ client_id: string;
1139
+ scope: Generated<string>;
1140
+ resource: string;
1141
+ created_at: Generated<Date>;
1142
+ last_used_at: Date | null;
1143
+ revoked_at: Date | null;
1144
+ }
1145
+ type OauthTokenKind = "access" | "refresh";
1146
+ interface OauthTokensTable {
1147
+ token_hash: string;
1148
+ grant_id: string;
1149
+ kind: OauthTokenKind;
1150
+ created_at: Generated<Date>;
1151
+ expires_at: Date;
1152
+ }
1153
+ /** A refresh token a rotation replaced: presented again, it revokes the grant. */
1154
+ interface OauthRefreshRetiredTable {
1155
+ token_hash: string;
1156
+ grant_id: string;
1157
+ retired_at: Generated<Date>;
1158
+ }
1052
1159
  /** Accounts Sentinel's service key may touch: created by Sentinel, or the
1053
1160
  * owner opted in. */
1054
1161
  interface SentinelAccountsTable {
@@ -1106,6 +1213,8 @@ interface WebhookOutboxTable {
1106
1213
  next_attempt_at: Generated<Date>;
1107
1214
  status: ColumnType<OutboxStatus, OutboxStatus | undefined, OutboxStatus>;
1108
1215
  is_replay: Generated<boolean>;
1216
+ /** The replay that created this row; null for live rows. */
1217
+ replay_id: string | null;
1109
1218
  /** The chain block_time of the matched event/tx. Null for rows written
1110
1219
  * before this column existed, and for the settlement scan path (not yet
1111
1220
  * wired). Lets `delivered_at - block_time` measure end-to-end webhook
@@ -1121,6 +1230,22 @@ interface WebhookOutboxTable {
1121
1230
  last_error: string | null;
1122
1231
  created_at: Generated<Date>;
1123
1232
  }
1233
+ type WebhookReplayStatus = "scanning" | "running" | "done" | "failed";
1234
+ interface WebhookReplaysTable {
1235
+ id: string;
1236
+ webhook_id: string;
1237
+ from_block: number | bigint;
1238
+ to_block: number | bigint;
1239
+ status: WebhookReplayStatus;
1240
+ scanned_count: Generated<number>;
1241
+ enqueued_count: Generated<number>;
1242
+ total: number | null;
1243
+ delivered: number | null;
1244
+ gave_up: number | null;
1245
+ error: string | null;
1246
+ started_at: Generated<Date>;
1247
+ finished_at: Date | null;
1248
+ }
1124
1249
  interface WebhookDeliveriesTable {
1125
1250
  id: Generated<string>;
1126
1251
  /** Nullable after migration 0077 — outbox row may be cleaned up while
@@ -402,6 +402,7 @@ interface InsightsTable {
402
402
  clear_streak: Generated<number>;
403
403
  resolved_at: Date | null;
404
404
  dismissed_at: Date | null;
405
+ snoozed_until: Date | null;
405
406
  }
406
407
  /** "Expected": mutes one rule for one resource until removed. */
407
408
  interface InsightMutesTable {
@@ -895,6 +896,7 @@ interface Database {
895
896
  webhooks: WebhooksTable;
896
897
  webhook_outbox: WebhookOutboxTable;
897
898
  webhook_deliveries: WebhookDeliveriesTable;
899
+ webhook_replays: WebhookReplaysTable;
898
900
  trigger_evaluator_state: TriggerEvaluatorStateTable;
899
901
  decoded_events: DecodedEventsTable;
900
902
  decoder_checkpoints: DecoderCheckpointsTable;
@@ -928,6 +930,12 @@ interface Database {
928
930
  account_balance_alerts: AccountBalanceAlertsTable;
929
931
  webhook_incident_alerts: WebhookIncidentAlertsTable;
930
932
  cli_device_logins: CliDeviceLoginsTable;
933
+ oauth_clients: OauthClientsTable;
934
+ oauth_requests: OauthRequestsTable;
935
+ oauth_codes: OauthCodesTable;
936
+ oauth_grants: OauthGrantsTable;
937
+ oauth_tokens: OauthTokensTable;
938
+ oauth_refresh_retired: OauthRefreshRetiredTable;
931
939
  sentinel_accounts: SentinelAccountsTable;
932
940
  waitlists: WaitlistsTable;
933
941
  waitlist_signups: WaitlistSignupsTable;
@@ -937,6 +945,9 @@ interface Database {
937
945
  insights: InsightsTable;
938
946
  insight_mutes: InsightMutesTable;
939
947
  feedback_tickets: FeedbackTicketsTable;
948
+ push_subscriptions: PushSubscriptionsTable;
949
+ push_prefs: PushPrefsTable;
950
+ push_sent: PushSentTable;
940
951
  }
941
952
  interface ServiceHeartbeatsTable {
942
953
  name: string;
@@ -1029,6 +1040,36 @@ interface WebhookIncidentAlertsTable {
1029
1040
  email_attempts: Generated<number>;
1030
1041
  resolved_at: Date | null;
1031
1042
  }
1043
+ /** One browser/device push subscription; the endpoint is unique. */
1044
+ interface PushSubscriptionsTable {
1045
+ id: Generated<string>;
1046
+ account_id: string;
1047
+ endpoint: string;
1048
+ p256dh: string;
1049
+ auth: string;
1050
+ user_agent: string | null;
1051
+ created_at: Generated<Date>;
1052
+ last_success_at: Date | null;
1053
+ failed_count: Generated<number>;
1054
+ }
1055
+ /** Per-account push toggles. A missing row means the column defaults. */
1056
+ interface PushPrefsTable {
1057
+ account_id: string;
1058
+ webhook_failing: Generated<boolean>;
1059
+ subgraph_stopped: Generated<boolean>;
1060
+ subgraph_not_serving: Generated<boolean>;
1061
+ replay_done: Generated<boolean>;
1062
+ spend_cap: Generated<boolean>;
1063
+ key_repeat: Generated<boolean>;
1064
+ updated_at: Generated<Date>;
1065
+ }
1066
+ /** A push already sent, keyed by what it was about; debounces repeat signals. */
1067
+ interface PushSentTable {
1068
+ account_id: string;
1069
+ key: string;
1070
+ kind: string;
1071
+ sent_at: Generated<Date>;
1072
+ }
1032
1073
  type CliDeviceLoginStatus = "pending" | "approved" | "denied" | "consumed";
1033
1074
  /** The product that started a CLI sign-in; null is `sl login`. */
1034
1075
  type CliDeviceProduct = "sentinel";
@@ -1049,6 +1090,72 @@ interface CliDeviceLoginsTable {
1049
1090
  approved_at: Date | null;
1050
1091
  consumed_at: Date | null;
1051
1092
  }
1093
+ /** An app that registered itself (RFC 7591). Public clients only: no secret. */
1094
+ interface OauthClientsTable {
1095
+ id: string;
1096
+ /** The app's own claim, validated at registration; shown quoted. */
1097
+ name: string;
1098
+ redirect_uris: string[];
1099
+ created_ip: string | null;
1100
+ created_at: Generated<Date>;
1101
+ /** Set when it first gets tokens; never-used clients are purged. */
1102
+ last_used_at: Date | null;
1103
+ }
1104
+ type OauthRequestStatus = "pending" | "approved" | "denied";
1105
+ /** One authorize request waiting for (or answered on) the consent page. */
1106
+ interface OauthRequestsTable {
1107
+ id: string;
1108
+ client_id: string;
1109
+ redirect_uri: string;
1110
+ state: string | null;
1111
+ code_challenge: string;
1112
+ resource: string;
1113
+ status: Generated<OauthRequestStatus>;
1114
+ account_id: string | null;
1115
+ created_at: Generated<Date>;
1116
+ expires_at: Date;
1117
+ answered_at: Date | null;
1118
+ }
1119
+ /** A one-time authorization code, bound to its client, redirect, PKCE
1120
+ * challenge, resource and account. */
1121
+ interface OauthCodesTable {
1122
+ code_hash: string;
1123
+ client_id: string;
1124
+ account_id: string;
1125
+ redirect_uri: string;
1126
+ code_challenge: string;
1127
+ resource: string;
1128
+ /** The grant its one use made, so a replay can revoke it. */
1129
+ grant_id: string | null;
1130
+ created_at: Generated<Date>;
1131
+ expires_at: Date;
1132
+ used_at: Date | null;
1133
+ }
1134
+ /** What a person allowed one app to do: use the hosted MCP as them. */
1135
+ interface OauthGrantsTable {
1136
+ id: Generated<string>;
1137
+ account_id: string;
1138
+ client_id: string;
1139
+ scope: Generated<string>;
1140
+ resource: string;
1141
+ created_at: Generated<Date>;
1142
+ last_used_at: Date | null;
1143
+ revoked_at: Date | null;
1144
+ }
1145
+ type OauthTokenKind = "access" | "refresh";
1146
+ interface OauthTokensTable {
1147
+ token_hash: string;
1148
+ grant_id: string;
1149
+ kind: OauthTokenKind;
1150
+ created_at: Generated<Date>;
1151
+ expires_at: Date;
1152
+ }
1153
+ /** A refresh token a rotation replaced: presented again, it revokes the grant. */
1154
+ interface OauthRefreshRetiredTable {
1155
+ token_hash: string;
1156
+ grant_id: string;
1157
+ retired_at: Generated<Date>;
1158
+ }
1052
1159
  /** Accounts Sentinel's service key may touch: created by Sentinel, or the
1053
1160
  * owner opted in. */
1054
1161
  interface SentinelAccountsTable {
@@ -1106,6 +1213,8 @@ interface WebhookOutboxTable {
1106
1213
  next_attempt_at: Generated<Date>;
1107
1214
  status: ColumnType<OutboxStatus, OutboxStatus | undefined, OutboxStatus>;
1108
1215
  is_replay: Generated<boolean>;
1216
+ /** The replay that created this row; null for live rows. */
1217
+ replay_id: string | null;
1109
1218
  /** The chain block_time of the matched event/tx. Null for rows written
1110
1219
  * before this column existed, and for the settlement scan path (not yet
1111
1220
  * wired). Lets `delivered_at - block_time` measure end-to-end webhook
@@ -1121,6 +1230,22 @@ interface WebhookOutboxTable {
1121
1230
  last_error: string | null;
1122
1231
  created_at: Generated<Date>;
1123
1232
  }
1233
+ type WebhookReplayStatus = "scanning" | "running" | "done" | "failed";
1234
+ interface WebhookReplaysTable {
1235
+ id: string;
1236
+ webhook_id: string;
1237
+ from_block: number | bigint;
1238
+ to_block: number | bigint;
1239
+ status: WebhookReplayStatus;
1240
+ scanned_count: Generated<number>;
1241
+ enqueued_count: Generated<number>;
1242
+ total: number | null;
1243
+ delivered: number | null;
1244
+ gave_up: number | null;
1245
+ error: string | null;
1246
+ started_at: Generated<Date>;
1247
+ finished_at: Date | null;
1248
+ }
1124
1249
  interface WebhookDeliveriesTable {
1125
1250
  id: Generated<string>;
1126
1251
  /** Nullable after migration 0077 — outbox row may be cleaned up while
@@ -719,6 +719,7 @@ interface InsightsTable {
719
719
  clear_streak: Generated<number>;
720
720
  resolved_at: Date | null;
721
721
  dismissed_at: Date | null;
722
+ snoozed_until: Date | null;
722
723
  }
723
724
  /** "Expected": mutes one rule for one resource until removed. */
724
725
  interface InsightMutesTable {
@@ -1212,6 +1213,7 @@ interface Database {
1212
1213
  webhooks: WebhooksTable;
1213
1214
  webhook_outbox: WebhookOutboxTable;
1214
1215
  webhook_deliveries: WebhookDeliveriesTable;
1216
+ webhook_replays: WebhookReplaysTable;
1215
1217
  trigger_evaluator_state: TriggerEvaluatorStateTable;
1216
1218
  decoded_events: DecodedEventsTable;
1217
1219
  decoder_checkpoints: DecoderCheckpointsTable;
@@ -1245,6 +1247,12 @@ interface Database {
1245
1247
  account_balance_alerts: AccountBalanceAlertsTable;
1246
1248
  webhook_incident_alerts: WebhookIncidentAlertsTable;
1247
1249
  cli_device_logins: CliDeviceLoginsTable;
1250
+ oauth_clients: OauthClientsTable;
1251
+ oauth_requests: OauthRequestsTable;
1252
+ oauth_codes: OauthCodesTable;
1253
+ oauth_grants: OauthGrantsTable;
1254
+ oauth_tokens: OauthTokensTable;
1255
+ oauth_refresh_retired: OauthRefreshRetiredTable;
1248
1256
  sentinel_accounts: SentinelAccountsTable;
1249
1257
  waitlists: WaitlistsTable;
1250
1258
  waitlist_signups: WaitlistSignupsTable;
@@ -1254,6 +1262,9 @@ interface Database {
1254
1262
  insights: InsightsTable;
1255
1263
  insight_mutes: InsightMutesTable;
1256
1264
  feedback_tickets: FeedbackTicketsTable;
1265
+ push_subscriptions: PushSubscriptionsTable;
1266
+ push_prefs: PushPrefsTable;
1267
+ push_sent: PushSentTable;
1257
1268
  }
1258
1269
  interface ServiceHeartbeatsTable {
1259
1270
  name: string;
@@ -1346,6 +1357,36 @@ interface WebhookIncidentAlertsTable {
1346
1357
  email_attempts: Generated<number>;
1347
1358
  resolved_at: Date | null;
1348
1359
  }
1360
+ /** One browser/device push subscription; the endpoint is unique. */
1361
+ interface PushSubscriptionsTable {
1362
+ id: Generated<string>;
1363
+ account_id: string;
1364
+ endpoint: string;
1365
+ p256dh: string;
1366
+ auth: string;
1367
+ user_agent: string | null;
1368
+ created_at: Generated<Date>;
1369
+ last_success_at: Date | null;
1370
+ failed_count: Generated<number>;
1371
+ }
1372
+ /** Per-account push toggles. A missing row means the column defaults. */
1373
+ interface PushPrefsTable {
1374
+ account_id: string;
1375
+ webhook_failing: Generated<boolean>;
1376
+ subgraph_stopped: Generated<boolean>;
1377
+ subgraph_not_serving: Generated<boolean>;
1378
+ replay_done: Generated<boolean>;
1379
+ spend_cap: Generated<boolean>;
1380
+ key_repeat: Generated<boolean>;
1381
+ updated_at: Generated<Date>;
1382
+ }
1383
+ /** A push already sent, keyed by what it was about; debounces repeat signals. */
1384
+ interface PushSentTable {
1385
+ account_id: string;
1386
+ key: string;
1387
+ kind: string;
1388
+ sent_at: Generated<Date>;
1389
+ }
1349
1390
  type CliDeviceLoginStatus = "pending" | "approved" | "denied" | "consumed";
1350
1391
  /** The product that started a CLI sign-in; null is `sl login`. */
1351
1392
  type CliDeviceProduct = "sentinel";
@@ -1366,6 +1407,72 @@ interface CliDeviceLoginsTable {
1366
1407
  approved_at: Date | null;
1367
1408
  consumed_at: Date | null;
1368
1409
  }
1410
+ /** An app that registered itself (RFC 7591). Public clients only: no secret. */
1411
+ interface OauthClientsTable {
1412
+ id: string;
1413
+ /** The app's own claim, validated at registration; shown quoted. */
1414
+ name: string;
1415
+ redirect_uris: string[];
1416
+ created_ip: string | null;
1417
+ created_at: Generated<Date>;
1418
+ /** Set when it first gets tokens; never-used clients are purged. */
1419
+ last_used_at: Date | null;
1420
+ }
1421
+ type OauthRequestStatus = "pending" | "approved" | "denied";
1422
+ /** One authorize request waiting for (or answered on) the consent page. */
1423
+ interface OauthRequestsTable {
1424
+ id: string;
1425
+ client_id: string;
1426
+ redirect_uri: string;
1427
+ state: string | null;
1428
+ code_challenge: string;
1429
+ resource: string;
1430
+ status: Generated<OauthRequestStatus>;
1431
+ account_id: string | null;
1432
+ created_at: Generated<Date>;
1433
+ expires_at: Date;
1434
+ answered_at: Date | null;
1435
+ }
1436
+ /** A one-time authorization code, bound to its client, redirect, PKCE
1437
+ * challenge, resource and account. */
1438
+ interface OauthCodesTable {
1439
+ code_hash: string;
1440
+ client_id: string;
1441
+ account_id: string;
1442
+ redirect_uri: string;
1443
+ code_challenge: string;
1444
+ resource: string;
1445
+ /** The grant its one use made, so a replay can revoke it. */
1446
+ grant_id: string | null;
1447
+ created_at: Generated<Date>;
1448
+ expires_at: Date;
1449
+ used_at: Date | null;
1450
+ }
1451
+ /** What a person allowed one app to do: use the hosted MCP as them. */
1452
+ interface OauthGrantsTable {
1453
+ id: Generated<string>;
1454
+ account_id: string;
1455
+ client_id: string;
1456
+ scope: Generated<string>;
1457
+ resource: string;
1458
+ created_at: Generated<Date>;
1459
+ last_used_at: Date | null;
1460
+ revoked_at: Date | null;
1461
+ }
1462
+ type OauthTokenKind = "access" | "refresh";
1463
+ interface OauthTokensTable {
1464
+ token_hash: string;
1465
+ grant_id: string;
1466
+ kind: OauthTokenKind;
1467
+ created_at: Generated<Date>;
1468
+ expires_at: Date;
1469
+ }
1470
+ /** A refresh token a rotation replaced: presented again, it revokes the grant. */
1471
+ interface OauthRefreshRetiredTable {
1472
+ token_hash: string;
1473
+ grant_id: string;
1474
+ retired_at: Generated<Date>;
1475
+ }
1369
1476
  /** Accounts Sentinel's service key may touch: created by Sentinel, or the
1370
1477
  * owner opted in. */
1371
1478
  interface SentinelAccountsTable {
@@ -1423,6 +1530,8 @@ interface WebhookOutboxTable {
1423
1530
  next_attempt_at: Generated<Date>;
1424
1531
  status: ColumnType<OutboxStatus, OutboxStatus | undefined, OutboxStatus>;
1425
1532
  is_replay: Generated<boolean>;
1533
+ /** The replay that created this row; null for live rows. */
1534
+ replay_id: string | null;
1426
1535
  /** The chain block_time of the matched event/tx. Null for rows written
1427
1536
  * before this column existed, and for the settlement scan path (not yet
1428
1537
  * wired). Lets `delivered_at - block_time` measure end-to-end webhook
@@ -1438,6 +1547,22 @@ interface WebhookOutboxTable {
1438
1547
  last_error: string | null;
1439
1548
  created_at: Generated<Date>;
1440
1549
  }
1550
+ type WebhookReplayStatus = "scanning" | "running" | "done" | "failed";
1551
+ interface WebhookReplaysTable {
1552
+ id: string;
1553
+ webhook_id: string;
1554
+ from_block: number | bigint;
1555
+ to_block: number | bigint;
1556
+ status: WebhookReplayStatus;
1557
+ scanned_count: Generated<number>;
1558
+ enqueued_count: Generated<number>;
1559
+ total: number | null;
1560
+ delivered: number | null;
1561
+ gave_up: number | null;
1562
+ error: string | null;
1563
+ started_at: Generated<Date>;
1564
+ finished_at: Date | null;
1565
+ }
1441
1566
  interface WebhookDeliveriesTable {
1442
1567
  id: Generated<string>;
1443
1568
  /** Nullable after migration 0077 — outbox row may be cleaned up while