deepline 0.3.409 → 0.3.410

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 (213) hide show
  1. package/dist/bundling-sources/sdk/src/agent-runtime.ts +18 -1
  2. package/dist/bundling-sources/sdk/src/client.ts +206 -333
  3. package/dist/bundling-sources/sdk/src/compat.ts +45 -2
  4. package/dist/bundling-sources/sdk/src/db/query.ts +9 -10
  5. package/dist/bundling-sources/sdk/src/http.ts +38 -20
  6. package/dist/bundling-sources/sdk/src/index.ts +4 -5
  7. package/dist/bundling-sources/sdk/src/play.ts +10 -10
  8. package/dist/bundling-sources/sdk/src/plays/bundle-play-file.ts +6 -4
  9. package/dist/bundling-sources/sdk/src/plays/check-local-play-graph.ts +114 -0
  10. package/dist/bundling-sources/sdk/src/plays/client.ts +431 -0
  11. package/dist/bundling-sources/sdk/src/plays/local-play-execution.ts +2 -2
  12. package/dist/bundling-sources/sdk/src/plays/run-request.ts +106 -16
  13. package/dist/bundling-sources/sdk/src/release.ts +13 -7
  14. package/dist/bundling-sources/sdk/src/runs/read-errors.ts +8 -0
  15. package/dist/bundling-sources/sdk/src/runs/watch-polling.ts +124 -13
  16. package/dist/bundling-sources/sdk/src/tool-calls.ts +108 -19
  17. package/dist/bundling-sources/sdk/src/types.ts +18 -0
  18. package/dist/bundling-sources/shared_libs/integration-contracts/execute-retry-contract.ts +37 -0
  19. package/dist/bundling-sources/shared_libs/integration-contracts/package.json +3 -0
  20. package/dist/bundling-sources/shared_libs/play-data-plane/r2.ts +3 -1
  21. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +277 -76
  22. package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +6 -0
  23. package/dist/bundling-sources/shared_libs/play-runtime/dataset-row-input.ts +124 -0
  24. package/dist/bundling-sources/shared_libs/play-runtime/durable-call-cache.ts +6 -62
  25. package/dist/bundling-sources/shared_libs/play-runtime/durable-receipt-execution.ts +1 -0
  26. package/dist/bundling-sources/shared_libs/play-runtime/durable-tool-identity.ts +67 -0
  27. package/dist/bundling-sources/shared_libs/play-runtime/execute-server-timing.ts +64 -6
  28. package/dist/bundling-sources/shared_libs/play-runtime/freshness-receipt-execution.ts +1 -5
  29. package/dist/bundling-sources/shared_libs/play-runtime/ledger-safe-payload.ts +1 -1
  30. package/dist/bundling-sources/shared_libs/play-runtime/play-artifact-runtime-contract.ts +3 -0
  31. package/dist/bundling-sources/shared_libs/play-runtime/play-http-request.ts +10 -0
  32. package/dist/bundling-sources/shared_libs/play-runtime/query-result-dataset.ts +3 -0
  33. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/egress-probe.ts +3 -0
  34. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +26 -16
  35. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-heartbeat.ts +36 -11
  36. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-terminal-push.ts +35 -0
  37. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-launch-transport.ts +21 -0
  38. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +21 -6
  39. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-session-execution.ts +11 -2
  40. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +85 -20
  41. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +71 -13
  42. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-execution-queries.ts +100 -0
  43. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-pool.ts +9 -5
  44. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +337 -146
  45. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-shared.ts +26 -12
  46. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-state.ts +3 -1
  47. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +425 -102
  48. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-dispatch-authority.ts +4 -0
  49. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-outbox-recovery.ts +4 -0
  50. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-event-search.ts +9 -0
  51. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-sandbox-capacity.ts +181 -124
  52. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-worker-wake.ts +152 -0
  53. package/dist/bundling-sources/shared_libs/play-runtime/test-runtime-seams.ts +5 -3
  54. package/dist/bundling-sources/shared_libs/play-runtime/tool-cache-policy.ts +31 -0
  55. package/dist/bundling-sources/shared_libs/play-runtime/transient-service-error.ts +10 -0
  56. package/dist/bundling-sources/shared_libs/plays/ambiguous-write-error.ts +25 -0
  57. package/dist/bundling-sources/shared_libs/plays/artifact-types.ts +4 -0
  58. package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +63 -68
  59. package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +129 -63
  60. package/dist/bundling-sources/shared_libs/plays/contracts.ts +14 -0
  61. package/dist/bundling-sources/shared_libs/plays/enrich-play-compiler.ts +58 -16
  62. package/dist/bundling-sources/shared_libs/plays/fetch-http-error.ts +50 -0
  63. package/dist/bundling-sources/shared_libs/plays/source-authoring-edition.ts +351 -0
  64. package/dist/bundling-sources/shared_libs/plays/tool-list-getter-mode.ts +9 -0
  65. package/dist/bundling-sources/shared_libs/plays/tool-result-types.ts +1 -1
  66. package/dist/cli/{auth-5HIIACF7.mjs → auth-LLXLIILR.mjs} +6 -6
  67. package/dist/cli/auth-UG4WOYGL.js +40 -0
  68. package/dist/cli/{billing-EVZ3ZWKC.mjs → billing-5FVXZYAC.mjs} +8 -8
  69. package/dist/cli/{billing-4275N7FL.js → billing-NFPK7GYP.js} +84 -84
  70. package/dist/cli/{chunk-ZAAKSJAS.js → chunk-27GJKC5Q.js} +17 -10
  71. package/dist/cli/{chunk-UHNDVD3P.js → chunk-2H4EY7KH.js} +100 -41
  72. package/dist/cli/{chunk-TQNHMWUR.mjs → chunk-2YR7BIIC.mjs} +5 -2
  73. package/dist/cli/{chunk-AU4OFDFX.js → chunk-36AHOWVS.js} +26 -26
  74. package/dist/cli/{chunk-OSDXE27V.mjs → chunk-4PG4ARL2.mjs} +17 -10
  75. package/dist/cli/{chunk-PBZYY7VZ.mjs → chunk-4XQLVUC7.mjs} +734 -511
  76. package/dist/cli/chunk-5TUGWDEB.js +313 -0
  77. package/dist/cli/{chunk-DEC3JECC.js → chunk-5UUQAGJS.js} +5 -2
  78. package/dist/cli/{chunk-JRAVVJ3T.mjs → chunk-5V5Y37BC.mjs} +2 -2
  79. package/dist/cli/{chunk-PMVFAYL7.mjs → chunk-63ILLN5H.mjs} +2 -2
  80. package/dist/cli/{chunk-V76R6HMV.js → chunk-63S7X326.js} +1029 -806
  81. package/dist/cli/{chunk-VWBTTJYQ.mjs → chunk-6PGATQ6L.mjs} +6 -3
  82. package/dist/cli/{chunk-6BJK4EJ3.mjs → chunk-AARWVK4C.mjs} +2132 -65
  83. package/dist/cli/{chunk-FERRDNMO.js → chunk-AD47BXN2.js} +5 -5
  84. package/dist/cli/{chunk-GAICVLKL.js → chunk-D37FEBES.js} +2 -2
  85. package/dist/cli/{chunk-S6R3OX7B.js → chunk-E4CABWXL.js} +25 -2
  86. package/dist/cli/{chunk-NXGCO4JB.mjs → chunk-E4IXDAZI.mjs} +5 -0
  87. package/dist/cli/chunk-EQ4Z7RJW.mjs +35 -0
  88. package/dist/cli/chunk-FR2ILQ4G.mjs +8 -0
  89. package/dist/cli/{chunk-FJCIUF34.js → chunk-FVLNYDI4.js} +128 -376
  90. package/dist/cli/{chunk-O3VSXP2F.js → chunk-FZJAM576.js} +50 -50
  91. package/dist/cli/{chunk-BZAMOCPA.mjs → chunk-IMZ56EOH.mjs} +3 -3
  92. package/dist/cli/{chunk-N44JHMKY.mjs → chunk-IPG42SD2.mjs} +452 -138
  93. package/dist/cli/chunk-J33TSWO5.js +8 -0
  94. package/dist/cli/{chunk-BEVLCKXG.mjs → chunk-JNXJZ577.mjs} +1 -1
  95. package/dist/cli/{chunk-JI2B4RZP.mjs → chunk-KCC3FKIW.mjs} +71 -12
  96. package/dist/cli/{chunk-6IKARAYD.mjs → chunk-L2VKAASU.mjs} +23 -0
  97. package/dist/cli/{chunk-WLOCMJWV.js → chunk-LTFL4JRN.js} +426 -721
  98. package/dist/cli/{chunk-YHPYUZZX.mjs → chunk-MAMVGOSC.mjs} +1 -1
  99. package/dist/cli/{chunk-NZSF57UO.mjs → chunk-MN2OWLFA.mjs} +2 -2
  100. package/dist/cli/{chunk-YD7B53RW.js → chunk-NF4V5DTY.js} +12 -12
  101. package/dist/cli/{chunk-Q2E4E4FJ.js → chunk-NPBJI7RE.js} +8 -8
  102. package/dist/cli/chunk-NTVLGHFM.js +501 -0
  103. package/dist/cli/{chunk-HQXL5526.mjs → chunk-NUX2QHNC.mjs} +1 -1
  104. package/dist/cli/{chunk-JGHG4NBA.js → chunk-OBB4QXHA.js} +19 -4
  105. package/dist/cli/{chunk-SEFMGO3Z.js → chunk-OQQOXSYA.js} +2184 -117
  106. package/dist/cli/chunk-OZ6E3IRZ.js +6084 -0
  107. package/dist/cli/{chunk-7P5AA3SN.js → chunk-PPDBRJRW.js} +6 -1
  108. package/dist/cli/{chunk-FHPCKULR.mjs → chunk-PZSLPBDA.mjs} +2 -2
  109. package/dist/cli/{chunk-ILHRA7AD.js → chunk-QADCFSGH.js} +7 -7
  110. package/dist/cli/{chunk-RTCDW2GU.js → chunk-QJI5FIOW.js} +170 -49
  111. package/dist/cli/{chunk-WYQO2CBS.mjs → chunk-QL22Y56M.mjs} +149 -28
  112. package/dist/cli/{chunk-2PRO2JTX.mjs → chunk-RFB25DSQ.mjs} +36 -284
  113. package/dist/cli/chunk-T44IIOT6.mjs +501 -0
  114. package/dist/cli/chunk-VK2LZ5IP.js +35 -0
  115. package/dist/cli/chunk-VKKVSFRD.js +62 -0
  116. package/dist/cli/{chunk-2GI3QD3L.js → chunk-VYKV5XHL.js} +644 -330
  117. package/dist/cli/{chunk-GSK6NHVM.js → chunk-WU4U6VSB.js} +5 -5
  118. package/dist/cli/chunk-XHLKJOTN.mjs +313 -0
  119. package/dist/cli/{chunk-IT3XBJQR.mjs → chunk-XIHWSXSV.mjs} +330 -625
  120. package/dist/cli/chunk-XNZJKRJA.mjs +6084 -0
  121. package/dist/cli/chunk-ZCALLYF6.mjs +62 -0
  122. package/dist/cli/{chunk-WHG43Z67.js → chunk-ZFZ427HN.js} +11 -8
  123. package/dist/cli/{chunk-J6DCKJNM.mjs → chunk-ZKYEW55U.mjs} +16 -1
  124. package/dist/cli/{csv-BBOTCGQY.js → csv-7U3FJYKZ.js} +14 -14
  125. package/dist/cli/{csv-A2MP6EFV.mjs → csv-ZWLDMM4A.mjs} +5 -5
  126. package/dist/cli/{db-G4CODDCA.js → db-DNCGV5PC.js} +42 -47
  127. package/dist/cli/{db-3W5DMTUX.mjs → db-IMYXVMGI.mjs} +15 -20
  128. package/dist/cli/{describe-XYJ2ZT4M.mjs → describe-Q23DWJUK.mjs} +20 -15
  129. package/dist/cli/{describe-PQTQ5B6F.js → describe-S34JUX5W.js} +27 -22
  130. package/dist/cli/describe-core.d.mts +2 -2
  131. package/dist/cli/describe-core.d.ts +2 -2
  132. package/dist/cli/describe-core.js +6 -6
  133. package/dist/cli/describe-core.mjs +5 -5
  134. package/dist/cli/{executions-UR67CA5H.mjs → executions-A73EUGSI.mjs} +8 -8
  135. package/dist/cli/{executions-TLANVR7Y.js → executions-XOMPX2YJ.js} +10 -10
  136. package/dist/cli/{feedback-WHCMGEIL.mjs → feedback-N4GJ2POI.mjs} +11 -7
  137. package/dist/cli/{feedback-6C2YTRJL.js → feedback-QTCHNJJH.js} +13 -9
  138. package/dist/cli/{git-GWLB6VSA.mjs → git-4Q3JIQYZ.mjs} +6 -6
  139. package/dist/cli/git-I6CPBEVG.js +24 -0
  140. package/dist/cli/index.js +236 -348
  141. package/dist/cli/index.mjs +162 -274
  142. package/dist/cli/{legacy-noops-KF4XIFEF.mjs → legacy-noops-3SOVGFF7.mjs} +6 -6
  143. package/dist/cli/{legacy-noops-ZUFA7RWI.js → legacy-noops-4OSHZM34.js} +9 -9
  144. package/dist/cli/{monitors-TB3K2IVE.js → monitors-LVE2KZNG.js} +94 -94
  145. package/dist/cli/{monitors-6UOIDIMB.mjs → monitors-QP2XID2B.mjs} +8 -8
  146. package/dist/cli/{org-WGHHMVDZ.mjs → org-IVRETVQA.mjs} +8 -8
  147. package/dist/cli/{org-CKWMBPIR.js → org-XFLK5IAP.js} +68 -68
  148. package/dist/cli/{play-LYPAXCLD.mjs → play-DE7VH6NU.mjs} +533 -98
  149. package/dist/cli/{play-N2B3KAVS.js → play-XXTPPP23.js} +1067 -632
  150. package/dist/cli/{providers-UR42MD6Z.js → providers-DLD3I4JF.js} +11 -11
  151. package/dist/cli/{providers-6DI5BVZJ.mjs → providers-GX2NIZQQ.mjs} +8 -8
  152. package/dist/cli/{quickstart-MNA2S6PO.mjs → quickstart-72B4L4PK.mjs} +5 -5
  153. package/dist/cli/{quickstart-FCJG4XGB.js → quickstart-XNS3HSWC.js} +7 -7
  154. package/dist/cli/search-6NCH4HB5.js +30 -0
  155. package/dist/cli/{search-VPPYUJ2M.mjs → search-SUTRANJI.mjs} +9 -9
  156. package/dist/cli/{secrets-QOSMVO36.js → secrets-6OBLOVEV.js} +17 -17
  157. package/dist/cli/{secrets-DRI4W7Z5.mjs → secrets-FPFXXNUH.mjs} +8 -8
  158. package/dist/cli/sessions-235532DR.js +16 -0
  159. package/dist/cli/{sessions-DQNUKP43.mjs → sessions-R25I4QKN.mjs} +6 -6
  160. package/dist/cli/{settings-DWDK3Y2R.mjs → settings-AOP6F5PZ.mjs} +12 -12
  161. package/dist/cli/{settings-JUGGCOPS.js → settings-VL5FOFPN.js} +79 -79
  162. package/dist/cli/{setup-AWBHNYXK.js → setup-BSC67YT3.js} +45 -44
  163. package/dist/cli/{setup-D5DWVNY4.mjs → setup-ZMPHBHVJ.mjs} +11 -10
  164. package/dist/cli/skills-3IWLYYRL.js +29 -0
  165. package/dist/cli/{skills-YV2XR35J.mjs → skills-KF65TVFO.mjs} +7 -7
  166. package/dist/cli/tools-LR66SPYU.js +44 -0
  167. package/dist/cli/{tools-VFJYHNV2.mjs → tools-SX4XIPM7.mjs} +19 -14
  168. package/dist/cli/{update-IM6VOTGL.mjs → update-QRMFRAQQ.mjs} +11 -9
  169. package/dist/cli/update-SP7UEO53.js +31 -0
  170. package/dist/cli/{workflow-26URBWPV.mjs → workflow-FOEEEB7G.mjs} +44 -20
  171. package/dist/cli/{workflow-3BS6U6SL.js → workflow-KOCK2FLW.js} +54 -30
  172. package/dist/{client-BfInrZPB.d.mts → client-74k4IPh6.d.mts} +135 -54
  173. package/dist/{client-BMjJFpan.d.ts → client-C1IK1f8c.d.ts} +135 -54
  174. package/dist/{compiler-manifest-Ycx-hoAr.d.mts → compiler-manifest-lzWpbV3w.d.mts} +83 -66
  175. package/dist/{compiler-manifest-Ycx-hoAr.d.ts → compiler-manifest-lzWpbV3w.d.ts} +83 -66
  176. package/dist/{index-CZXgiN3E.d.mts → index-C8Cy-YTm.d.ts} +5 -1
  177. package/dist/{index-NPG5pqxQ.d.ts → index-CtNK-0nI.d.mts} +5 -1
  178. package/dist/index.d.mts +47 -9
  179. package/dist/index.d.ts +47 -9
  180. package/dist/index.js +17 -14
  181. package/dist/index.mjs +13 -10
  182. package/dist/install-integrity.json +118 -102
  183. package/dist/plays/bundle-play-file.d.mts +3 -3
  184. package/dist/plays/bundle-play-file.d.ts +3 -3
  185. package/dist/plays/bundle-play-file.mjs +5 -4
  186. package/dist/plays/csv-data-input.js +3 -3
  187. package/dist/plays/csv-data-input.mjs +2 -2
  188. package/dist/plays/play-description.d.mts +2 -2
  189. package/dist/plays/play-description.d.ts +2 -2
  190. package/dist/plays/run-request.d.mts +2 -2
  191. package/dist/plays/run-request.d.ts +2 -2
  192. package/dist/plays/run-request.js +11 -3
  193. package/dist/plays/run-request.mjs +10 -2
  194. package/dist/release.d.mts +15 -6
  195. package/dist/release.d.ts +15 -6
  196. package/dist/release.js +2 -2
  197. package/dist/release.mjs +1 -1
  198. package/package.json +2 -2
  199. package/dist/cli/auth-F6BX32XZ.js +0 -40
  200. package/dist/cli/chunk-264PC2RN.mjs +0 -83
  201. package/dist/cli/chunk-44ZTYHMQ.mjs +0 -155
  202. package/dist/cli/chunk-53HN7WZN.js +0 -392
  203. package/dist/cli/chunk-7AALNGTA.mjs +0 -392
  204. package/dist/cli/chunk-ALD4COBH.js +0 -155
  205. package/dist/cli/chunk-CLJGSOVM.mjs +0 -1716
  206. package/dist/cli/chunk-WNUR6VP5.js +0 -83
  207. package/dist/cli/chunk-Z4VNJOF7.js +0 -1716
  208. package/dist/cli/git-VQFF6D7C.js +0 -24
  209. package/dist/cli/search-NCEEONMQ.js +0 -30
  210. package/dist/cli/sessions-3WYKQABW.js +0 -16
  211. package/dist/cli/skills-AKPAJVSO.js +0 -29
  212. package/dist/cli/tools-RBKEK6PM.js +0 -39
  213. package/dist/cli/update-VFNUBLDA.js +0 -29
@@ -289,6 +289,7 @@ const POSTGRES_SCHEDULER_SCHEMA_INIT_ADVISORY_LOCK_NAMESPACE = 0x44505349;
289
289
  const POSTGRES_SCHEDULER_SCHEMA_INIT_ADVISORY_LOCK_SEED =
290
290
  'deepline:runtime-scheduler:schema-init:v1';
291
291
  const POSTGRES_SCHEDULER_RELEASE_ADMISSION_LOCK_NAMESPACE = 0x44505241;
292
+ const POSTGRES_SCHEDULER_ADMISSION_LOCK_TIMEOUT_MS = 5_000;
292
293
  const POSTGRES_SCHEDULER_SCHEMA_INIT_STATEMENT_TIMEOUT_MS = 60_000;
293
294
  /**
294
295
  * Every new scheduler DDL migration must bump this version. Runtime callers
@@ -4707,6 +4708,7 @@ function absurdSchemaInitAdvisoryLockKey(): readonly [number, number] {
4707
4708
  */
4708
4709
  export async function ensureAbsurdSchema(
4709
4710
  client: PostgresSchedulerQueryClient,
4711
+ options: { refreshClaimBody?: boolean } = {},
4710
4712
  ): Promise<void> {
4711
4713
  const [lockClassId, lockObjectId] = absurdSchemaInitAdvisoryLockKey();
4712
4714
  let stage = 'begin transaction';
@@ -4760,9 +4762,28 @@ export async function ensureAbsurdSchema(
4760
4762
  `SELECT absurd.deepline_no_scheduler_org_cap_compatibility_version() AS v`,
4761
4763
  )
4762
4764
  : null;
4765
+ // Only the explicit deployment migration reconciles equivalent bodies.
4766
+ // Runtime readiness keeps the same ABI marker and remains read-only.
4767
+ let claimBodyCurrent = true;
4768
+ if (options.refreshClaimBody) {
4769
+ stage = 'compare Absurd claim function body';
4770
+ const sql = readAbsurdSchemaNoSchedulerOrgCapCompatibilitySql();
4771
+ const start = sql.indexOf('as $$');
4772
+ const end = sql.lastIndexOf('$$');
4773
+ if (start < 0 || end <= start) {
4774
+ throw new Error('Absurd claim body delimiters are malformed.');
4775
+ }
4776
+ const body = await client.query<{ current: boolean }>(
4777
+ `SELECT prosrc = $1::text AS current FROM pg_proc
4778
+ WHERE oid = to_regprocedure('absurd.claim_task(text,text,integer,integer)')`,
4779
+ [sql.slice(start + 'as $$'.length, end)],
4780
+ );
4781
+ claimBodyCurrent = body.rows[0]?.current === true;
4782
+ }
4763
4783
  if (
4784
+ !claimBodyCurrent ||
4764
4785
  markerVersion?.rows[0]?.v !==
4765
- ABSURD_NO_SCHEDULER_ORG_CAP_COMPATIBILITY_VERSION
4786
+ ABSURD_NO_SCHEDULER_ORG_CAP_COMPATIBILITY_VERSION
4766
4787
  ) {
4767
4788
  stage = 'apply Absurd no-org-cap compatibility function patch';
4768
4789
  await client.query(
@@ -5147,7 +5168,17 @@ async function submitPostgresSchedulerRunTransaction(
5147
5168
  existing_launch_json: unknown | null;
5148
5169
  }>(
5149
5170
  `
5150
- WITH run_lock AS MATERIALIZED (
5171
+ WITH admission_lock_timeout AS MATERIALIZED (
5172
+ -- This single-statement admission can wait on run, release, or row
5173
+ -- locks. Bound that wait inside the same implicit transaction; the
5174
+ -- dependency below applies it before acquiring the first lock, and
5175
+ -- transaction-local scope cannot leak through PgBouncer.
5176
+ SELECT set_config(
5177
+ 'lock_timeout',
5178
+ '${POSTGRES_SCHEDULER_ADMISSION_LOCK_TIMEOUT_MS}ms',
5179
+ true
5180
+ )
5181
+ ), run_lock AS MATERIALIZED (
5151
5182
  -- A run id is a global job key, not a release-local key. Serialize it
5152
5183
  -- before inspecting the runs table so two app releases cannot each
5153
5184
  -- spawn the
@@ -5158,6 +5189,7 @@ async function submitPostgresSchedulerRunTransaction(
5158
5189
  'run:' || $1::text
5159
5190
  )
5160
5191
  )
5192
+ FROM admission_lock_timeout
5161
5193
  ), admission_lock AS (
5162
5194
  SELECT pg_advisory_xact_lock_shared(
5163
5195
  ${POSTGRES_SCHEDULER_RELEASE_ADMISSION_LOCK_NAMESPACE},
@@ -7082,33 +7114,23 @@ export type PostgresSchedulerRunLaneState = {
7082
7114
  laneClaiming: boolean;
7083
7115
  };
7084
7116
 
7085
- /**
7086
- * One transactional read of everything the lane guard needs about a claimed
7087
- * run: which lane owns it, whether that lane still claims, and whether the run
7088
- * is parked in a wait a newer lane may adopt.
7089
- *
7090
- * The scheduler run row — not the engine task row — is the authority on "parked
7091
- * in a durable wait". The engine cannot tell a durable suspension from a retry
7092
- * backoff or a capacity `DeferTask`, all of which sit in `sleeping`.
7093
- */
7094
- export async function readPostgresSchedulerRunLaneState(
7095
- client: PostgresSchedulerQueryClient,
7096
- input: { runId: string },
7117
+ type RunLaneStateRow = {
7118
+ release_id: string | null;
7119
+ environment: string;
7120
+ status: string;
7121
+ wait_kind: string | null;
7122
+ parked: boolean;
7123
+ lane_claiming: boolean | null;
7124
+ };
7125
+
7126
+ /** The lane prefix is bound as $2 by both readers of this projection. */
7127
+ function runLaneStateSelectionSql(
7097
7128
  options?: Pick<PostgresSchedulerOptions, 'schema'>,
7098
- ): Promise<PostgresSchedulerRunLaneState | null> {
7129
+ ): string {
7099
7130
  const adoptableKinds = POSTGRES_SCHEDULER_ADOPTABLE_WAIT_KINDS.map(
7100
7131
  (kind) => `'${kind}'`,
7101
7132
  ).join(', ');
7102
- const result = await client.query<{
7103
- release_id: string | null;
7104
- environment: string;
7105
- status: string;
7106
- wait_kind: string | null;
7107
- parked: boolean;
7108
- lane_claiming: boolean | null;
7109
- }>(
7110
- `
7111
- SELECT
7133
+ return `
7112
7134
  nullif(btrim(runs.launch_json ->> 'absurdReleaseId'), '') AS release_id,
7113
7135
  coalesce(
7114
7136
  nullif(btrim(runs.launch_json ->> 'absurdReleaseEnvironment'), ''),
@@ -7141,14 +7163,12 @@ export async function readPostgresSchedulerRunLaneState(
7141
7163
  )
7142
7164
  || ':'
7143
7165
  || nullif(btrim(runs.launch_json ->> 'absurdReleaseId'), '')
7144
- ) AS lane_claiming
7145
- FROM ${tableName('runs', options)} AS runs
7146
- WHERE runs.run_id = $1
7147
- `,
7148
- [input.runId, POSTGRES_SCHEDULER_RELEASE_CLAIMING_CONTROL_PREFIX],
7149
- );
7150
- const row = result.rows[0];
7151
- if (!row) return null;
7166
+ ) AS lane_claiming`;
7167
+ }
7168
+
7169
+ function runLaneStateFromRow(
7170
+ row: RunLaneStateRow,
7171
+ ): PostgresSchedulerRunLaneState {
7152
7172
  return {
7153
7173
  releaseId: row.release_id,
7154
7174
  environment: row.environment === 'preview' ? 'preview' : 'production',
@@ -7159,6 +7179,32 @@ export async function readPostgresSchedulerRunLaneState(
7159
7179
  };
7160
7180
  }
7161
7181
 
7182
+ /**
7183
+ * One transactional read of everything the lane guard needs about a claimed
7184
+ * run: which lane owns it, whether that lane still claims, and whether the run
7185
+ * is parked in a wait a newer lane may adopt.
7186
+ *
7187
+ * The scheduler run row — not the engine task row — is the authority on "parked
7188
+ * in a durable wait". The engine cannot tell a durable suspension from a retry
7189
+ * backoff or a capacity `DeferTask`, all of which sit in `sleeping`.
7190
+ */
7191
+ export async function readPostgresSchedulerRunLaneState(
7192
+ client: PostgresSchedulerQueryClient,
7193
+ input: { runId: string },
7194
+ options?: Pick<PostgresSchedulerOptions, 'schema'>,
7195
+ ): Promise<PostgresSchedulerRunLaneState | null> {
7196
+ const result = await client.query<RunLaneStateRow>(
7197
+ `
7198
+ SELECT ${runLaneStateSelectionSql(options)}
7199
+ FROM ${tableName('runs', options)} AS runs
7200
+ WHERE runs.run_id = $1
7201
+ `,
7202
+ [input.runId, POSTGRES_SCHEDULER_RELEASE_CLAIMING_CONTROL_PREFIX],
7203
+ );
7204
+ const row = result.rows[0];
7205
+ return row ? runLaneStateFromRow(row) : null;
7206
+ }
7207
+
7162
7208
  /**
7163
7209
  * Discover the lanes whose parked durable waits the caller may adopt: lanes
7164
7210
  * that have explicitly stopped claiming AND still hold at least one adoptable
@@ -7259,21 +7305,30 @@ export async function assignPostgresSchedulerRunAttempt(
7259
7305
  runId: string;
7260
7306
  attempt: number;
7261
7307
  taskToken: string;
7308
+ /** Persist cleanup ownership in the same fenced write as a fresh claim. */
7309
+ initialRuntimeResource?: unknown;
7262
7310
  },
7263
7311
  options?: Pick<PostgresSchedulerOptions, 'schema'>,
7264
7312
  ): Promise<{
7265
7313
  checkpoint?: unknown;
7266
7314
  suspension?: unknown;
7267
7315
  firstClaimLatencyMs?: number;
7316
+ initialRuntimeResourcePersisted: boolean;
7268
7317
  } | null> {
7269
7318
  const assigned = await client.query<{
7270
7319
  checkpoint_json: unknown;
7271
7320
  suspension_json: unknown;
7272
7321
  first_claim_latency_ms: string | null;
7322
+ initial_runtime_resource_persisted: boolean;
7273
7323
  }>(
7274
7324
  `
7275
7325
  WITH locked_run AS MATERIALIZED (
7276
- SELECT runs.run_id, runs.started_at, runs.status
7326
+ SELECT
7327
+ runs.run_id,
7328
+ runs.started_at,
7329
+ runs.status,
7330
+ runs.checkpoint_json,
7331
+ runs.suspension_json
7277
7332
  FROM ${tableName('runs', options)} AS runs
7278
7333
  WHERE runs.run_id = $1
7279
7334
  AND runs.status IN ('queued', 'running', 'waiting')
@@ -7282,6 +7337,8 @@ export async function assignPostgresSchedulerRunAttempt(
7282
7337
  SELECT
7283
7338
  runs.run_id,
7284
7339
  runs.started_at,
7340
+ runs.checkpoint_json,
7341
+ runs.suspension_json,
7285
7342
  max(attempts.attempt) FILTER (
7286
7343
  WHERE attempts.status IN ('claimed', 'running', 'waiting')
7287
7344
  ) AS active_attempt
@@ -7290,7 +7347,11 @@ export async function assignPostgresSchedulerRunAttempt(
7290
7347
  ON attempts.run_id = runs.run_id
7291
7348
  WHERE runs.run_id = $1
7292
7349
  AND runs.status IN ('queued', 'running', 'waiting')
7293
- GROUP BY runs.run_id, runs.started_at
7350
+ GROUP BY
7351
+ runs.run_id,
7352
+ runs.started_at,
7353
+ runs.checkpoint_json,
7354
+ runs.suspension_json
7294
7355
  HAVING max(attempts.attempt) FILTER (
7295
7356
  WHERE attempts.status IN ('claimed', 'running', 'waiting')
7296
7357
  ) IS NULL
@@ -7306,7 +7367,8 @@ export async function assignPostgresSchedulerRunAttempt(
7306
7367
  lease_owner,
7307
7368
  lease_token,
7308
7369
  lease_expires_at,
7309
- heartbeat_at
7370
+ heartbeat_at,
7371
+ resource_json
7310
7372
  )
7311
7373
  SELECT
7312
7374
  run_id,
@@ -7315,10 +7377,17 @@ export async function assignPostgresSchedulerRunAttempt(
7315
7377
  $3,
7316
7378
  $4,
7317
7379
  NULL,
7318
- now()
7380
+ now(),
7381
+ CASE
7382
+ WHEN $5::jsonb IS NOT NULL
7383
+ AND checkpoint_json IS NULL
7384
+ AND suspension_json IS NULL
7385
+ THEN jsonb_build_array($5::jsonb)
7386
+ ELSE NULL
7387
+ END
7319
7388
  FROM target_run
7320
7389
  ON CONFLICT (run_id, attempt) DO NOTHING
7321
- RETURNING run_id
7390
+ RETURNING run_id, resource_json IS NOT NULL AS initial_runtime_resource_persisted
7322
7391
  ),
7323
7392
  retired_attempts AS (
7324
7393
  UPDATE ${tableName('attempts', options)}
@@ -7396,16 +7465,20 @@ export async function assignPostgresSchedulerRunAttempt(
7396
7465
  WHERE run_started.run_id = runs.run_id
7397
7466
  )
7398
7467
  ELSE NULL
7399
- END AS first_claim_latency_ms
7468
+ END AS first_claim_latency_ms,
7469
+ assigned.initial_runtime_resource_persisted
7400
7470
  FROM ${tableName('runs', options)}
7401
7471
  AS runs
7402
- WHERE run_id IN (SELECT run_id FROM assigned)
7472
+ JOIN assigned USING (run_id)
7403
7473
  `,
7404
7474
  [
7405
7475
  input.runId,
7406
7476
  input.attempt,
7407
7477
  POSTGRES_SCHEDULER_ATTEMPT_OWNER,
7408
7478
  input.taskToken,
7479
+ input.initialRuntimeResource === undefined
7480
+ ? null
7481
+ : stringifyPostgresJson(input.initialRuntimeResource),
7409
7482
  ],
7410
7483
  );
7411
7484
  const row = assigned.rows[0] ?? null;
@@ -7416,6 +7489,8 @@ export async function assignPostgresSchedulerRunAttempt(
7416
7489
  ...(row.first_claim_latency_ms == null
7417
7490
  ? {}
7418
7491
  : { firstClaimLatencyMs: Number(row.first_claim_latency_ms) }),
7492
+ initialRuntimeResourcePersisted:
7493
+ row.initial_runtime_resource_persisted === true,
7419
7494
  };
7420
7495
  }
7421
7496
 
@@ -9157,12 +9232,11 @@ export async function readPostgresSchedulerLaunchRouting(
9157
9232
  }
9158
9233
  }
9159
9234
 
9160
- export async function readPostgresSchedulerLaunch(
9161
- client: PostgresSchedulerQueryClient,
9162
- runId: string,
9235
+ async function observeSchedulerLaunchRead<T>(
9163
9236
  options: Pick<PostgresSchedulerOptions, 'schema'> | undefined,
9164
9237
  telemetry: { caller: SchedulerLaunchReadCaller },
9165
- ): Promise<PlaySchedulerSubmitInput | null> {
9238
+ read: () => Promise<{ launch: PlaySchedulerSubmitInput | null; value: T }>,
9239
+ ): Promise<T> {
9166
9240
  const caller = telemetry.caller;
9167
9241
  const startedAt = performance.now();
9168
9242
  try {
@@ -9176,21 +9250,7 @@ export async function readPostgresSchedulerLaunch(
9176
9250
  },
9177
9251
  },
9178
9252
  async (span) => {
9179
- const result = await client.query<{ launch_json: unknown }>(
9180
- `
9181
- -- admission_json is the first-writer trusted projection. New rows
9182
- -- retain non-secret lane/callback routing there while legacy rows
9183
- -- fall back to launch_json. Merge it last so descendants never
9184
- -- follow transport metadata changed by a later operational repair.
9185
- SELECT launch_json || coalesce(admission_json, '{}'::jsonb)
9186
- AS launch_json
9187
- FROM ${tableName('runs', options)}
9188
- WHERE run_id = $1
9189
- `,
9190
- [runId],
9191
- );
9192
- const row = result.rows[0] ?? null;
9193
- const launch = row ? parseLaunchJson(row.launch_json) : null;
9253
+ const { launch, value } = await read();
9194
9254
  const durationMs = performance.now() - startedAt;
9195
9255
  recordSchedulerLaunchRead({
9196
9256
  caller,
@@ -9204,7 +9264,7 @@ export async function readPostgresSchedulerLaunch(
9204
9264
  'runtime.scheduler.release_id':
9205
9265
  launch?.absurdReleaseId?.trim() || 'unassigned',
9206
9266
  });
9207
- return launch;
9267
+ return value;
9208
9268
  },
9209
9269
  );
9210
9270
  } catch (error) {
@@ -9218,6 +9278,108 @@ export async function readPostgresSchedulerLaunch(
9218
9278
  }
9219
9279
  }
9220
9280
 
9281
+ export async function readPostgresSchedulerLaunch(
9282
+ client: PostgresSchedulerQueryClient,
9283
+ runId: string,
9284
+ options: Pick<PostgresSchedulerOptions, 'schema'> | undefined,
9285
+ telemetry: { caller: SchedulerLaunchReadCaller },
9286
+ ): Promise<PlaySchedulerSubmitInput | null> {
9287
+ return observeSchedulerLaunchRead(options, telemetry, async () => {
9288
+ const result = await client.query<{ launch_json: unknown }>(
9289
+ `
9290
+ -- admission_json is the first-writer trusted projection. New rows
9291
+ -- retain non-secret lane/callback routing there while legacy rows
9292
+ -- fall back to launch_json. Merge it last so descendants never
9293
+ -- follow transport metadata changed by a later operational repair.
9294
+ SELECT launch_json || coalesce(admission_json, '{}'::jsonb)
9295
+ AS launch_json
9296
+ FROM ${tableName('runs', options)}
9297
+ WHERE run_id = $1
9298
+ `,
9299
+ [runId],
9300
+ );
9301
+ const row = result.rows[0];
9302
+ const launch = row ? parseLaunchJson(row.launch_json) : null;
9303
+ return { launch, value: launch };
9304
+ });
9305
+ }
9306
+
9307
+ export type PostgresSchedulerAttemptPreparation = {
9308
+ launch: PlaySchedulerSubmitInput;
9309
+ existing: PlaySchedulerResultEnvelope | null;
9310
+ laneState: PostgresSchedulerRunLaneState;
9311
+ createdProjection: PostgresSchedulerOutboxDeliveryStatus;
9312
+ /** Candidate only; the assignment fence admits at most one racing reader. */
9313
+ nextAttempt: number;
9314
+ };
9315
+
9316
+ /**
9317
+ * Read attempt ingress facts in one database snapshot. Assignment and detached
9318
+ * recovery still perform their own fenced writes; this read cannot claim work.
9319
+ */
9320
+ export async function readPostgresSchedulerAttemptPreparation(
9321
+ client: PostgresSchedulerQueryClient,
9322
+ runId: string,
9323
+ options?: Pick<PostgresSchedulerOptions, 'schema'>,
9324
+ ): Promise<PostgresSchedulerAttemptPreparation | null> {
9325
+ return observeSchedulerLaunchRead(
9326
+ options,
9327
+ { caller: 'runtime_worker_execute' },
9328
+ async () => {
9329
+ const result = await client.query<
9330
+ RunRow &
9331
+ RunLaneStateRow & {
9332
+ launch_json: unknown;
9333
+ projection_exists: boolean;
9334
+ projection_delivered: boolean;
9335
+ projection_blocked: boolean;
9336
+ next_attempt: number | string;
9337
+ }
9338
+ >(
9339
+ `
9340
+ SELECT runs.run_id, runs.terminal_result_json, runs.terminal_error_json,
9341
+ runs.runtime_timing_json, runs.created_at, runs.started_at, runs.finished_at,
9342
+ runs.launch_json || coalesce(runs.admission_json, '{}'::jsonb) AS launch_json,
9343
+ (SELECT coalesce(max(attempt), 0) + 1
9344
+ FROM ${tableName('attempts', options)}
9345
+ WHERE run_id = runs.run_id) AS next_attempt,
9346
+ ${runLaneStateSelectionSql(options)},
9347
+ created.event_id IS NOT NULL AS projection_exists,
9348
+ created.delivered_at IS NOT NULL AS projection_delivered,
9349
+ CASE WHEN created.event_id IS NULL THEN false
9350
+ ELSE ${outboxDeliveryBlockedSql('created', options)}
9351
+ END AS projection_blocked
9352
+ FROM ${tableName('runs', options)} AS runs
9353
+ LEFT JOIN ${tableName('outbox', options)} AS created
9354
+ ON created.run_id = runs.run_id
9355
+ AND created.event_id = 'run-created:' || runs.run_id
9356
+ AND (runs.launch_json || coalesce(runs.admission_json, '{}'::jsonb))
9357
+ ->> 'requiresRunCreatedProjection' = 'true'
9358
+ WHERE runs.run_id = $1
9359
+ `,
9360
+ [runId, POSTGRES_SCHEDULER_RELEASE_CLAIMING_CONTROL_PREFIX],
9361
+ );
9362
+ const row = result.rows[0];
9363
+ const launch = row ? parseLaunchJson(row.launch_json) : null;
9364
+ const value: PostgresSchedulerAttemptPreparation | null =
9365
+ row && launch
9366
+ ? {
9367
+ launch,
9368
+ existing: schedulerResultFromRow(row),
9369
+ laneState: runLaneStateFromRow(row),
9370
+ nextAttempt: Number(row.next_attempt),
9371
+ createdProjection: {
9372
+ exists: row.projection_exists === true,
9373
+ delivered: row.projection_delivered === true,
9374
+ blocked: row.projection_blocked === true,
9375
+ },
9376
+ }
9377
+ : null;
9378
+ return { launch, value };
9379
+ },
9380
+ );
9381
+ }
9382
+
9221
9383
  /**
9222
9384
  * One round-trip replay inspection for externally retried admissions. The
9223
9385
  * caller uses this only to return an already-committed Run without rebuilding
@@ -9316,7 +9478,7 @@ const POSTGRES_SCHEDULER_ATTEMPT_AUTHORITY_FIELDS = [
9316
9478
  export type PostgresSchedulerAttemptAuthority = Pick<
9317
9479
  PlaySchedulerSubmitInput,
9318
9480
  (typeof POSTGRES_SCHEDULER_ATTEMPT_AUTHORITY_FIELDS)[number]
9319
- >;
9481
+ > & { computeSessionIntentPersisted?: boolean };
9320
9482
 
9321
9483
  type PostgresSchedulerAuthorityRow = PostgresSchedulerAttemptAuthority & {
9322
9484
  launch_required_fields_valid: boolean;
@@ -9362,7 +9524,17 @@ export async function readPostgresSchedulerAttemptAuthorityForLease(
9362
9524
  ): Promise<PostgresSchedulerAttemptAuthority | null> {
9363
9525
  const result = await client.query<PostgresSchedulerAuthorityRow>(
9364
9526
  `
9365
- SELECT ${POSTGRES_SCHEDULER_AUTHORITY_SELECT}
9527
+ SELECT ${POSTGRES_SCHEDULER_AUTHORITY_SELECT},
9528
+ EXISTS (
9529
+ SELECT 1 FROM jsonb_array_elements(
9530
+ CASE
9531
+ WHEN jsonb_typeof(attempts.resource_json) = 'array' THEN attempts.resource_json
9532
+ WHEN attempts.resource_json IS NULL THEN '[]'::jsonb
9533
+ ELSE jsonb_build_array(attempts.resource_json)
9534
+ END
9535
+ ) AS resource
9536
+ WHERE resource->>'kind' = 'compute_session'
9537
+ ) AS "computeSessionIntentPersisted"
9366
9538
  FROM ${tableName('runs', options)} AS runs
9367
9539
  JOIN ${tableName('attempts', options)} AS attempts
9368
9540
  ON attempts.run_id = runs.run_id
@@ -13215,14 +13387,10 @@ export async function readPostgresSchedulerRunnerLiveness(
13215
13387
  * and no newer attempt to exist. Gateway replicas must never infer this fence
13216
13388
  * from process memory.
13217
13389
  */
13218
- export async function isPostgresSchedulerRunnerProgressAttemptCurrent(
13219
- client: PostgresSchedulerQueryClient,
13220
- input: { runId: string; attempt: number },
13390
+ function runnerProgressAttemptCurrentCondition(
13221
13391
  options?: Pick<PostgresSchedulerOptions, 'schema'>,
13222
- ): Promise<boolean> {
13223
- const result = await client.query<{ current: boolean }>(
13224
- `
13225
- SELECT EXISTS (
13392
+ ): string {
13393
+ return `EXISTS (
13226
13394
  SELECT 1
13227
13395
  FROM ${tableName('attempts', options)} AS current_attempt
13228
13396
  JOIN ${tableName('runs', options)} AS run
@@ -13237,13 +13405,50 @@ export async function isPostgresSchedulerRunnerProgressAttemptCurrent(
13237
13405
  WHERE newer_attempt.run_id = current_attempt.run_id
13238
13406
  AND newer_attempt.attempt > current_attempt.attempt
13239
13407
  )
13240
- ) AS current
13241
- `,
13408
+ )`;
13409
+ }
13410
+
13411
+ export async function isPostgresSchedulerRunnerProgressAttemptCurrent(
13412
+ client: PostgresSchedulerQueryClient,
13413
+ input: { runId: string; attempt: number },
13414
+ options?: Pick<PostgresSchedulerOptions, 'schema'>,
13415
+ ): Promise<boolean> {
13416
+ const result = await client.query<{ current: boolean }>(
13417
+ `SELECT ${runnerProgressAttemptCurrentCondition(options)} AS current`,
13242
13418
  [input.runId, input.attempt],
13243
13419
  );
13244
13420
  return result.rows[0]?.current === true;
13245
13421
  }
13246
13422
 
13423
+ /** Observe terminal ingress authority without loading the stored result. */
13424
+ export async function readPostgresSchedulerRunnerAttemptAuthority(
13425
+ client: PostgresSchedulerQueryClient,
13426
+ input: { runId: string; attempt: number },
13427
+ options?: Pick<PostgresSchedulerOptions, 'schema'>,
13428
+ ): Promise<{ current: boolean; terminalRecorded: boolean }> {
13429
+ const result = await client.query<{
13430
+ current: boolean;
13431
+ terminal_recorded: boolean;
13432
+ }>(
13433
+ `
13434
+ SELECT ${runnerProgressAttemptCurrentCondition(options)} AS current,
13435
+ EXISTS (
13436
+ SELECT 1
13437
+ FROM ${tableName('attempts', options)}
13438
+ WHERE run_id = $1
13439
+ AND attempt = $2
13440
+ AND runner_result_json IS NOT NULL
13441
+ AND runner_result_json <> 'null'::jsonb
13442
+ ) AS terminal_recorded
13443
+ `,
13444
+ [input.runId, input.attempt],
13445
+ );
13446
+ return {
13447
+ current: result.rows[0]?.current === true,
13448
+ terminalRecorded: result.rows[0]?.terminal_recorded === true,
13449
+ };
13450
+ }
13451
+
13247
13452
  /**
13248
13453
  * Push execution (B2-final): the detached runner's liveness/cancellation poll.
13249
13454
  * Bumps `heartbeat_at` on the runner's attempt row (the same liveness column
@@ -17667,6 +17872,13 @@ export async function readPostgresSchedulerResult(
17667
17872
  );
17668
17873
  const row = result.rows[0] ?? null;
17669
17874
  if (!row) return null;
17875
+ return schedulerResultFromRow(row, options?.includeCancelledOutput);
17876
+ }
17877
+
17878
+ function schedulerResultFromRow(
17879
+ row: RunRow,
17880
+ includeCancelledOutput = false,
17881
+ ): PlaySchedulerResultEnvelope | null {
17670
17882
  if (
17671
17883
  row.status !== 'completed' &&
17672
17884
  row.status !== 'failed' &&
@@ -17684,7 +17896,7 @@ export async function readPostgresSchedulerResult(
17684
17896
  status: row.status,
17685
17897
  ...(row.status === 'completed' ||
17686
17898
  ((row.status === 'failed' ||
17687
- (row.status === 'cancelled' && options?.includeCancelledOutput)) &&
17899
+ (row.status === 'cancelled' && includeCancelledOutput)) &&
17688
17900
  row.terminal_result_json !== null)
17689
17901
  ? { output: row.terminal_result_json }
17690
17902
  : {}),
@@ -18590,7 +18802,13 @@ function runLedgerOutboxPrefixClaimSql(
18590
18802
  Parameters<typeof readPendingPostgresSchedulerOutboxEvents>[1]
18591
18803
  >,
18592
18804
  options: Pick<PostgresSchedulerOptions, 'schema'> | undefined,
18593
- acknowledgement: { eventIds: string[]; targetEventId: string } | undefined,
18805
+ acknowledgement:
18806
+ | {
18807
+ eventIds: string[];
18808
+ targetEventId: string;
18809
+ createdProjectionWake?: RunCreatedProjectionWake;
18810
+ }
18811
+ | undefined,
18594
18812
  supportsTerminalBatchMarker: boolean,
18595
18813
  fence?: { runId: string; expectedHeadEventId?: string },
18596
18814
  ): string {
@@ -19020,12 +19238,7 @@ function runLedgerOutboxPrefixClaimSql(
19020
19238
  ${
19021
19239
  acknowledgement
19022
19240
  ? `, notified AS MATERIALIZED (
19023
- SELECT delivered.*,
19024
- pg_notify(
19025
- $7,
19026
- json_build_object('eventId', event_id, 'kind', 'outbox_delivered')::text
19027
- ) AS notification
19028
- FROM delivered
19241
+ ${runLedgerDeliveredNotificationsSql('$7', acknowledgement.createdProjectionWake ? { queue: '$8', event: '$9' } : undefined)}
19029
19242
  )`
19030
19243
  : ''
19031
19244
  }
@@ -19156,7 +19369,11 @@ async function claimRunLedgerOutboxPrefixInBatch(
19156
19369
  recoverTransportDelivery?: boolean;
19157
19370
  },
19158
19371
  options?: Pick<PostgresSchedulerOptions, 'schema'>,
19159
- acknowledgement?: { eventIds: string[]; targetEventId: string },
19372
+ acknowledgement?: {
19373
+ eventIds: string[];
19374
+ targetEventId: string;
19375
+ createdProjectionWake?: RunCreatedProjectionWake;
19376
+ },
19160
19377
  ): Promise<{
19161
19378
  events: PostgresSchedulerOutboxEvent[];
19162
19379
  deliveredRows: RunLedgerOutboxDeliveredRow[];
@@ -19184,6 +19401,12 @@ async function claimRunLedgerOutboxPrefixInBatch(
19184
19401
  acknowledgement.eventIds,
19185
19402
  acknowledgement.targetEventId,
19186
19403
  postgresSchedulerNotifyChannel('terminal', options),
19404
+ ...(acknowledgement.createdProjectionWake
19405
+ ? [
19406
+ acknowledgement.createdProjectionWake.queueName,
19407
+ acknowledgement.createdProjectionWake.eventName,
19408
+ ]
19409
+ : []),
19187
19410
  ]
19188
19411
  : input.extendOwnedHead
19189
19412
  ? [input.extendOwnedHead]
@@ -19395,7 +19618,11 @@ async function claimPostgresSchedulerOutboxPrefix(
19395
19618
  Parameters<typeof readPendingPostgresSchedulerOutboxEvents>[1]
19396
19619
  >,
19397
19620
  options?: Pick<PostgresSchedulerOptions, 'schema'>,
19398
- acknowledgement?: { eventIds: string[]; targetEventId: string },
19621
+ acknowledgement?: {
19622
+ eventIds: string[];
19623
+ targetEventId: string;
19624
+ createdProjectionWake?: RunCreatedProjectionWake;
19625
+ },
19399
19626
  ): Promise<{
19400
19627
  events: PostgresSchedulerOutboxEvent[];
19401
19628
  deliveredRows: RunLedgerOutboxDeliveredRow[];
@@ -19426,6 +19653,12 @@ async function claimPostgresSchedulerOutboxPrefix(
19426
19653
  acknowledgement.eventIds,
19427
19654
  acknowledgement.targetEventId,
19428
19655
  postgresSchedulerNotifyChannel('terminal', options),
19656
+ ...(acknowledgement.createdProjectionWake
19657
+ ? [
19658
+ acknowledgement.createdProjectionWake.queueName,
19659
+ acknowledgement.createdProjectionWake.eventName,
19660
+ ]
19661
+ : []),
19429
19662
  ]
19430
19663
  : input.extendOwnedHead
19431
19664
  ? [input.extendOwnedHead]
@@ -19654,13 +19887,7 @@ export async function readPostgresSchedulerOutboxDeliveryStatus(
19654
19887
  ${
19655
19888
  options?.includeBlocked === false
19656
19889
  ? 'false'
19657
- : `target.failed_at IS NOT NULL OR EXISTS (
19658
- SELECT 1
19659
- FROM ${tableName('outbox', options)} predecessor
19660
- WHERE predecessor.run_id = target.run_id
19661
- AND predecessor.failed_at IS NOT NULL
19662
- AND ${runLedgerPrecedesSql('predecessor', 'target', true)}
19663
- )`
19890
+ : outboxDeliveryBlockedSql('target', options)
19664
19891
  } AS blocked
19665
19892
  FROM ${tableName('outbox', options)} target
19666
19893
  WHERE target.run_id = $1 AND target.event_id = $2`,
@@ -19674,6 +19901,19 @@ export async function readPostgresSchedulerOutboxDeliveryStatus(
19674
19901
  };
19675
19902
  }
19676
19903
 
19904
+ function outboxDeliveryBlockedSql(
19905
+ target: string,
19906
+ options?: Pick<PostgresSchedulerOptions, 'schema'>,
19907
+ ): string {
19908
+ return `${target}.failed_at IS NOT NULL OR EXISTS (
19909
+ SELECT 1
19910
+ FROM ${tableName('outbox', options)} predecessor
19911
+ WHERE predecessor.run_id = ${target}.run_id
19912
+ AND predecessor.failed_at IS NOT NULL
19913
+ AND ${runLedgerPrecedesSql('predecessor', target, true)}
19914
+ )`;
19915
+ }
19916
+
19677
19917
  /**
19678
19918
  * Read the age of one oldest pending outbox row through the partial
19679
19919
  * `(created_at, event_id)` index. This deliberately avoids the historical
@@ -20156,6 +20396,31 @@ function runLedgerAcknowledgeSql(
20156
20396
  AS lag_ms`;
20157
20397
  }
20158
20398
 
20399
+ type RunCreatedProjectionWake = { queueName: string; eventName: string };
20400
+
20401
+ /** Both standalone and continuation ACKs publish the same atomic wake. */
20402
+ function runLedgerDeliveredNotificationsSql(
20403
+ terminalChannel: string,
20404
+ wake?: { queue: string; event: string },
20405
+ ): string {
20406
+ return `SELECT delivered.*,
20407
+ pg_notify(${terminalChannel},
20408
+ json_build_object('eventId', event_id, 'kind', 'outbox_delivered')::text
20409
+ ) AS notification
20410
+ ${
20411
+ wake
20412
+ ? `,
20413
+ CASE WHEN event_type = ${RUN_EVENT_KIND_SQL_LITERAL.runCreated}
20414
+ THEN absurd.emit_event(${wake.queue}, ${wake.event}, 'true'::jsonb)
20415
+ END AS engine_wake,
20416
+ CASE WHEN event_type = ${RUN_EVENT_KIND_SQL_LITERAL.runCreated}
20417
+ THEN pg_notify('absurd_wake', ${wake.queue})
20418
+ END AS engine_notification`
20419
+ : ''
20420
+ }
20421
+ FROM delivered`;
20422
+ }
20423
+
20159
20424
  /**
20160
20425
  * Lease-fenced acknowledgement that also reports each row's event type and
20161
20426
  * commit-to-acknowledgement lag on the database clock, so lag metrics never
@@ -20166,6 +20431,7 @@ export async function markPostgresSchedulerOutboxDeliveredRows(
20166
20431
  eventIds: string[],
20167
20432
  options?: Pick<PostgresSchedulerOptions, 'schema'> & {
20168
20433
  leaseOwner?: string | null;
20434
+ createdProjectionWake?: RunCreatedProjectionWake;
20169
20435
  },
20170
20436
  ): Promise<RunLedgerOutboxDeliveredRow[]> {
20171
20437
  if (eventIds.length === 0) return [];
@@ -20177,20 +20443,21 @@ export async function markPostgresSchedulerOutboxDeliveredRows(
20177
20443
  `
20178
20444
  WITH delivered AS (
20179
20445
  ${runLedgerAcknowledgeSql(options, '$1', '$2')}
20446
+ ), notified AS MATERIALIZED (
20447
+ ${runLedgerDeliveredNotificationsSql('$3', options?.createdProjectionWake ? { queue: '$4', event: '$5' } : undefined)}
20180
20448
  )
20181
- SELECT event_id,
20182
- event_type,
20183
- lag_ms,
20184
- pg_notify(
20185
- $3,
20186
- json_build_object('eventId', event_id, 'kind', 'outbox_delivered')::text
20187
- )
20188
- FROM delivered
20449
+ SELECT event_id, event_type, lag_ms FROM notified
20189
20450
  `,
20190
20451
  [
20191
20452
  eventIds,
20192
20453
  options?.leaseOwner?.trim() || null,
20193
20454
  postgresSchedulerNotifyChannel('terminal', options),
20455
+ ...(options?.createdProjectionWake
20456
+ ? [
20457
+ options.createdProjectionWake.queueName,
20458
+ options.createdProjectionWake.eventName,
20459
+ ]
20460
+ : []),
20194
20461
  ],
20195
20462
  );
20196
20463
  return result.rows.map((row) => {
@@ -20705,6 +20972,8 @@ export function postgresSchedulerCancellationOutboxLease(runId: string): {
20705
20972
  }
20706
20973
 
20707
20974
  export type RunLedgerOutboxDispatchInput = {
20975
+ /** App-owned transport; launch authority and routing still come from the lease. */
20976
+ appRuntimeFetch?: typeof fetch;
20708
20977
  /** Dedicated bounded checkout for indexing; claims/ACKs use withClient. */
20709
20978
  withObservabilityClient?: RunLedgerOutboxWithClient;
20710
20979
 
@@ -21054,10 +21323,14 @@ async function dispatchRunLedgerOutboxBatchWithScope(
21054
21323
  // A committed terminal is running-work control traffic. Use the existing
21055
21324
  // alternating critical admission lane so its short lease operations do not
21056
21325
  // sit behind a full ordinary-work backlog; capacity is unchanged.
21057
- const terminalRunId =
21326
+ // Only a caller that explicitly targets this run's terminal may reuse the
21327
+ // claim's first-HTTP lease proof. Background discovery can still prioritize
21328
+ // a terminal after claiming it, but must renew before its first append.
21329
+ const targetedTerminalRunId =
21058
21330
  scope && scope.targetEventId === `run-terminal:${scope.runId}`
21059
21331
  ? scope.runId
21060
21332
  : null;
21333
+ let terminalRunId = targetedTerminalRunId;
21061
21334
  const withClient: RunLedgerOutboxWithClient = (operation, trace) =>
21062
21335
  acquireClient(
21063
21336
  operation,
@@ -21092,7 +21365,11 @@ async function dispatchRunLedgerOutboxBatchWithScope(
21092
21365
  WHEN ${runLedgerSearchOnlyOutboxFactSql('head')} THEN NULL::jsonb
21093
21366
  ELSE ${runLedgerDispatchLaunchSql('runs')}
21094
21367
  END`
21095
- : 'NULL::jsonb';
21368
+ : input.searchOnly
21369
+ ? 'NULL::jsonb'
21370
+ : `CASE WHEN head.event_type = ${RUN_EVENT_KIND_SQL_LITERAL.runCreated}
21371
+ THEN ${runLedgerDispatchLaunchSql('runs')}
21372
+ ELSE NULL::jsonb END`;
21096
21373
  const transportHeadSelection =
21097
21374
  tokenAuthority === 'gateway' &&
21098
21375
  !input.searchOnly &&
@@ -21439,7 +21716,7 @@ async function dispatchRunLedgerOutboxBatchWithScope(
21439
21716
  dispatch_launch_json: unknown;
21440
21717
  }>(
21441
21718
  `SELECT runs.run_id,
21442
- ${needsLaunch ? runLedgerDispatchLaunchSql('runs') : 'NULL::jsonb'} AS dispatch_launch_json
21719
+ ${!input.searchOnly ? runLedgerDispatchLaunchSql('runs') : 'NULL::jsonb'} AS dispatch_launch_json
21443
21720
  FROM ${tableName('runs', options)} AS runs
21444
21721
  WHERE runs.run_id = $1 FOR UPDATE`,
21445
21722
  [targetRunId],
@@ -21582,6 +21859,21 @@ async function dispatchRunLedgerOutboxBatchWithScope(
21582
21859
  });
21583
21860
  }
21584
21861
 
21862
+ // Background discovery cannot classify its head before claiming it. Once
21863
+ // the owned prefix contains a terminal, its renewal and acknowledgement
21864
+ // are the same critical running-work control as a targeted worker drain.
21865
+ const prioritizeClaimedTerminal = () => {
21866
+ if (
21867
+ !input.searchOnly &&
21868
+ events.some((event) =>
21869
+ RUN_SCHEDULER_OUTBOX_TERMINAL_EVENT_TYPES.includes(event.eventType),
21870
+ )
21871
+ ) {
21872
+ terminalRunId = events[0]!.runId;
21873
+ }
21874
+ };
21875
+ prioritizeClaimedTerminal();
21876
+
21585
21877
  const claimedAtMs = Date.now();
21586
21878
  let authorityReadyAtMs: number | null = null;
21587
21879
  let appendFinishedAtMs: number | null = null;
@@ -21948,6 +22240,9 @@ async function dispatchRunLedgerOutboxBatchWithScope(
21948
22240
  gatewayRuntimeApi ?? input.runtimeApi ?? (await resolveRunRuntimeApi());
21949
22241
  runtimeApi = {
21950
22242
  ...resolvedRuntimeApi,
22243
+ ...(tokenAuthority === 'local_mint' && input.appRuntimeFetch
22244
+ ? { fetch: input.appRuntimeFetch }
22245
+ : {}),
21951
22246
  ...(gatewayRuntimeApi && input.runtimeApi
21952
22247
  ? {
21953
22248
  ...(input.runtimeApi.fetch
@@ -22082,6 +22377,7 @@ async function dispatchRunLedgerOutboxBatchWithScope(
22082
22377
  );
22083
22378
  }
22084
22379
  events = widened;
22380
+ prioritizeClaimedTerminal();
22085
22381
  }
22086
22382
  // Widening can add deferred facts to the lease. Project the complete
22087
22383
  // frozen prefix before any HTTP append, including its newly claimed rows.
@@ -22137,7 +22433,7 @@ async function dispatchRunLedgerOutboxBatchWithScope(
22137
22433
  }
22138
22434
  if (
22139
22435
  firstRenewal &&
22140
- terminalRunId === first.runId &&
22436
+ targetedTerminalRunId === first.runId &&
22141
22437
  tokenAuthority === 'gateway' &&
22142
22438
  runtimeApi?.runLedgerIngressPrefixBatching === true &&
22143
22439
  ledgerEvents !== null &&
@@ -22267,12 +22563,30 @@ async function dispatchRunLedgerOutboxBatchWithScope(
22267
22563
  await renewDelivery();
22268
22564
  }
22269
22565
  appendFinishedAtMs = Date.now();
22566
+ let createdProjectionWake: RunCreatedProjectionWake | undefined;
22567
+ if (
22568
+ events.some((event) => event.eventType === RUN_EVENT_KIND.runCreated) &&
22569
+ launchJson
22570
+ ) {
22571
+ const routing = parseRunLedgerDispatchLaunch(launchJson);
22572
+ if (routing.requiresRunCreatedProjection) {
22573
+ const {
22574
+ absurdQueueNameForLaunch,
22575
+ absurdRunCreatedProjectionEventName,
22576
+ } = await import('./absurd-shared');
22577
+ createdProjectionWake = {
22578
+ queueName: absurdQueueNameForLaunch(routing),
22579
+ eventName: absurdRunCreatedProjectionEventName(first.runId),
22580
+ };
22581
+ }
22582
+ }
22270
22583
  const acknowledgement = await acknowledgeRunLedgerPrefixWithContinuation(
22271
22584
  withClient,
22272
22585
  {
22273
22586
  input,
22274
22587
  events,
22275
22588
  leaseOwner,
22589
+ createdProjectionWake,
22276
22590
  runtimeApi,
22277
22591
  scope,
22278
22592
  continuation,
@@ -22667,6 +22981,7 @@ async function acknowledgeRunLedgerPrefixWithContinuation(
22667
22981
  input: RunLedgerOutboxDispatchInput;
22668
22982
  events: PostgresSchedulerOutboxEvent[];
22669
22983
  leaseOwner: string;
22984
+ createdProjectionWake?: RunCreatedProjectionWake;
22670
22985
  runtimeApi: WorkerRuntimeApiContext | null;
22671
22986
  scope?: RunLedgerOutboxDrainScope;
22672
22987
  continuation: { current: RunLedgerOutboxDrainScope | null };
@@ -22769,13 +23084,21 @@ async function acknowledgeRunLedgerPrefixWithContinuation(
22769
23084
  input.runtimeApi?.runLedgerIngressPrefixBatching === true,
22770
23085
  },
22771
23086
  options,
22772
- { eventIds, targetEventId },
23087
+ {
23088
+ eventIds,
23089
+ targetEventId,
23090
+ createdProjectionWake: input.createdProjectionWake,
23091
+ },
22773
23092
  )
22774
23093
  : {
22775
23094
  deliveredRows: await markPostgresSchedulerOutboxDeliveredRows(
22776
23095
  client,
22777
23096
  eventIds,
22778
- { ...options, leaseOwner: input.leaseOwner },
23097
+ {
23098
+ ...options,
23099
+ leaseOwner: input.leaseOwner,
23100
+ createdProjectionWake: input.createdProjectionWake,
23101
+ },
22779
23102
  ),
22780
23103
  events: [],
22781
23104
  };