deepline 0.3.365 → 0.3.367

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 (99) hide show
  1. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  2. package/dist/bundling-sources/shared_libs/observability/scheduled-jobs.json +36 -18
  3. package/dist/bundling-sources/shared_libs/observability/scheduled-work-manual.ts +2 -0
  4. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +53 -20
  5. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-dispatch-authority.ts +9 -2
  6. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-event-search.ts +25 -13
  7. package/dist/bundling-sources/shared_libs/security/receipt-admission-token.ts +69 -0
  8. package/dist/cli/{auth-4S26H44S.mjs → auth-2DHYG3WZ.mjs} +4 -4
  9. package/dist/cli/auth-I2E45QZK.js +33 -0
  10. package/dist/cli/{billing-IMSQT3SR.mjs → billing-VFBFBOO5.mjs} +4 -4
  11. package/dist/cli/{billing-JD3SGKJN.js → billing-YBTOTHXM.js} +73 -73
  12. package/dist/cli/{chunk-4LQ3MCZU.js → chunk-3BZR22CZ.js} +60 -60
  13. package/dist/cli/{chunk-QGVNIGCS.mjs → chunk-3Y3N7WQO.mjs} +1 -1
  14. package/dist/cli/{chunk-YME4BTRK.mjs → chunk-3ZO2O2LE.mjs} +2 -2
  15. package/dist/cli/{chunk-E66FPPCT.js → chunk-5NVMNFCG.js} +15 -15
  16. package/dist/cli/{chunk-VHWW76SC.mjs → chunk-6DRGO4CB.mjs} +6 -6
  17. package/dist/cli/{chunk-UIZZ6CC6.js → chunk-AVSPBILZ.js} +8 -8
  18. package/dist/cli/{chunk-FTP4HYWY.js → chunk-B7JTGFDS.js} +1 -1
  19. package/dist/cli/{chunk-NFBQAQRE.mjs → chunk-BBQ5IWRX.mjs} +5 -5
  20. package/dist/cli/{chunk-VZRENGJ2.js → chunk-CRRVBNZD.js} +8 -8
  21. package/dist/cli/{chunk-XA7MAC3G.js → chunk-D7SELX66.js} +6 -6
  22. package/dist/cli/{chunk-XH7A4IWD.mjs → chunk-DIDYT2TH.mjs} +2 -2
  23. package/dist/cli/{chunk-YKRINWA2.mjs → chunk-GD2VVYGB.mjs} +1 -1
  24. package/dist/cli/{chunk-7MS7JCBB.mjs → chunk-GXRUBTXZ.mjs} +2 -2
  25. package/dist/cli/{chunk-QTBWSCF4.mjs → chunk-HEWGTHI2.mjs} +3 -3
  26. package/dist/cli/{chunk-6RX64O34.mjs → chunk-JNQECSK5.mjs} +1 -1
  27. package/dist/cli/{chunk-NDQBGF6U.mjs → chunk-K4V4FX76.mjs} +1 -1
  28. package/dist/cli/{chunk-HNZNRS6I.js → chunk-KJFND3YT.js} +9 -9
  29. package/dist/cli/{chunk-6ZZX3NVM.mjs → chunk-LDSBBKSM.mjs} +2 -2
  30. package/dist/cli/{chunk-XPYEE2AU.js → chunk-LJIWLZHS.js} +45 -45
  31. package/dist/cli/{chunk-LYLMIZIK.js → chunk-MKRADCZK.js} +26 -26
  32. package/dist/cli/{chunk-HHZIOAWI.js → chunk-MXJEKMHU.js} +224 -224
  33. package/dist/cli/{chunk-5OMKEQGL.mjs → chunk-OCX5D2WI.mjs} +1 -1
  34. package/dist/cli/{chunk-L7CUNWVR.js → chunk-R2HHZLOP.js} +4 -4
  35. package/dist/cli/{chunk-WJ4IGJR4.mjs → chunk-W2LUUB3U.mjs} +4 -4
  36. package/dist/cli/{chunk-QNJZROLE.js → chunk-XPIZKTYZ.js} +50 -50
  37. package/dist/cli/{chunk-7MF27ZQA.js → chunk-Z35CB3I5.js} +2 -2
  38. package/dist/cli/{client-TIT2YEEY.mjs → client-F6SEXBBG.mjs} +3 -3
  39. package/dist/cli/client-VWGJIUGH.js +20 -0
  40. package/dist/cli/{csv-XYARLBKW.js → csv-4AB42QFI.js} +12 -12
  41. package/dist/cli/{csv-64W5LJIT.mjs → csv-DC3MLBOC.mjs} +3 -3
  42. package/dist/cli/{db-BGC5QKV6.mjs → db-JAQHMLUY.mjs} +4 -4
  43. package/dist/cli/{db-52N7JAVE.js → db-M3URDTLR.js} +29 -29
  44. package/dist/cli/{describe-DPDNIXVH.js → describe-AVXUIRDK.js} +15 -15
  45. package/dist/cli/{describe-3ZSRFCHD.mjs → describe-RYXCBLJG.mjs} +8 -8
  46. package/dist/cli/{executions-WVIUSOSA.js → executions-DYDW4WY3.js} +6 -6
  47. package/dist/cli/{executions-ZIMU47UG.mjs → executions-XPY5FKOS.mjs} +4 -4
  48. package/dist/cli/{feedback-VS5A3KAR.js → feedback-HMP2ZYNW.js} +7 -7
  49. package/dist/cli/{feedback-7Z2PUW4R.mjs → feedback-ZOI3GFAC.mjs} +3 -3
  50. package/dist/cli/{git-JTIF7PN4.mjs → git-K35PB25D.mjs} +4 -4
  51. package/dist/cli/git-TJEM2U4K.js +23 -0
  52. package/dist/cli/index.js +82 -82
  53. package/dist/cli/index.mjs +32 -32
  54. package/dist/cli/{legacy-noops-7IR7H7N4.mjs → legacy-noops-PKGJRS75.mjs} +4 -4
  55. package/dist/cli/{legacy-noops-B2WDO6FW.js → legacy-noops-QNNMKQDW.js} +7 -7
  56. package/dist/cli/{monitors-LWBSJ3OX.mjs → monitors-JMNBMRSQ.mjs} +4 -4
  57. package/dist/cli/{monitors-OESDIJVL.js → monitors-MUMWTBID.js} +85 -85
  58. package/dist/cli/{org-ENJDFLCZ.js → org-2QL6AY6G.js} +64 -64
  59. package/dist/cli/{org-T7OXEG5L.mjs → org-AQPMMPQE.mjs} +4 -4
  60. package/dist/cli/{play-FOB5OCJ3.mjs → play-AITTTWYK.mjs} +7 -7
  61. package/dist/cli/play-YO52D4YP.js +153 -0
  62. package/dist/cli/{providers-3OGXEWHS.js → providers-BGTPNAOA.js} +7 -7
  63. package/dist/cli/{providers-TU3QNHIS.mjs → providers-LRCYLIQA.mjs} +4 -4
  64. package/dist/cli/{quickstart-VSVPWMNU.mjs → quickstart-3II563Z7.mjs} +3 -3
  65. package/dist/cli/{quickstart-DM4UGC7C.js → quickstart-WMAUXWKW.js} +5 -5
  66. package/dist/cli/search-FQ5NSE7R.js +26 -0
  67. package/dist/cli/{search-CBHKG7YA.mjs → search-J6ELRZNH.mjs} +5 -5
  68. package/dist/cli/{secrets-YTN4ZA4T.js → secrets-HDBB2647.js} +13 -13
  69. package/dist/cli/{secrets-6ZQF4M27.mjs → secrets-L7LOTLZB.mjs} +4 -4
  70. package/dist/cli/{sessions-BPRYFAWJ.mjs → sessions-7DMN24LF.mjs} +4 -4
  71. package/dist/cli/{sessions-ZO7IOGUF.js → sessions-ZO3WB3EF.js} +5 -5
  72. package/dist/cli/{settings-N65YB4QA.js → settings-OVUM5BLZ.js} +74 -74
  73. package/dist/cli/{settings-WYCYXUM7.mjs → settings-USHCF4YD.mjs} +7 -7
  74. package/dist/cli/{setup-KLEY2DJT.mjs → setup-FZQ7HDL4.mjs} +6 -6
  75. package/dist/cli/{setup-RW4BMAKA.js → setup-W77Y5ALX.js} +41 -41
  76. package/dist/cli/{skills-DFXLS3KI.mjs → skills-A2LR3RAP.mjs} +4 -4
  77. package/dist/cli/skills-GLKEZPFV.js +28 -0
  78. package/dist/cli/{tools-UDSCRFZN.mjs → tools-FX7G7KX5.mjs} +6 -6
  79. package/dist/cli/tools-HBNJ3WP3.js +40 -0
  80. package/dist/cli/update-A2TZZO2S.js +28 -0
  81. package/dist/cli/{update-3MKDIO3Y.mjs → update-EODE5FHU.mjs} +6 -6
  82. package/dist/cli/{workflow-XNYXVEVF.mjs → workflow-7352UH6L.mjs} +5 -5
  83. package/dist/cli/{workflow-64Y2B5YZ.js → workflow-JEY4ESZA.js} +15 -15
  84. package/dist/index.js +6 -6
  85. package/dist/index.mjs +3 -3
  86. package/dist/install-integrity.json +74 -74
  87. package/dist/release.d.mts +1 -1
  88. package/dist/release.d.ts +1 -1
  89. package/dist/release.js +2 -2
  90. package/dist/release.mjs +1 -1
  91. package/package.json +1 -1
  92. package/dist/cli/auth-AXY27RN4.js +0 -33
  93. package/dist/cli/client-OBQBVVUE.js +0 -20
  94. package/dist/cli/git-QJYLQED2.js +0 -23
  95. package/dist/cli/play-Z6SXXNP5.js +0 -153
  96. package/dist/cli/search-26IGFUMM.js +0 -26
  97. package/dist/cli/skills-XTGVE76M.js +0 -28
  98. package/dist/cli/tools-AK4XQBQJ.js +0 -40
  99. package/dist/cli/update-NIIEFTPX.js +0 -28
@@ -200,7 +200,7 @@ export const SDK_RELEASE = {
200
200
  // getters keep their established compatibility behavior.
201
201
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
202
202
  // 0.3.90 is the first deliberately versioned SDK release for API v3.
203
- version: '0.3.365',
203
+ version: '0.3.367',
204
204
  updateSummary:
205
205
  'Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.',
206
206
  packageCapabilities: {
@@ -9,7 +9,7 @@
9
9
  "locator": "schedule:",
10
10
  "owner": "product",
11
11
  "criticality": "critical",
12
- "purpose": "Run clean Sonnet/Claude Code and Luna/Codex installs from the production homepage prompt through browser claim completion and the agent’s complete starter handoff.",
12
+ "purpose": "Run clean Sonnet/Claude Code and Luna/Codex installs from the production homepage prompt through browser claim completion and the agent\u2019s complete starter handoff.",
13
13
  "successDefinition": "Both latest coding harnesses install Deepline without installation warnings or errors, finish browser authorization and setup, present all four starter choices as a card or text, and verify test key revocation; after seven valid daily runs each finishes within 240 seconds.",
14
14
  "failureDefinition": "Either agent, browser claim completion, read-only setup verification, complete starter handoff, verified key cleanup, or the applicable timing gate fails.",
15
15
  "startGraceMs": 900000,
@@ -1304,37 +1304,37 @@
1304
1304
  {
1305
1305
  "id": "github.credit-purchase-readiness-scan",
1306
1306
  "name": "Credit Purchase Readiness Daily Scan",
1307
- "scheduler": "github_actions",
1308
- "cadence": "0 8 * * *",
1309
- "timezone": "America/New_York",
1310
- "source": ".github/workflows/credit-purchase-readiness-scan.yml",
1311
- "locator": "schedule:",
1307
+ "scheduler": "convex_cron",
1308
+ "cadence": "0 12 * * *",
1309
+ "timezone": "UTC",
1310
+ "source": "packages/convex/crons.ts",
1311
+ "locator": "credit purchase readiness daily scan",
1312
1312
  "owner": "billing",
1313
1313
  "criticality": "critical",
1314
- "purpose": "Inspect every workspace through the production read-only credit addition check and retain private evidence.",
1315
- "successDefinition": "A complete, source-certified scan/report is retained and aggregate telemetry is ingested; account findings remain visible in the Better Stack monitor.",
1316
- "failureDefinition": "The scan or report is missing, incomplete, inaccessible, or cannot publish telemetry.",
1314
+ "purpose": "Start a durable read-only credit readiness sweep; each continuation checks one workspace.",
1315
+ "successDefinition": "The scan intent and first page are atomically scheduled; the morning report verifies fleet completion.",
1316
+ "failureDefinition": "The scan cannot be started or its daily completion evidence is unavailable.",
1317
1317
  "startGraceMs": 900000,
1318
1318
  "runTimeoutMs": 3000000,
1319
- "failureAlert": "github_workflow_run",
1319
+ "failureAlert": "convex_action_wrapper",
1320
1320
  "domainReportRequired": true
1321
1321
  },
1322
1322
  {
1323
1323
  "id": "github.credit-purchase-readiness-morning",
1324
1324
  "name": "Credit Purchase Readiness Morning Report",
1325
- "scheduler": "github_actions",
1326
- "cadence": "0 9 * * *",
1327
- "timezone": "America/New_York",
1328
- "source": ".github/workflows/credit-purchase-readiness-morning.yml",
1329
- "locator": "schedule:",
1325
+ "scheduler": "convex_cron",
1326
+ "cadence": "0 13 * * *",
1327
+ "timezone": "UTC",
1328
+ "source": "packages/convex/crons.ts",
1329
+ "locator": "credit purchase readiness morning report",
1330
1330
  "owner": "billing",
1331
1331
  "criticality": "critical",
1332
- "purpose": "Publish daily issue counts and feed the Axiom to Better Stack readiness alert at 9 a.m. Eastern.",
1333
- "successDefinition": "A complete, source-certified scan/report is retained and aggregate telemetry is ingested; account findings remain visible in the Better Stack monitor.",
1332
+ "purpose": "Publish daily credit readiness counts through existing Convex telemetry.",
1333
+ "successDefinition": "Today\u2019s complete scan is reported to Axiom; purchase defects and unknown prerequisites remain visible in Better Stack.",
1334
1334
  "failureDefinition": "The scan or report is missing, incomplete, inaccessible, or cannot publish telemetry.",
1335
1335
  "startGraceMs": 900000,
1336
1336
  "runTimeoutMs": 600000,
1337
- "failureAlert": "github_workflow_run",
1337
+ "failureAlert": "convex_action_wrapper",
1338
1338
  "domainReportRequired": true
1339
1339
  },
1340
1340
  {
@@ -1354,5 +1354,23 @@
1354
1354
  "runTimeoutMs": 120000,
1355
1355
  "failureAlert": "convex_action_wrapper",
1356
1356
  "domainReportRequired": true
1357
+ },
1358
+ {
1359
+ "id": "convex.credit-purchase-readiness-page",
1360
+ "name": "Credit purchase readiness page",
1361
+ "scheduler": "convex_scheduled_action",
1362
+ "cadence": "runAfter previous credit readiness checkpoint",
1363
+ "timezone": "UTC",
1364
+ "source": "packages/convex/scheduledJobsNode.ts",
1365
+ "locator": "checkCreditReadinessPage",
1366
+ "owner": "billing",
1367
+ "criticality": "critical",
1368
+ "purpose": "Inspect one workspace without a payment or grant and commit a fenced aggregate checkpoint.",
1369
+ "successDefinition": "A read-only page is committed once with its next continuation, or a stale delivery is skipped.",
1370
+ "failureDefinition": "A callback or checkpoint fails; the scan remains incomplete and the daily report pages.",
1371
+ "startGraceMs": 120000,
1372
+ "runTimeoutMs": 300000,
1373
+ "failureAlert": "convex_action_wrapper",
1374
+ "domainReportRequired": true
1357
1375
  }
1358
1376
  ]
@@ -9,6 +9,8 @@
9
9
  */
10
10
  export const MANUALLY_RUNNABLE_CONVEX_CRON_JOB_IDS = [
11
11
  // These persisted operations ids predate the move of their clocks to Convex.
12
+ 'github.credit-purchase-readiness-scan',
13
+ 'github.credit-purchase-readiness-morning',
12
14
  'inngest.play-cron-tick',
13
15
  'convex.fold-usage-rollups',
14
16
  'convex.cleanup-expired-org-day-usage-shards',
@@ -17960,14 +17960,18 @@ export async function renewPostgresRunLedgerDelivery(
17960
17960
  options?: Pick<PostgresSchedulerOptions, 'schema'>,
17961
17961
  ): Promise<void> {
17962
17962
  const renew = (freezeReceipt: boolean) =>
17963
- client.query<{ event_id: string }>(
17963
+ client.query<{ event_id: string; pending: boolean }>(
17964
17964
  `
17965
- UPDATE ${tableName('outbox', options)}
17965
+ WITH ${outboxByEventIdSql('target', '$1', options)}
17966
+ UPDATE ${tableName('outbox', options)} AS outbox
17966
17967
  SET lease_expires_at = now() + make_interval(secs => $3::integer)
17967
- ${freezeReceipt ? ', delivery_batch_started_at = coalesce(delivery_batch_started_at, statement_timestamp())' : ''}
17968
- WHERE event_id = ANY($1::text[]) AND lease_owner = $2
17969
- AND delivered_at IS NULL AND failed_at IS NULL AND lease_expires_at > now()
17970
- RETURNING event_id
17968
+ ${freezeReceipt ? ', delivery_batch_started_at = coalesce(outbox.delivery_batch_started_at, statement_timestamp())' : ''}
17969
+ FROM target
17970
+ WHERE outbox.event_id = target.event_id
17971
+ AND target.delivered_at IS NULL AND target.failed_at IS NULL
17972
+ AND outbox.lease_owner = $2 AND outbox.lease_expires_at > now()
17973
+ RETURNING outbox.event_id,
17974
+ outbox.delivered_at IS NULL AND outbox.failed_at IS NULL AS pending
17971
17975
  `,
17972
17976
  [
17973
17977
  input.eventIds,
@@ -17987,10 +17991,36 @@ export async function renewPostgresRunLedgerDelivery(
17987
17991
  markOutboxDeliveryBatchMarkerUnavailable(options);
17988
17992
  result = await renew(false);
17989
17993
  }
17990
- if (result.rows.length !== input.eventIds.length)
17994
+ if (
17995
+ result.rows.length !== input.eventIds.length ||
17996
+ result.rows.some((row) => !row.pending)
17997
+ )
17991
17998
  throw new RunLedgerProjectionLeaseLostError('delivery_lease_not_current');
17992
17999
  }
17993
18000
 
18001
+ /**
18002
+ * Outbox rows named by event ID, read through the primary key alone. Lease
18003
+ * statements filter this snapshot's state rather than the outbox scan: a
18004
+ * pending-state predicate there lets a plan built from a drained outbox's
18005
+ * statistics scan a whole pending partial index for every ID. An UPDATE that
18006
+ * joins it re-checks its lease predicates on a concurrent change and returns
18007
+ * the state of the version it wrote.
18008
+ */
18009
+ function outboxByEventIdSql(
18010
+ alias: string,
18011
+ eventIdsParameter: string,
18012
+ options: Pick<PostgresSchedulerOptions, 'schema'> | undefined,
18013
+ forUpdate = false,
18014
+ ): string {
18015
+ return `${alias} AS MATERIALIZED (
18016
+ SELECT event_id, run_id, lease_owner, lease_expires_at,
18017
+ delivered_at, failed_at, projection_attempts
18018
+ FROM ${tableName('outbox', options)}
18019
+ WHERE event_id = ANY(${eventIdsParameter}::text[])
18020
+ ${forUpdate ? 'FOR UPDATE' : ''}
18021
+ )`;
18022
+ }
18023
+
17994
18024
  // A projection receipt has always admitted at most eight ordered outbox facts.
17995
18025
  // Share that existing boundary between its SQL snapshot and prefix validation.
17996
18026
  const RUN_LEDGER_PROJECTION_PREFIX_MAX_FACTS = 8;
@@ -19266,14 +19296,13 @@ export async function settlePostgresSchedulerOutboxFailure(
19266
19296
  blocked: boolean;
19267
19297
  }>(
19268
19298
  `
19269
- WITH current_outbox AS (
19299
+ WITH ${outboxByEventIdSql('locked', 'ARRAY[$1::text]', options, true)},
19300
+ current_outbox AS (
19270
19301
  SELECT event_id, projection_attempts
19271
- FROM ${tableName('outbox', options)}
19272
- WHERE event_id = $1
19273
- AND delivered_at IS NULL
19302
+ FROM locked
19303
+ WHERE delivered_at IS NULL
19274
19304
  AND failed_at IS NULL
19275
19305
  AND ($4::text IS NULL OR lease_owner = $4)
19276
- FOR UPDATE
19277
19306
  ), settled AS (
19278
19307
  UPDATE ${tableName('outbox', options)} outbox
19279
19308
  SET projection_attempts = current_outbox.projection_attempts + 1,
@@ -19461,14 +19490,16 @@ export async function releasePostgresSchedulerOutboxLease(
19461
19490
  if (eventIds.length === 0 || !leaseOwner) return [];
19462
19491
  const result = await client.query<{ event_id: string }>(
19463
19492
  `
19464
- WITH released AS (
19465
- UPDATE ${tableName('outbox', options)}
19493
+ WITH ${outboxByEventIdSql('target', '$1', options)},
19494
+ released AS (
19495
+ UPDATE ${tableName('outbox', options)} AS outbox
19466
19496
  SET lease_owner = NULL,
19467
19497
  lease_expires_at = NULL
19468
- WHERE event_id = ANY($1::text[])
19469
- AND delivered_at IS NULL
19470
- AND lease_owner = $2
19471
- RETURNING event_id
19498
+ FROM target
19499
+ WHERE outbox.event_id = target.event_id
19500
+ AND target.delivered_at IS NULL
19501
+ AND outbox.lease_owner = $2
19502
+ RETURNING outbox.event_id, outbox.delivered_at IS NULL AS pending
19472
19503
  )
19473
19504
  SELECT event_id,
19474
19505
  pg_notify(
@@ -19476,6 +19507,7 @@ export async function releasePostgresSchedulerOutboxLease(
19476
19507
  json_build_object('eventId', event_id, 'kind', 'outbox_released')::text
19477
19508
  )
19478
19509
  FROM released
19510
+ WHERE pending
19479
19511
  `,
19480
19512
  [eventIds, leaseOwner, postgresSchedulerNotifyChannel('outbox', options)],
19481
19513
  );
@@ -22396,14 +22428,15 @@ export async function readRunLedgerDispatchLaunchForLease(
22396
22428
  launch_json: unknown;
22397
22429
  first_claim_latency_ms: string | null;
22398
22430
  }>(
22399
- `SELECT runs.launch_json,
22431
+ `WITH ${outboxByEventIdSql('leased', 'ARRAY[$1::text]', options)}
22432
+ SELECT runs.launch_json,
22400
22433
  CASE
22401
22434
  WHEN runs.started_at IS NULL THEN NULL
22402
22435
  ELSE (
22403
22436
  extract(epoch FROM (runs.started_at - runs.created_at)) * 1000
22404
22437
  )::bigint::text
22405
22438
  END AS first_claim_latency_ms
22406
- FROM ${tableName('outbox', options)} outbox
22439
+ FROM leased outbox
22407
22440
  JOIN ${tableName('runs', options)} runs ON runs.run_id = outbox.run_id
22408
22441
  WHERE outbox.event_id = $1
22409
22442
  AND outbox.run_id = $2
@@ -81,8 +81,15 @@ export async function readRunLedgerDispatchAuthorityForLease(
81
81
  options?: Pick<PostgresSchedulerOptions, 'schema'>,
82
82
  ): Promise<RunLedgerDispatchLaunch | null> {
83
83
  const result = await client.query<{ launch_json: unknown }>(
84
- `SELECT ${runLedgerDispatchLaunchSql('runs')} AS launch_json
85
- FROM ${dispatchTableName('outbox', options)} outbox
84
+ // Read the row by primary key, then filter its lease and state. A
85
+ // pending-state predicate on the scan lets stale statistics choose a whole
86
+ // pending partial index instead.
87
+ `WITH leased AS MATERIALIZED (
88
+ SELECT event_id, run_id, lease_owner, lease_expires_at, delivered_at, failed_at
89
+ FROM ${dispatchTableName('outbox', options)} WHERE event_id = $1
90
+ )
91
+ SELECT ${runLedgerDispatchLaunchSql('runs')} AS launch_json
92
+ FROM leased outbox
86
93
  JOIN ${dispatchTableName('runs', options)} runs ON runs.run_id = outbox.run_id
87
94
  WHERE outbox.event_id = $1
88
95
  AND outbox.run_id = $2
@@ -86,7 +86,7 @@ const RUNTIME_EVENT_LOG_POSITION_EXPRESSION = `jsonb_set(
86
86
  // Migration readiness tracks this projection body independently of scheduler
87
87
  // wire and schema versions. Bump when a changed trigger must be reinstalled.
88
88
  export const RUNTIME_EVENT_SEARCH_OUTBOX_PROJECTION_MARKER =
89
- 'runtime-event-search-origin-batch-fields-once';
89
+ 'runtime-event-search-primary-key-fence-direct-independent-receipts';
90
90
  /** Trusted trigger-authored state; absent on already-projected legacy rows. */
91
91
  export const RUNTIME_EVENT_DEFERRED_PROJECTION_FIELD =
92
92
  '_runtimeSearchProjectionPending';
@@ -1905,7 +1905,7 @@ export async function attachRuntimeEventSearchTriggers(
1905
1905
  await client.query(`
1906
1906
  CREATE OR REPLACE FUNCTION ${schema}.search_receipt_event() RETURNS trigger
1907
1907
  LANGUAGE plpgsql SECURITY DEFINER SET search_path = pg_catalog AS $function$
1908
- DECLARE play text; ref uuid; canonical_events boolean;
1908
+ DECLARE play text; ref uuid; canonical_events boolean; deferred_run boolean;
1909
1909
  BEGIN
1910
1910
  IF NEW.status NOT IN ('completed', 'failed', 'skipped') THEN RETURN NEW; END IF;
1911
1911
  IF TG_OP = 'UPDATE' THEN
@@ -1918,7 +1918,10 @@ export async function attachRuntimeEventSearchTriggers(
1918
1918
  IF NEW.run_id IS NULL OR NEW.org_id IS NULL THEN
1919
1919
  RAISE EXCEPTION 'Terminal receipt search projection requires run and organization';
1920
1920
  END IF;
1921
- IF ${schema}.defer_runtime_event_projection_for_run(NEW.run_id) THEN
1921
+ deferred_run := ${schema}.defer_runtime_event_projection_for_run(NEW.run_id);
1922
+ -- Independent projection indexes the receipt row directly below. Only
1923
+ -- the deferred compatibility stage still queues a receipt carrier.
1924
+ IF deferred_run AND NOT ${schema}.independent_runtime_event_projection() THEN
1922
1925
  IF NOT EXISTS (SELECT 1 FROM ${schema}.runs
1923
1926
  WHERE run_id = NEW.run_id AND org_id = NEW.org_id) THEN
1924
1927
  RAISE EXCEPTION 'Terminal receipt search projection has no matching run';
@@ -1935,8 +1938,12 @@ export async function attachRuntimeEventSearchTriggers(
1935
1938
  FROM ${schema}.outbox WHERE run_id = NEW.run_id), clock_timestamp())));
1936
1939
  RETURN NEW;
1937
1940
  END IF;
1938
- -- Serialize event ordering with the same per-run lock as outbox facts.
1939
- PERFORM pg_advisory_xact_lock(hashtext(${quoteLiteral(scope)}), hashtext(NEW.run_id));
1941
+ -- Inline runs serialize event ordering with the same per-run lock as
1942
+ -- outbox facts. Independent runs index logs from a background prefix,
1943
+ -- so this one-row receipt insert takes no log mutex and need not wait.
1944
+ IF NOT deferred_run THEN
1945
+ PERFORM pg_advisory_xact_lock(hashtext(${quoteLiteral(scope)}), hashtext(NEW.run_id));
1946
+ END IF;
1940
1947
  SELECT play_name, run_event_store = 'planet_scale'
1941
1948
  INTO play, canonical_events FROM ${schema}.runs
1942
1949
  WHERE run_id = NEW.run_id AND org_id = NEW.org_id;
@@ -1961,7 +1968,8 @@ export async function attachRuntimeEventSearchTriggers(
1961
1968
  'source', 'receipt', 'producerAttempt', NEW.lease_owner_attempt,
1962
1969
  -- Only the reserved runtime diagnostic record supplies this flat
1963
1970
  -- context. Ordinary authored receipt outputs are never indexed.
1964
- 'context', CASE WHEN NEW.k LIKE 'tool-evidence:%'
1971
+ -- The reserved key is buildDurableToolReceiptPrefix(call-evidence).
1972
+ 'context', CASE WHEN NEW.k LIKE 'ctx:%:call:tool:call\\_evidence:%'
1965
1973
  AND NEW.output_json->>'kind' = 'tool_call_evidence' THEN jsonb_strip_nulls(jsonb_build_object(
1966
1974
  'callKey', NEW.output_json#>'{context,callKey}',
1967
1975
  'toolId', NEW.output_json#>'{context,toolId}',
@@ -3033,12 +3041,15 @@ export async function attachRuntimeEventSearchTriggers(
3033
3041
  await client.query(`
3034
3042
  CREATE OR REPLACE FUNCTION ${schema}.materialize_runtime_outbox_event(eid text, owner text) RETURNS jsonb
3035
3043
  LANGUAGE plpgsql SECURITY DEFINER SET search_path = pg_catalog AS $function$
3036
- DECLARE observation ${schema}.outbox; projection_observation ${schema}.outbox;
3044
+ DECLARE observation ${schema}.outbox; projection_observation ${schema}.outbox; still_pending boolean;
3037
3045
  BEGIN
3038
- SELECT * INTO observation FROM ${schema}.outbox
3039
- WHERE event_id = eid AND lease_owner = owner AND lease_expires_at > clock_timestamp()
3040
- AND delivered_at IS NULL AND failed_at IS NULL;
3041
- IF NOT FOUND THEN
3046
+ -- Both fences probe only the primary key, then check lease and state on
3047
+ -- that row. A pending-state predicate lets a plan built from statistics
3048
+ -- of a drained outbox scan a whole pending partial index for every fact.
3049
+ SELECT * INTO observation FROM ${schema}.outbox WHERE event_id = eid;
3050
+ IF NOT FOUND OR NOT coalesce(observation.lease_owner = owner, false)
3051
+ OR NOT coalesce(observation.lease_expires_at > clock_timestamp(), false)
3052
+ OR observation.delivered_at IS NOT NULL OR observation.failed_at IS NOT NULL THEN
3042
3053
  RAISE EXCEPTION 'Deferred event projection lease is not current' USING ERRCODE = '40001';
3043
3054
  END IF;
3044
3055
  IF observation.payload_json->'${RUNTIME_EVENT_DEFERRED_PROJECTION_FIELD}' = 'true'::jsonb THEN
@@ -3049,10 +3060,11 @@ export async function attachRuntimeEventSearchTriggers(
3049
3060
  -- Project against immutable input without locking the outbox row.
3050
3061
  -- Cancellation may transfer its lease while indexing is in flight.
3051
3062
  -- This final fence rolls the journal back if ownership changed.
3063
+ -- A settled row that still names this owner rolls back as well.
3052
3064
  UPDATE ${schema}.outbox SET payload_json = observation.payload_json
3053
3065
  WHERE event_id = eid AND lease_owner = owner AND lease_expires_at > clock_timestamp()
3054
- AND delivered_at IS NULL AND failed_at IS NULL;
3055
- IF NOT FOUND THEN
3066
+ RETURNING delivered_at IS NULL AND failed_at IS NULL INTO still_pending;
3067
+ IF NOT coalesce(still_pending, false) THEN
3056
3068
  RAISE EXCEPTION 'Deferred event projection lease is not current' USING ERRCODE = '40001';
3057
3069
  END IF;
3058
3070
  END IF;
@@ -75,6 +75,75 @@ async function mint(claims: Record<string, unknown>): Promise<string> {
75
75
  return `${signingInput}.${await sign(signingInput)}`;
76
76
  }
77
77
 
78
+ /** One read-only billing observer page; cannot authorize a payment or grant. */
79
+ export async function mintCreditReadinessToken(input: {
80
+ scanId: string;
81
+ cursor: string | null;
82
+ nowMs?: number;
83
+ }): Promise<string> {
84
+ const issuedAt = Math.floor((input.nowMs ?? Date.now()) / 1000);
85
+ return mint({
86
+ v: TOKEN_VERSION,
87
+ iss: TOKEN_ISSUER,
88
+ aud: 'deepline.credit_readiness',
89
+ scanId: input.scanId,
90
+ cursor: input.cursor,
91
+ iat: issuedAt,
92
+ exp: issuedAt + TOKEN_TTL_SECONDS,
93
+ });
94
+ }
95
+
96
+ export async function verifyCreditReadinessToken(
97
+ token: string,
98
+ nowMs = Date.now(),
99
+ ): Promise<{
100
+ scanId: string;
101
+ cursor: string | null;
102
+ }> {
103
+ const parts = token.split('.');
104
+ if (parts.length !== 3) throw new Error('Invalid credit readiness token.');
105
+ const decode = (value: string) =>
106
+ JSON.parse(Buffer.from(value, 'base64url').toString('utf8'));
107
+ const key = await crypto.subtle.importKey(
108
+ 'raw',
109
+ new TextEncoder().encode(rootSecret()),
110
+ { name: 'HMAC', hash: 'SHA-256' },
111
+ false,
112
+ ['verify'],
113
+ );
114
+ const header = decode(parts[0]);
115
+ const claims = decode(parts[1]);
116
+ const valid = await crypto.subtle.verify(
117
+ 'HMAC',
118
+ key,
119
+ Buffer.from(parts[2], 'base64url'),
120
+ new TextEncoder().encode(`${parts[0]}.${parts[1]}`),
121
+ );
122
+ const now = Math.floor(nowMs / 1000);
123
+ if (
124
+ !valid ||
125
+ header.alg !== 'HS256' ||
126
+ header.typ !== 'JWT' ||
127
+ claims.v !== TOKEN_VERSION ||
128
+ claims.iss !== TOKEN_ISSUER ||
129
+ claims.aud !== 'deepline.credit_readiness' ||
130
+ typeof claims.scanId !== 'string' ||
131
+ !claims.scanId ||
132
+ (claims.cursor !== null &&
133
+ (typeof claims.cursor !== 'string' || !claims.cursor)) ||
134
+ !Number.isSafeInteger(claims.iat) ||
135
+ !Number.isSafeInteger(claims.exp) ||
136
+ claims.iat > now + 30 ||
137
+ claims.exp <= now ||
138
+ claims.exp - claims.iat !== TOKEN_TTL_SECONDS
139
+ )
140
+ throw new Error('Invalid credit readiness token.');
141
+ return {
142
+ scanId: claims.scanId,
143
+ cursor: claims.cursor,
144
+ };
145
+ }
146
+
78
147
  /**
79
148
  * Bind a large request body to its capability without putting up to 1,024
80
149
  * receipt IDs in an HTTP header. JSON preserves order and length, so this is
@@ -10,12 +10,12 @@ import {
10
10
  registerAuthCommands,
11
11
  reportSetupCompleted,
12
12
  resolvePendingAuthClaimForResume
13
- } from "./chunk-YME4BTRK.mjs";
14
- import "./chunk-NDQBGF6U.mjs";
15
- import "./chunk-5OMKEQGL.mjs";
13
+ } from "./chunk-3ZO2O2LE.mjs";
14
+ import "./chunk-K4V4FX76.mjs";
15
+ import "./chunk-OCX5D2WI.mjs";
16
16
  import "./chunk-DLIRSHV6.mjs";
17
17
  import "./chunk-NXGCO4JB.mjs";
18
- import "./chunk-YKRINWA2.mjs";
18
+ import "./chunk-GD2VVYGB.mjs";
19
19
  import "./chunk-WZYKRD7T.mjs";
20
20
  import "./chunk-KRD6H754.mjs";
21
21
  export {
@@ -0,0 +1,33 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+ var _chunkXPIZKTYZjs = require('./chunk-XPIZKTYZ.js');
14
+ require('./chunk-D7SELX66.js');
15
+ require('./chunk-R2HHZLOP.js');
16
+ require('./chunk-XNYIZMNQ.js');
17
+ require('./chunk-7P5AA3SN.js');
18
+ require('./chunk-B7JTGFDS.js');
19
+ require('./chunk-OG64RBAC.js');
20
+ require('./chunk-COGTRVWW.js');
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+ exports.EXIT_WORKSPACE_MISMATCH = _chunkXPIZKTYZjs.EXIT_WORKSPACE_MISMATCH; exports.authStatusLine = _chunkXPIZKTYZjs.authStatusLine; exports.clearPendingClaim = _chunkXPIZKTYZjs.clearPendingClaim; exports.clearSatisfiedPendingClaim = _chunkXPIZKTYZjs.clearSatisfiedPendingClaim; exports.handleRegister = _chunkXPIZKTYZjs.handleRegister; exports.handleStatus = _chunkXPIZKTYZjs.handleStatus; exports.handleWait = _chunkXPIZKTYZjs.handleWait; exports.readPendingAuthClaim = _chunkXPIZKTYZjs.readPendingAuthClaim; exports.registerAuthCommands = _chunkXPIZKTYZjs.registerAuthCommands; exports.reportSetupCompleted = _chunkXPIZKTYZjs.reportSetupCompleted; exports.resolvePendingAuthClaimForResume = _chunkXPIZKTYZjs.resolvePendingAuthClaimForResume;
@@ -2,7 +2,7 @@ import {
2
2
  DeeplineClient,
3
3
  iterateCursorPages,
4
4
  requireBillingIdempotencyKey
5
- } from "./chunk-6RX64O34.mjs";
5
+ } from "./chunk-JNQECSK5.mjs";
6
6
  import "./chunk-6IKARAYD.mjs";
7
7
  import "./chunk-CBVIJSHF.mjs";
8
8
  import {
@@ -11,16 +11,16 @@ import {
11
11
  openInBrowser,
12
12
  printCommandEnvelope,
13
13
  shouldEmitJson
14
- } from "./chunk-NDQBGF6U.mjs";
14
+ } from "./chunk-K4V4FX76.mjs";
15
15
  import {
16
16
  AuthError,
17
17
  ConfigError
18
- } from "./chunk-5OMKEQGL.mjs";
18
+ } from "./chunk-OCX5D2WI.mjs";
19
19
  import "./chunk-DLIRSHV6.mjs";
20
20
  import {
21
21
  DeeplineError
22
22
  } from "./chunk-NXGCO4JB.mjs";
23
- import "./chunk-YKRINWA2.mjs";
23
+ import "./chunk-GD2VVYGB.mjs";
24
24
  import "./chunk-WZYKRD7T.mjs";
25
25
  import "./chunk-KRD6H754.mjs";
26
26