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
@@ -18,6 +18,8 @@ export type RunLedgerDispatchLaunch = Pick<
18
18
  | 'runtimeTokenOrigin'
19
19
  | 'orgId'
20
20
  | 'runtimeAuthority'
21
+ | 'absurdReleaseId'
22
+ | 'requiresRunCreatedProjection'
21
23
  | 'vercelProtectionBypassToken'
22
24
  >;
23
25
 
@@ -34,6 +36,8 @@ export function runLedgerDispatchLaunchSql(alias: string): string {
34
36
  'runtimeTokenOrigin',
35
37
  'orgId',
36
38
  'runtimeAuthority',
39
+ 'absurdReleaseId',
40
+ 'requiresRunCreatedProjection',
37
41
  'vercelProtectionBypassToken',
38
42
  ];
39
43
  return `jsonb_build_object(${fields
@@ -886,6 +886,7 @@ export async function deliverRunCreatedOutboxEventInline(input: {
886
886
  withClient: RunLedgerOutboxWithClient;
887
887
  schedulerOptions?: Pick<PostgresSchedulerOptions, 'schema'>;
888
888
  tokenAuthority: RunLedgerOutboxTokenAuthority;
889
+ appRuntimeFetch?: typeof fetch;
889
890
  deadlineMs?: number;
890
891
  /** Test seam; clamped to `RUN_CREATED_INLINE_RELEASE_GRACE_MS`. */
891
892
  releaseGraceMs?: number;
@@ -926,6 +927,9 @@ export async function deliverRunCreatedOutboxEventInline(input: {
926
927
  limit: 1,
927
928
  leaseSeconds: RUN_CREATED_INLINE_DELIVERY_LEASE_SECONDS,
928
929
  tokenAuthority: input.tokenAuthority,
930
+ ...(input.appRuntimeFetch
931
+ ? { appRuntimeFetch: input.appRuntimeFetch }
932
+ : {}),
929
933
  signal: controller.signal,
930
934
  onAbortedLeaseRelease: (released) => {
931
935
  abandoned.releaseFailed = !released;
@@ -1223,6 +1223,15 @@ export async function getRuntimeRunLogPage(
1223
1223
  };
1224
1224
  }
1225
1225
 
1226
+ /** Acquire inside the writer's transaction; all namespaces share these objects. */
1227
+ export async function lockRuntimeEventSearchDdl(
1228
+ client: PostgresSchedulerQueryClient,
1229
+ ): Promise<void> {
1230
+ await client.query(
1231
+ `SELECT pg_advisory_xact_lock(hashtext('deepline:runtime-observability'), hashtext('schema-ddl'))`,
1232
+ );
1233
+ }
1234
+
1226
1235
  async function ensureRuntimeEventFetchReceiptFunction(
1227
1236
  client: PostgresSchedulerQueryClient,
1228
1237
  ): Promise<void> {
@@ -242,6 +242,19 @@ export async function readRuntimeOrgSandboxLimitCacheViaRuntimeScheduler(input:
242
242
  );
243
243
  }
244
244
 
245
+ type RuntimeSandboxReservationProof = {
246
+ org_id: string;
247
+ run_id: string;
248
+ provider: RuntimeSandboxProviderId;
249
+ operation_attempt: number | null;
250
+ operation_environment: 'preview' | 'production' | null;
251
+ operation_provider: RuntimeSandboxProviderId | null;
252
+ provider_operation_token: string | null;
253
+ operation_state: RuntimeSandboxOperationState | null;
254
+ released_at: Date | null;
255
+ sandbox_id: string | null;
256
+ };
257
+
245
258
  export async function reserveRuntimeSandboxCapacityLease(
246
259
  client: PostgresSchedulerQueryClient,
247
260
  input: {
@@ -272,11 +285,47 @@ export async function reserveRuntimeSandboxCapacityLease(
272
285
  const orgId = assertId(input.orgId, 'orgId');
273
286
  const runId = assertId(input.runId, 'runId');
274
287
  const { cache, leases, operations, runs } = schemaTables(options);
288
+ const leaseId = input.leaseId?.trim() || randomUUID();
289
+ const unsentProviderOperationToken = unsentReservationInputs.get(input);
290
+ const providerOperationToken =
291
+ input.attempt === undefined
292
+ ? null
293
+ : (unsentProviderOperationToken ??
294
+ runtimeSandboxProviderOperationToken(leaseId));
295
+ const resumePendingDaytonaOperation =
296
+ input.resumePendingDaytonaOperation === true &&
297
+ input.provider === RuntimeSandboxProvider.Daytona &&
298
+ input.attempt !== undefined;
275
299
  // This is deliberately a soft admission check: concurrent workers may both
276
300
  // observe space and insert. Avoid locks and long transactions; the provider
277
301
  // has its own global safety cap and the 50-minute lease bounds crashes.
278
- const observed = await client.query<{ limit: number; active_leases: number }>(
302
+ // Read the capacity and existing intent together. Neither observation proves
303
+ // an INSERT winner; the fenced write must return proof or read it back.
304
+ const observed = await client.query<{
305
+ limit: number;
306
+ active_leases: number;
307
+ pending_operations: Array<{
308
+ lease_id: string;
309
+ provider_operation_token: string | null;
310
+ attempt: number;
311
+ environment: 'preview' | 'production';
312
+ }>;
313
+ existing_operation: {
314
+ org_id: string;
315
+ run_id: string;
316
+ provider: RuntimeSandboxProviderId;
317
+ operation_attempt: number | null;
318
+ operation_environment: 'preview' | 'production' | null;
319
+ operation_provider: RuntimeSandboxProviderId | null;
320
+ provider_operation_token: string | null;
321
+ operation_state: RuntimeSandboxOperationState | null;
322
+ released_at: string | null;
323
+ sandbox_id: string | null;
324
+ } | null;
325
+ inserted_proof: RuntimeSandboxReservationProof | null;
326
+ }>(
279
327
  `
328
+ WITH observed AS MATERIALIZED (
280
329
  SELECT
281
330
  coalesce(
282
331
  (
@@ -300,53 +349,119 @@ export async function reserveRuntimeSandboxCapacityLease(
300
349
  -- is only the crash backstop for a create that never became
301
350
  -- durable enough to bind to a sandbox id.
302
351
  AND (sandbox_id IS NOT NULL OR expires_at > clock_timestamp())
303
- ) AS active_leases
352
+ ) AS active_leases,
353
+ coalesce((
354
+ SELECT jsonb_agg(pending)
355
+ FROM (
356
+ SELECT lease.lease_id, operation.provider_operation_token,
357
+ operation.attempt, operation.environment
358
+ FROM ${operations} AS operation
359
+ JOIN ${leases} AS lease ON lease.lease_id = operation.operation_key
360
+ WHERE $6::boolean
361
+ AND operation.org_id = $1
362
+ AND operation.run_id = $3
363
+ AND operation.provider = $4
364
+ AND operation.state = $7
365
+ AND lease.released_at IS NULL
366
+ AND lease.sandbox_id IS NULL
367
+ ORDER BY operation.created_at ASC
368
+ LIMIT 2
369
+ ) AS pending
370
+ ), '[]'::jsonb) AS pending_operations,
371
+ (
372
+ SELECT to_jsonb(existing)
373
+ FROM (
374
+ SELECT lease.org_id, lease.run_id, lease.provider,
375
+ operation.attempt AS operation_attempt,
376
+ operation.environment AS operation_environment,
377
+ operation.provider AS operation_provider,
378
+ operation.provider_operation_token,
379
+ operation.state AS operation_state,
380
+ lease.released_at, lease.sandbox_id
381
+ FROM ${leases} AS lease
382
+ LEFT JOIN ${operations} AS operation
383
+ ON operation.operation_key = lease.lease_id
384
+ WHERE lease.lease_id = $5
385
+ ) AS existing
386
+ ) AS existing_operation
387
+ ), active_run AS MATERIALIZED (
388
+ SELECT run.run_id
389
+ FROM ${runs} AS run, observed
390
+ WHERE $8::integer IS NOT NULL
391
+ AND observed.existing_operation IS NULL
392
+ AND jsonb_array_length(observed.pending_operations) = 0
393
+ AND (observed.active_leases < observed."limit" OR $10::text <> 'enforce')
394
+ AND run.run_id = $3
395
+ AND run.status IN ('queued', 'running', 'waiting')
396
+ AND NOT EXISTS (
397
+ SELECT 1 FROM ${quoteIdent(options?.schema ?? 'runtime_scheduler')}."control"
398
+ WHERE key = 'runtime_cancel:' || $3::text
399
+ )
400
+ FOR UPDATE OF run
401
+ ), reserved_lease AS (
402
+ INSERT INTO ${leases} (
403
+ lease_id, org_id, run_id, provider, expires_at
404
+ )
405
+ SELECT $5, $1, $3, $4,
406
+ clock_timestamp() + ($11::integer * interval '1 millisecond')
407
+ WHERE EXISTS (SELECT 1 FROM active_run)
408
+ ON CONFLICT (lease_id) DO NOTHING
409
+ RETURNING lease_id, org_id, run_id, provider, released_at, sandbox_id
410
+ ), reserved_operation AS (
411
+ INSERT INTO ${operations} (
412
+ operation_key, provider_operation_token, provider, run_id, attempt, org_id, environment, state
413
+ )
414
+ SELECT lease_id, $12, $4, $3, $8, $1, $9, $7
415
+ FROM reserved_lease
416
+ ON CONFLICT (operation_key) DO NOTHING
417
+ RETURNING operation_key, provider_operation_token, provider,
418
+ attempt, environment, state
419
+ )
420
+ SELECT observed.*, (
421
+ SELECT to_jsonb(proof)
422
+ FROM (
423
+ SELECT lease.org_id, lease.run_id, lease.provider,
424
+ operation.attempt AS operation_attempt,
425
+ operation.environment AS operation_environment,
426
+ operation.provider AS operation_provider,
427
+ operation.provider_operation_token,
428
+ operation.state AS operation_state,
429
+ lease.released_at, lease.sandbox_id
430
+ FROM reserved_lease AS lease
431
+ JOIN reserved_operation AS operation
432
+ ON operation.operation_key = lease.lease_id
433
+ ) AS proof
434
+ ) AS inserted_proof
435
+ FROM observed
304
436
  `,
305
- [orgId, DEFAULT_PLAY_SANDBOX_CONCURRENCY],
437
+ [
438
+ orgId,
439
+ DEFAULT_PLAY_SANDBOX_CONCURRENCY,
440
+ runId,
441
+ input.provider,
442
+ leaseId,
443
+ resumePendingDaytonaOperation,
444
+ RuntimeSandboxOperationState.Reserved,
445
+ input.attempt ?? null,
446
+ input.environment ?? 'production',
447
+ input.mode ?? 'enforce',
448
+ RUNTIME_SANDBOX_CAPACITY_LEASE_MS,
449
+ providerOperationToken,
450
+ ],
306
451
  );
307
452
  const limit = Number(
308
453
  observed.rows[0]?.limit ?? DEFAULT_PLAY_SANDBOX_CONCURRENCY,
309
454
  );
310
455
  const activeLeases = Number(observed.rows[0]?.active_leases ?? 0);
311
456
  const wouldDefer = activeLeases >= limit;
312
- const leaseId = input.leaseId?.trim() || randomUUID();
313
- const unsentProviderOperationToken = unsentReservationInputs.get(input);
314
- const providerOperationToken =
315
- input.attempt === undefined
316
- ? null
317
- : (unsentProviderOperationToken ??
318
- runtimeSandboxProviderOperationToken(leaseId));
319
- if (
320
- input.resumePendingDaytonaOperation === true &&
321
- input.provider === RuntimeSandboxProvider.Daytona &&
322
- input.attempt !== undefined
323
- ) {
324
- const pending = await client.query<{
325
- lease_id: string;
326
- provider_operation_token: string | null;
327
- attempt: number;
328
- environment: 'preview' | 'production';
329
- }>(
330
- `SELECT lease.lease_id, operation.provider_operation_token,
331
- operation.attempt, operation.environment
332
- FROM ${operations} AS operation
333
- JOIN ${leases} AS lease ON lease.lease_id = operation.operation_key
334
- WHERE operation.org_id = $1
335
- AND operation.run_id = $2
336
- AND operation.provider = $3
337
- AND operation.state = $4
338
- AND lease.released_at IS NULL
339
- AND lease.sandbox_id IS NULL
340
- ORDER BY operation.created_at ASC
341
- LIMIT 2`,
342
- [orgId, runId, input.provider, RuntimeSandboxOperationState.Reserved],
343
- );
344
- if (pending.rows.length > 1) {
457
+ if (resumePendingDaytonaOperation) {
458
+ const pending = observed.rows[0]?.pending_operations ?? [];
459
+ if (pending.length > 1) {
345
460
  throw new Error(
346
461
  'More than one Daytona sandbox operation is pending for this run.',
347
462
  );
348
463
  }
349
- const pendingOperation = pending.rows[0];
464
+ const pendingOperation = pending[0];
350
465
  if (
351
466
  pendingOperation &&
352
467
  !(
@@ -384,33 +499,9 @@ export async function reserveRuntimeSandboxCapacityLease(
384
499
  };
385
500
  }
386
501
  }
387
- const existing = await client.query<{
388
- org_id: string;
389
- run_id: string;
390
- provider: RuntimeSandboxProviderId;
391
- operation_attempt: number | null;
392
- operation_environment: 'preview' | 'production' | null;
393
- operation_provider: RuntimeSandboxProviderId | null;
394
- provider_operation_token: string | null;
395
- operation_state: RuntimeSandboxOperationState | null;
396
- released_at: Date | null;
397
- sandbox_id: string | null;
398
- }>(
399
- `SELECT lease.org_id, lease.run_id, lease.provider,
400
- operation.attempt AS operation_attempt,
401
- operation.environment AS operation_environment,
402
- operation.provider AS operation_provider,
403
- operation.provider_operation_token,
404
- operation.state AS operation_state,
405
- lease.released_at, lease.sandbox_id
406
- FROM ${leases} AS lease
407
- LEFT JOIN ${operations} AS operation
408
- ON operation.operation_key = lease.lease_id
409
- WHERE lease.lease_id = $1`,
410
- [leaseId],
411
- );
412
- if (existing.rows[0]) {
413
- const row = existing.rows[0];
502
+ const existingOperation = observed.rows[0]?.existing_operation;
503
+ if (existingOperation) {
504
+ const row = existingOperation;
414
505
  if (
415
506
  row.org_id !== orgId ||
416
507
  row.run_id !== runId ||
@@ -487,64 +578,30 @@ export async function reserveRuntimeSandboxCapacityLease(
487
578
  if (wouldDefer && (input.mode ?? 'enforce') === 'enforce') {
488
579
  return { granted: false, limit, activeLeases };
489
580
  }
490
- await client.query(
491
- `
492
- WITH active_run AS MATERIALIZED (
493
- SELECT run_id FROM ${runs}
494
- WHERE run_id = $3 AND status IN ('queued', 'running', 'waiting')
495
- AND NOT EXISTS (
496
- SELECT 1 FROM ${quoteIdent(options?.schema ?? 'runtime_scheduler')}."control"
497
- WHERE key = 'runtime_cancel:' || $3::text
498
- )
499
- FOR UPDATE
500
- ), reserved_lease AS (
501
- INSERT INTO ${leases} (
502
- lease_id, org_id, run_id, provider, expires_at
503
- )
504
- SELECT $1, $2, $3, $4,
505
- clock_timestamp() + ($5::integer * interval '1 millisecond')
506
- WHERE $6::integer IS NULL OR EXISTS (SELECT 1 FROM active_run)
507
- ON CONFLICT (lease_id) DO NOTHING
508
- RETURNING lease_id
509
- )
510
- INSERT INTO ${operations} (
511
- operation_key, provider_operation_token, provider, run_id, attempt, org_id, environment, state
512
- )
513
- SELECT
514
- reserved_lease.lease_id, $9, $4, $3, $6, $2, $7, $8
515
- FROM reserved_lease
516
- WHERE $6::integer IS NOT NULL
517
- ON CONFLICT (operation_key) DO NOTHING
518
- `,
519
- [
520
- leaseId,
521
- orgId,
522
- runId,
523
- input.provider,
524
- RUNTIME_SANDBOX_CAPACITY_LEASE_MS,
525
- input.attempt ?? null,
526
- input.environment ?? 'production',
527
- RuntimeSandboxOperationState.Reserved,
528
- providerOperationToken,
529
- ],
530
- );
531
- // The caller may have lost the INSERT response after PostgreSQL committed,
532
- // or raced another identical retry. A second read is mandatory: conflict
533
- // swallowing alone would manufacture a lease without proving whose intent
534
- // it resumes.
535
- const persisted = await client.query<{
536
- org_id: string;
537
- run_id: string;
538
- provider: RuntimeSandboxProviderId;
539
- operation_attempt: number | null;
540
- operation_environment: 'preview' | 'production' | null;
541
- operation_provider: RuntimeSandboxProviderId | null;
542
- provider_operation_token: string | null;
543
- operation_state: RuntimeSandboxOperationState | null;
544
- released_at: Date | null;
545
- sandbox_id: string | null;
546
- }>(
547
- `SELECT lease.org_id, lease.run_id, lease.provider,
581
+ if (input.attempt === undefined) {
582
+ // Historical lease-only callers retain their read/write/readback contract.
583
+ await client.query(
584
+ `INSERT INTO ${leases} (lease_id, org_id, run_id, provider, expires_at)
585
+ VALUES ($1, $2, $3, $4,
586
+ clock_timestamp() + ($5::integer * interval '1 millisecond'))
587
+ ON CONFLICT (lease_id) DO NOTHING`,
588
+ [
589
+ leaseId,
590
+ orgId,
591
+ runId,
592
+ input.provider,
593
+ RUNTIME_SANDBOX_CAPACITY_LEASE_MS,
594
+ ],
595
+ );
596
+ }
597
+ // RETURNING proves only this statement's newly inserted lease AND intent.
598
+ // A conflict, legacy lease-only write or ambiguous result still needs the
599
+ // authoritative readback; swallowing a conflict never grants create rights.
600
+ const insertedProof = observed.rows[0]?.inserted_proof;
601
+ const persisted = insertedProof
602
+ ? { rows: [insertedProof] }
603
+ : await client.query<RuntimeSandboxReservationProof>(
604
+ `SELECT lease.org_id, lease.run_id, lease.provider,
548
605
  operation.attempt AS operation_attempt,
549
606
  operation.environment AS operation_environment,
550
607
  operation.provider AS operation_provider,
@@ -555,8 +612,8 @@ export async function reserveRuntimeSandboxCapacityLease(
555
612
  LEFT JOIN ${operations} AS operation
556
613
  ON operation.operation_key = lease.lease_id
557
614
  WHERE lease.lease_id = $1`,
558
- [leaseId],
559
- );
615
+ [leaseId],
616
+ );
560
617
  const row = persisted.rows[0];
561
618
  if (
562
619
  !row ||
@@ -0,0 +1,152 @@
1
+ import dns from 'node:dns';
2
+ import type { IncomingMessage, ServerResponse } from 'node:http';
3
+
4
+ export const RUNTIME_WORKER_WAKE_PATH = '/internal/runtime-worker/wake';
5
+
6
+ /** Wake the existing, fixed queue only. The hint cannot admit or name a task. */
7
+ export function handleRuntimeWorkerWake(
8
+ req: IncomingMessage,
9
+ res: ServerResponse,
10
+ input: { expectedToken: string | undefined; wake: (() => boolean) | null },
11
+ ): boolean {
12
+ if (
13
+ new URL(req.url ?? '/', 'http://worker.local').pathname !==
14
+ RUNTIME_WORKER_WAKE_PATH
15
+ )
16
+ return false;
17
+ const respond = (status: number, body: string) => {
18
+ res.writeHead(status, {
19
+ 'content-type': 'application/json',
20
+ 'cache-control': 'no-store',
21
+ ...(status === 405 ? { allow: 'POST' } : {}),
22
+ });
23
+ res.end(body);
24
+ };
25
+ if (req.method !== 'POST') {
26
+ respond(405, '{"error":"Use POST."}');
27
+ } else if (
28
+ !input.expectedToken?.trim() ||
29
+ req.headers.authorization?.trim() !== `Bearer ${input.expectedToken.trim()}`
30
+ ) {
31
+ respond(401, '{"error":"Unauthorized."}');
32
+ } else if (!input.wake?.()) {
33
+ respond(503, '{"error":"Worker is not accepting wakes."}');
34
+ } else {
35
+ respond(202, '{"ok":true}');
36
+ }
37
+ return true;
38
+ }
39
+
40
+ export type RuntimeWorkerWakeTarget = { url: string; token: string };
41
+
42
+ /** Deployment-owned routing only. No request may supply a worker destination. */
43
+ export function runtimeWorkerWakeTargetFromEnv(
44
+ env: Record<string, string | undefined> = process.env,
45
+ ): RuntimeWorkerWakeTarget | null {
46
+ const url = env.DEEPLINE_RUNTIME_WORKER_WAKE_URL?.trim();
47
+ const token = env.DEEPLINE_INTERNAL_TOKEN?.trim();
48
+ return url && token ? { url, token } : null;
49
+ }
50
+
51
+ /** A bounded hint, never a retry, admission, or correctness dependency. */
52
+ export async function requestRuntimeWorkerWake(
53
+ target: RuntimeWorkerWakeTarget,
54
+ ): Promise<boolean> {
55
+ const startedAt = performance.now();
56
+ let phase = 'resolve';
57
+ let status: number | undefined;
58
+ // Bun 1.3.14 can drop AbortSignal.timeout's timer when DNS removes its listener.
59
+ const controller = new AbortController();
60
+ const signal = controller.signal;
61
+ const timeout = setTimeout(
62
+ () =>
63
+ controller.abort(
64
+ new DOMException('Worker wake timed out.', 'TimeoutError'),
65
+ ),
66
+ 500,
67
+ );
68
+ try {
69
+ const url = new URL(target.url);
70
+ // Fly's private names use IPv6. Bun 1.3.14 can select an unusable IPv4
71
+ // address even with node:http's family: 6; dial the resolved address.
72
+ if (url.protocol === 'http:' && url.hostname.endsWith('.internal')) {
73
+ const address = await new Promise<string>((resolve, reject) => {
74
+ const abort = () => reject(signal.reason);
75
+ signal.addEventListener('abort', abort, { once: true });
76
+ dns.lookup(url.hostname, { family: 6 }, (error, address) => {
77
+ signal.removeEventListener('abort', abort);
78
+ if (signal.aborted) reject(signal.reason);
79
+ else if (error) reject(error);
80
+ else resolve(address);
81
+ });
82
+ });
83
+ url.hostname = `[${address}]`;
84
+ }
85
+ phase = 'request';
86
+ const response = await fetch(url, {
87
+ method: 'POST',
88
+ headers: { authorization: `Bearer ${target.token}` },
89
+ redirect: 'error',
90
+ signal,
91
+ });
92
+ status = response.status;
93
+ phase = 'cancel_response';
94
+ await response.body?.cancel();
95
+ if (status === 202) {
96
+ console.info('[runtime.worker_wake.accepted]', {
97
+ status,
98
+ ms: performance.now() - startedAt,
99
+ });
100
+ return true;
101
+ }
102
+ console.warn('[runtime.worker_wake.failed]', { status });
103
+ } catch (error) {
104
+ console.warn('[runtime.worker_wake.failed]', {
105
+ phase,
106
+ status,
107
+ error: error instanceof Error ? error.name : 'TransportError',
108
+ code:
109
+ error instanceof Error && 'code' in error
110
+ ? String(error.code)
111
+ : undefined,
112
+ });
113
+ } finally {
114
+ clearTimeout(timeout);
115
+ }
116
+ return false;
117
+ }
118
+
119
+ /** The existing gateway relays to its fixed private worker; it cannot name work. */
120
+ export async function handleRuntimeGatewayWorkerWake(
121
+ req: Request,
122
+ input: {
123
+ expectedToken: string | undefined;
124
+ target: RuntimeWorkerWakeTarget | null;
125
+ },
126
+ ): Promise<Response | null> {
127
+ if (new URL(req.url).pathname !== RUNTIME_WORKER_WAKE_PATH) return null;
128
+ const respond = (status: number, body: string) =>
129
+ new Response(body, {
130
+ status,
131
+ headers: {
132
+ 'content-type': 'application/json',
133
+ 'cache-control': 'no-store',
134
+ ...(status === 405 ? { allow: 'POST' } : {}),
135
+ },
136
+ });
137
+ if (req.method !== 'POST') return respond(405, '{"error":"Use POST."}');
138
+ if (
139
+ !input.expectedToken?.trim() ||
140
+ req.headers.get('authorization')?.trim() !==
141
+ `Bearer ${input.expectedToken.trim()}`
142
+ ) {
143
+ return respond(401, '{"error":"Unauthorized."}');
144
+ }
145
+ if (!input.target || !(await requestRuntimeWorkerWake(input.target))) {
146
+ return respond(
147
+ 503,
148
+ '{"error":"Worker wake unavailable; durable polling remains active."}',
149
+ );
150
+ }
151
+ return respond(202, '{"ok":true}');
152
+ }
@@ -59,9 +59,11 @@ type ValidatedRuntimeTestPolicyOverrides =
59
59
  | { ok: true; overrides: RuntimeTestPolicyOverrides | null }
60
60
  | { ok: false; status: 400 | 403; error: string };
61
61
 
62
- const SUPPORTED_RUNTIME_TEST_FAULTS = new Set<RuntimeTestFaultName>(
63
- runtimeTestFaultDrills().map(({ id }) => id),
64
- );
62
+ // Keep local construction removable when a Play never uses the test seams.
63
+ const SUPPORTED_RUNTIME_TEST_FAULTS = /* @__PURE__ */ (() =>
64
+ new Set<RuntimeTestFaultName>(
65
+ runtimeTestFaultDrills().map(({ id }) => id),
66
+ ))();
65
67
 
66
68
  const RUNTIME_TEST_POLICY_MS_FIELDS = new Set([
67
69
  'receiptLeaseTtlMs',
@@ -14,3 +14,34 @@ const ALWAYS_FRESH_INTEGRATION_TOOL_IDS = new Set([
14
14
  export function isAlwaysFreshIntegrationTool(toolId: string): boolean {
15
15
  return ALWAYS_FRESH_INTEGRATION_TOOL_IDS.has(toolId.trim());
16
16
  }
17
+
18
+ /** Catalog capabilities are added without changing legacy artifact semantics. */
19
+ export function toolReusableResultSupport(input: {
20
+ toolId?: string;
21
+ resultReuse?: 'read' | 'operation';
22
+ retry?: { idempotencyScope?: 'invocation'; requiresExecutionFence?: boolean };
23
+ authoredTags?: string[];
24
+ tags?: string[];
25
+ }): { supported: boolean; unavailableReason: string | null } {
26
+ if (input.toolId && isAlwaysFreshIntegrationTool(input.toolId))
27
+ return {
28
+ supported: false,
29
+ unavailableReason:
30
+ 'Workspace edits and conflict checks must be observed on every invocation.',
31
+ };
32
+ const write =
33
+ input.resultReuse === 'operation' ||
34
+ input.retry?.idempotencyScope === 'invocation' ||
35
+ input.retry?.requiresExecutionFence === true ||
36
+ [...(input.authoredTags ?? []), ...(input.tags ?? [])].some(
37
+ (tag) => tag === 'write' || tag.endsWith('_write') || tag === 'persist',
38
+ );
39
+ return {
40
+ supported: !write && input.resultReuse === 'read',
41
+ unavailableReason: write
42
+ ? 'This action performs an intended operation; completed operations recover within their run, but separate calls must execute independently.'
43
+ : input.resultReuse === 'read'
44
+ ? null
45
+ : 'This action has no declared read-result reuse capability. Use staleAfterSeconds: 0; completed operations still recover within their run.',
46
+ };
47
+ }
@@ -72,6 +72,16 @@ export function transientServiceUnavailableDetail(
72
72
  ) {
73
73
  return detail;
74
74
  }
75
+ // Convex can fail to start a clean isolate before a function begins. Keep
76
+ // the exact structured internal error retryable without
77
+ // treating a matching message alone as a transient failure.
78
+ if (
79
+ /(?=[\s\S]*"code"\s*:\s*"IsolateNotClean")(?=[\s\S]*"message"\s*:\s*"Couldn't start running this function")/i.test(
80
+ detail,
81
+ )
82
+ ) {
83
+ return detail;
84
+ }
75
85
  // Convex uses explicit ServiceUnavailable, ExpiredInQueue and
76
86
  // TooManyConcurrentRequests admission responses, as
77
87
  // well as this generic InternalServerError envelope for short control-plane
@@ -0,0 +1,25 @@
1
+ /** A dispatched HTTP write whose outcome could not be confirmed. */
2
+ export class CtxFetchAmbiguousWriteError extends Error {
3
+ static [Symbol.hasInstance](value: unknown): boolean {
4
+ return (
5
+ value !== null &&
6
+ typeof value === 'object' &&
7
+ (value as { code?: unknown }).code === 'CTX_FETCH_WRITE_OUTCOME_UNKNOWN'
8
+ );
9
+ }
10
+
11
+ readonly code = 'CTX_FETCH_WRITE_OUTCOME_UNKNOWN' as const;
12
+ readonly outcome = 'unknown' as const;
13
+
14
+ constructor(
15
+ readonly method: string,
16
+ readonly origin: string,
17
+ options?: ErrorOptions,
18
+ ) {
19
+ super(
20
+ `ctx.fetch ${method} to ${origin} was dispatched, but its outcome is unknown. Reconcile with the provider or retry only under its supported idempotency contract using the same intended-operation key.`,
21
+ options,
22
+ );
23
+ this.name = 'CtxFetchAmbiguousWriteError';
24
+ }
25
+ }
@@ -38,6 +38,8 @@ export type PlayArtifactCompatibility = {
38
38
  toolResponseReceiptRevision?: string;
39
39
  /** Missing preserves Slack HITL's historical parked timeout behavior. */
40
40
  slackHitlTimeoutOutcome?: 'error';
41
+ /** Missing preserves historical stored-output hydration into dataset rows. */
42
+ datasetRowInput?: 'current_run';
41
43
  };
42
44
 
43
45
  /** The only executable Play artifact contract. */
@@ -53,6 +55,8 @@ export type PlayBundleArtifact = {
53
55
  /** Defaults to cjs_node20 when not present on legacy Node artifacts. */
54
56
  artifactKind?: PlayArtifactKind;
55
57
  entryFile: string;
58
+ /** Source declaration selected by a named-export bundle. Absent preserves legacy admission. */
59
+ exportName?: string;
56
60
  virtualFilename: string;
57
61
  sourceHash: string;
58
62
  graphHash: string;