deepline 0.3.409 → 0.3.411

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 (215) 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/monitors/package.json +1 -1
  21. package/dist/bundling-sources/shared_libs/play-data-plane/r2.ts +3 -1
  22. package/dist/bundling-sources/shared_libs/play-runtime/async-operation.ts +1 -1
  23. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +277 -76
  24. package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +6 -0
  25. package/dist/bundling-sources/shared_libs/play-runtime/dataset-row-input.ts +124 -0
  26. package/dist/bundling-sources/shared_libs/play-runtime/durable-call-cache.ts +6 -62
  27. package/dist/bundling-sources/shared_libs/play-runtime/durable-receipt-execution.ts +1 -0
  28. package/dist/bundling-sources/shared_libs/play-runtime/durable-tool-identity.ts +67 -0
  29. package/dist/bundling-sources/shared_libs/play-runtime/execute-server-timing.ts +64 -6
  30. package/dist/bundling-sources/shared_libs/play-runtime/freshness-receipt-execution.ts +1 -5
  31. package/dist/bundling-sources/shared_libs/play-runtime/ledger-safe-payload.ts +1 -1
  32. package/dist/bundling-sources/shared_libs/play-runtime/play-artifact-runtime-contract.ts +3 -0
  33. package/dist/bundling-sources/shared_libs/play-runtime/play-http-request.ts +10 -0
  34. package/dist/bundling-sources/shared_libs/play-runtime/query-result-dataset.ts +3 -0
  35. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/egress-probe.ts +3 -0
  36. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +26 -16
  37. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-heartbeat.ts +36 -11
  38. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-terminal-push.ts +35 -0
  39. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-launch-transport.ts +21 -0
  40. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +21 -6
  41. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-session-execution.ts +11 -2
  42. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +85 -20
  43. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +71 -13
  44. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-execution-queries.ts +100 -0
  45. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-pool.ts +9 -5
  46. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +337 -146
  47. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-shared.ts +26 -12
  48. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-state.ts +3 -1
  49. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +425 -102
  50. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-dispatch-authority.ts +4 -0
  51. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-outbox-recovery.ts +4 -0
  52. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-event-search.ts +9 -0
  53. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-sandbox-capacity.ts +181 -124
  54. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-worker-wake.ts +152 -0
  55. package/dist/bundling-sources/shared_libs/play-runtime/test-runtime-seams.ts +5 -3
  56. package/dist/bundling-sources/shared_libs/play-runtime/tool-cache-policy.ts +31 -0
  57. package/dist/bundling-sources/shared_libs/play-runtime/transient-service-error.ts +10 -0
  58. package/dist/bundling-sources/shared_libs/plays/ambiguous-write-error.ts +25 -0
  59. package/dist/bundling-sources/shared_libs/plays/artifact-types.ts +4 -0
  60. package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +63 -68
  61. package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +129 -63
  62. package/dist/bundling-sources/shared_libs/plays/contracts.ts +14 -0
  63. package/dist/bundling-sources/shared_libs/plays/enrich-play-compiler.ts +58 -16
  64. package/dist/bundling-sources/shared_libs/plays/fetch-http-error.ts +50 -0
  65. package/dist/bundling-sources/shared_libs/plays/source-authoring-edition.ts +351 -0
  66. package/dist/bundling-sources/shared_libs/plays/tool-list-getter-mode.ts +9 -0
  67. package/dist/bundling-sources/shared_libs/plays/tool-result-types.ts +1 -1
  68. package/dist/cli/auth-M7TBLW55.js +40 -0
  69. package/dist/cli/{auth-5HIIACF7.mjs → auth-YD6ZTMB2.mjs} +6 -6
  70. package/dist/cli/{billing-4275N7FL.js → billing-2L3MOB42.js} +84 -84
  71. package/dist/cli/{billing-EVZ3ZWKC.mjs → billing-ZT46GV4C.mjs} +8 -8
  72. package/dist/cli/{chunk-ZAAKSJAS.js → chunk-27GJKC5Q.js} +17 -10
  73. package/dist/cli/{chunk-UHNDVD3P.js → chunk-2H4EY7KH.js} +100 -41
  74. package/dist/cli/{chunk-GAICVLKL.js → chunk-2NI6DHN7.js} +2 -2
  75. package/dist/cli/{chunk-JGHG4NBA.js → chunk-2WUY3VNK.js} +19 -4
  76. package/dist/cli/{chunk-FHPCKULR.mjs → chunk-4BA6ATHA.mjs} +2 -2
  77. package/dist/cli/{chunk-RTCDW2GU.js → chunk-4O4WNFFC.js} +170 -49
  78. package/dist/cli/{chunk-OSDXE27V.mjs → chunk-4PG4ARL2.mjs} +17 -10
  79. package/dist/cli/{chunk-AU4OFDFX.js → chunk-5IGDNBG7.js} +26 -26
  80. package/dist/cli/{chunk-WYQO2CBS.mjs → chunk-5LRWWMRV.mjs} +149 -28
  81. package/dist/cli/{chunk-PMVFAYL7.mjs → chunk-63ILLN5H.mjs} +2 -2
  82. package/dist/cli/chunk-6AUE6JXY.js +35 -0
  83. package/dist/cli/chunk-7DSMMCXP.js +501 -0
  84. package/dist/cli/{chunk-6BJK4EJ3.mjs → chunk-AARWVK4C.mjs} +2132 -65
  85. package/dist/cli/{chunk-FERRDNMO.js → chunk-AD47BXN2.js} +5 -5
  86. package/dist/cli/{chunk-J6DCKJNM.mjs → chunk-BJV3VSKE.mjs} +16 -1
  87. package/dist/cli/{chunk-S6R3OX7B.js → chunk-E4CABWXL.js} +25 -2
  88. package/dist/cli/{chunk-NXGCO4JB.mjs → chunk-E4IXDAZI.mjs} +5 -0
  89. package/dist/cli/{chunk-BEVLCKXG.mjs → chunk-E4KBNNNB.mjs} +1 -1
  90. package/dist/cli/{chunk-2PRO2JTX.mjs → chunk-E52TKKVI.mjs} +36 -284
  91. package/dist/cli/chunk-FR2ILQ4G.mjs +8 -0
  92. package/dist/cli/{chunk-2GI3QD3L.js → chunk-FRVPOXGB.js} +644 -330
  93. package/dist/cli/chunk-G43JLBDE.mjs +501 -0
  94. package/dist/cli/{chunk-BZAMOCPA.mjs → chunk-IMZ56EOH.mjs} +3 -3
  95. package/dist/cli/chunk-IPGV6VFA.mjs +6084 -0
  96. package/dist/cli/{chunk-IT3XBJQR.mjs → chunk-IR3AKJAY.mjs} +330 -625
  97. package/dist/cli/chunk-J33TSWO5.js +8 -0
  98. package/dist/cli/chunk-JQMZUWNM.mjs +35 -0
  99. package/dist/cli/chunk-JYHSEGRM.js +6084 -0
  100. package/dist/cli/{chunk-JI2B4RZP.mjs → chunk-KCC3FKIW.mjs} +71 -12
  101. package/dist/cli/{chunk-6IKARAYD.mjs → chunk-L2VKAASU.mjs} +23 -0
  102. package/dist/cli/{chunk-O3VSXP2F.js → chunk-LWBM5FFL.js} +50 -50
  103. package/dist/cli/{chunk-VWBTTJYQ.mjs → chunk-M6OASCVL.mjs} +6 -3
  104. package/dist/cli/{chunk-YHPYUZZX.mjs → chunk-MAMVGOSC.mjs} +1 -1
  105. package/dist/cli/{chunk-Q2E4E4FJ.js → chunk-NPBJI7RE.js} +8 -8
  106. package/dist/cli/chunk-NQWIZ5PZ.js +313 -0
  107. package/dist/cli/{chunk-HQXL5526.mjs → chunk-NUX2QHNC.mjs} +1 -1
  108. package/dist/cli/{chunk-TQNHMWUR.mjs → chunk-OGSXENOF.mjs} +5 -2
  109. package/dist/cli/{chunk-SEFMGO3Z.js → chunk-OQQOXSYA.js} +2184 -117
  110. package/dist/cli/{chunk-FJCIUF34.js → chunk-OZSC6CEW.js} +128 -376
  111. package/dist/cli/{chunk-NZSF57UO.mjs → chunk-PO2VU6WV.mjs} +2 -2
  112. package/dist/cli/{chunk-7P5AA3SN.js → chunk-PPDBRJRW.js} +6 -1
  113. package/dist/cli/{chunk-ILHRA7AD.js → chunk-QADCFSGH.js} +7 -7
  114. package/dist/cli/{chunk-N44JHMKY.mjs → chunk-RFES6Q2J.mjs} +452 -138
  115. package/dist/cli/{chunk-DEC3JECC.js → chunk-RMINBDYY.js} +5 -2
  116. package/dist/cli/{chunk-WLOCMJWV.js → chunk-TPFD4F5S.js} +426 -721
  117. package/dist/cli/{chunk-V76R6HMV.js → chunk-U5AQKE3T.js} +1029 -806
  118. package/dist/cli/{chunk-PBZYY7VZ.mjs → chunk-UAEVKAUR.mjs} +734 -511
  119. package/dist/cli/{chunk-WHG43Z67.js → chunk-VGHWJL4W.js} +11 -8
  120. package/dist/cli/chunk-VKKVSFRD.js +62 -0
  121. package/dist/cli/{chunk-GSK6NHVM.js → chunk-WU4U6VSB.js} +5 -5
  122. package/dist/cli/chunk-XAB47ZRV.mjs +313 -0
  123. package/dist/cli/{chunk-JRAVVJ3T.mjs → chunk-XJD6LZCO.mjs} +2 -2
  124. package/dist/cli/chunk-ZCALLYF6.mjs +62 -0
  125. package/dist/cli/{chunk-YD7B53RW.js → chunk-ZHWBU36H.js} +12 -12
  126. package/dist/cli/{csv-A2MP6EFV.mjs → csv-BKNRRW7D.mjs} +5 -5
  127. package/dist/cli/{csv-BBOTCGQY.js → csv-SVDP2EU7.js} +14 -14
  128. package/dist/cli/{db-3W5DMTUX.mjs → db-LU4KLRM6.mjs} +15 -20
  129. package/dist/cli/{db-G4CODDCA.js → db-OQ6S7TTZ.js} +42 -47
  130. package/dist/cli/{describe-PQTQ5B6F.js → describe-45TGETKR.js} +27 -22
  131. package/dist/cli/{describe-XYJ2ZT4M.mjs → describe-U4KOGDRA.mjs} +20 -15
  132. package/dist/cli/describe-core.d.mts +2 -2
  133. package/dist/cli/describe-core.d.ts +2 -2
  134. package/dist/cli/describe-core.js +6 -6
  135. package/dist/cli/describe-core.mjs +5 -5
  136. package/dist/cli/{executions-UR67CA5H.mjs → executions-2GAAUXLA.mjs} +8 -8
  137. package/dist/cli/{executions-TLANVR7Y.js → executions-VZN56VRC.js} +10 -10
  138. package/dist/cli/{feedback-6C2YTRJL.js → feedback-NS76B5GU.js} +13 -9
  139. package/dist/cli/{feedback-WHCMGEIL.mjs → feedback-XW5IWA5R.mjs} +11 -7
  140. package/dist/cli/{git-GWLB6VSA.mjs → git-CLI66XP6.mjs} +6 -6
  141. package/dist/cli/git-VGYRGFEN.js +24 -0
  142. package/dist/cli/index.js +236 -348
  143. package/dist/cli/index.mjs +162 -274
  144. package/dist/cli/{legacy-noops-ZUFA7RWI.js → legacy-noops-HCFB3ZOV.js} +9 -9
  145. package/dist/cli/{legacy-noops-KF4XIFEF.mjs → legacy-noops-ODDOMLBL.mjs} +6 -6
  146. package/dist/cli/{monitors-TB3K2IVE.js → monitors-7WXQ7JFT.js} +94 -94
  147. package/dist/cli/{monitors-6UOIDIMB.mjs → monitors-ZXX456TE.mjs} +8 -8
  148. package/dist/cli/{org-CKWMBPIR.js → org-YLX5PHBQ.js} +68 -68
  149. package/dist/cli/{org-WGHHMVDZ.mjs → org-YUN3TLNP.mjs} +8 -8
  150. package/dist/cli/{play-LYPAXCLD.mjs → play-LL73A7EP.mjs} +533 -98
  151. package/dist/cli/{play-N2B3KAVS.js → play-YGOXL52D.js} +1067 -632
  152. package/dist/cli/{providers-6DI5BVZJ.mjs → providers-35QSAUHL.mjs} +8 -8
  153. package/dist/cli/{providers-UR42MD6Z.js → providers-RHCIU2PX.js} +11 -11
  154. package/dist/cli/{quickstart-MNA2S6PO.mjs → quickstart-6BN3LCZH.mjs} +5 -5
  155. package/dist/cli/{quickstart-FCJG4XGB.js → quickstart-ORMM4NRK.js} +7 -7
  156. package/dist/cli/search-EWDMUPHR.js +30 -0
  157. package/dist/cli/{search-VPPYUJ2M.mjs → search-GAERWLSE.mjs} +9 -9
  158. package/dist/cli/{secrets-QOSMVO36.js → secrets-3AFCTGT7.js} +17 -17
  159. package/dist/cli/{secrets-DRI4W7Z5.mjs → secrets-FCL5PALY.mjs} +8 -8
  160. package/dist/cli/sessions-3OEHEQG7.js +16 -0
  161. package/dist/cli/{sessions-DQNUKP43.mjs → sessions-RSSL7I7O.mjs} +6 -6
  162. package/dist/cli/{settings-DWDK3Y2R.mjs → settings-GE4NRE7Y.mjs} +12 -12
  163. package/dist/cli/{settings-JUGGCOPS.js → settings-RX5K4I7H.js} +79 -79
  164. package/dist/cli/{setup-AWBHNYXK.js → setup-N3ZNAEL5.js} +45 -44
  165. package/dist/cli/{setup-D5DWVNY4.mjs → setup-X3RXGG4F.mjs} +11 -10
  166. package/dist/cli/{skills-YV2XR35J.mjs → skills-5JEAJBDE.mjs} +7 -7
  167. package/dist/cli/skills-VAAQVEQC.js +29 -0
  168. package/dist/cli/tools-BYYOT2DU.js +44 -0
  169. package/dist/cli/{tools-VFJYHNV2.mjs → tools-JOKUWEBZ.mjs} +19 -14
  170. package/dist/cli/update-36G4ZD7B.js +31 -0
  171. package/dist/cli/{update-IM6VOTGL.mjs → update-UBHEVFR3.mjs} +11 -9
  172. package/dist/cli/{workflow-26URBWPV.mjs → workflow-FR6EXAXF.mjs} +44 -20
  173. package/dist/cli/{workflow-3BS6U6SL.js → workflow-SZAVOHN6.js} +54 -30
  174. package/dist/{client-BfInrZPB.d.mts → client-74k4IPh6.d.mts} +135 -54
  175. package/dist/{client-BMjJFpan.d.ts → client-C1IK1f8c.d.ts} +135 -54
  176. package/dist/{compiler-manifest-Ycx-hoAr.d.mts → compiler-manifest-lzWpbV3w.d.mts} +83 -66
  177. package/dist/{compiler-manifest-Ycx-hoAr.d.ts → compiler-manifest-lzWpbV3w.d.ts} +83 -66
  178. package/dist/{index-CZXgiN3E.d.mts → index-C8Cy-YTm.d.ts} +5 -1
  179. package/dist/{index-NPG5pqxQ.d.ts → index-CtNK-0nI.d.mts} +5 -1
  180. package/dist/index.d.mts +47 -9
  181. package/dist/index.d.ts +47 -9
  182. package/dist/index.js +17 -14
  183. package/dist/index.mjs +13 -10
  184. package/dist/install-integrity.json +118 -102
  185. package/dist/plays/bundle-play-file.d.mts +3 -3
  186. package/dist/plays/bundle-play-file.d.ts +3 -3
  187. package/dist/plays/bundle-play-file.mjs +5 -4
  188. package/dist/plays/csv-data-input.js +3 -3
  189. package/dist/plays/csv-data-input.mjs +2 -2
  190. package/dist/plays/play-description.d.mts +2 -2
  191. package/dist/plays/play-description.d.ts +2 -2
  192. package/dist/plays/run-request.d.mts +2 -2
  193. package/dist/plays/run-request.d.ts +2 -2
  194. package/dist/plays/run-request.js +11 -3
  195. package/dist/plays/run-request.mjs +10 -2
  196. package/dist/release.d.mts +15 -6
  197. package/dist/release.d.ts +15 -6
  198. package/dist/release.js +2 -2
  199. package/dist/release.mjs +1 -1
  200. package/package.json +2 -2
  201. package/dist/cli/auth-F6BX32XZ.js +0 -40
  202. package/dist/cli/chunk-264PC2RN.mjs +0 -83
  203. package/dist/cli/chunk-44ZTYHMQ.mjs +0 -155
  204. package/dist/cli/chunk-53HN7WZN.js +0 -392
  205. package/dist/cli/chunk-7AALNGTA.mjs +0 -392
  206. package/dist/cli/chunk-ALD4COBH.js +0 -155
  207. package/dist/cli/chunk-CLJGSOVM.mjs +0 -1716
  208. package/dist/cli/chunk-WNUR6VP5.js +0 -83
  209. package/dist/cli/chunk-Z4VNJOF7.js +0 -1716
  210. package/dist/cli/git-VQFF6D7C.js +0 -24
  211. package/dist/cli/search-NCEEONMQ.js +0 -30
  212. package/dist/cli/sessions-3WYKQABW.js +0 -16
  213. package/dist/cli/skills-AKPAJVSO.js +0 -29
  214. package/dist/cli/tools-RBKEK6PM.js +0 -39
  215. package/dist/cli/update-VFNUBLDA.js +0 -29
@@ -3,8 +3,10 @@ import {
3
3
  RAW_V2_TOOL_RESPONSE_CONTRACT,
4
4
  legacyRawFromToolResponseRawV2,
5
5
  providerMetaFromToolResponseRawV2
6
- } from "./chunk-6IKARAYD.mjs";
6
+ } from "./chunk-L2VKAASU.mjs";
7
7
  import {
8
+ CLI_PLAY_RUN_START_STREAM_PATH,
9
+ CliPlayAdmissionUnsupportedError,
8
10
  PLAY_RUN_START_STREAM_PATH,
9
11
  decodePlayRunPublicStatus,
10
12
  isPlayRunPackage,
@@ -13,27 +15,29 @@ import {
13
15
  preparePlayRunAdmission,
14
16
  stagePlayFiles,
15
17
  startPlayRun
16
- } from "./chunk-JI2B4RZP.mjs";
18
+ } from "./chunk-KCC3FKIW.mjs";
17
19
  import {
18
20
  describePlayDetail,
19
21
  describePlayListItem
20
22
  } from "./chunk-EIMG75BT.mjs";
21
23
  import {
24
+ ATOMIC_TOOL_EXECUTE_SUPPORTED_HEADER,
22
25
  COORDINATOR_INTERNAL_TOKEN_HEADER,
23
26
  HttpClient,
24
27
  RETAINED_PIPELINE_RECEIVERS_HEADER,
25
28
  RUNTIME_ENVIRONMENT_TOKEN_HEADER,
29
+ SDK_API_MAJOR,
26
30
  resolveConfig
27
- } from "./chunk-2PRO2JTX.mjs";
31
+ } from "./chunk-E52TKKVI.mjs";
28
32
  import {
29
33
  CustomerDbQueryOutcomeUnknownError
30
- } from "./chunk-YHPYUZZX.mjs";
34
+ } from "./chunk-MAMVGOSC.mjs";
31
35
  import {
32
36
  DeeplineError,
33
37
  TOOL_EXECUTION_ERROR_SCHEMA_HEADER,
34
38
  TOOL_EXECUTION_ERROR_SCHEMA_VERSION,
35
39
  ToolExecutionError
36
- } from "./chunk-NXGCO4JB.mjs";
40
+ } from "./chunk-E4IXDAZI.mjs";
37
41
 
38
42
  // src/pagination.ts
39
43
  async function* iterateCursorPages(fetchPage) {
@@ -77,7 +81,7 @@ function requireBillingIdempotencyKey(value) {
77
81
  }
78
82
 
79
83
  // src/db/query.ts
80
- function planCustomerDbQuery(input) {
84
+ function planCustomerDbQuery(input, maxRetries = 3) {
81
85
  if (typeof input.sql !== "string" || !input.sql.trim())
82
86
  throw new DeeplineError("sql must not be empty.", 400, "INVALID_SQL");
83
87
  if (input.operationKey !== void 0 && typeof input.operationKey !== "string")
@@ -118,13 +122,13 @@ function planCustomerDbQuery(input) {
118
122
  recovery: operationKey === void 0 ? "unkeyed_unknown_outcome" : "original_operation_key"
119
123
  },
120
124
  transport: {
121
- maxRetries: 0,
122
- urlFailover: "disabled"
125
+ maxRetries: operationKey === void 0 ? 0 : maxRetries,
126
+ urlFailover: operationKey === void 0 ? "disabled" : "loopback_only"
123
127
  }
124
128
  };
125
129
  }
126
- async function queryCustomerDatabase(http, input) {
127
- const plan = planCustomerDbQuery(input);
130
+ async function queryCustomerDatabase(http, input, maxRetries) {
131
+ const plan = planCustomerDbQuery(input, maxRetries);
128
132
  let result;
129
133
  try {
130
134
  result = await http.post(
@@ -189,6 +193,156 @@ function resolveLookupClientTimeoutMs(toolId) {
189
193
  return lookupClientTimeouts.get(toolId.trim().toLowerCase());
190
194
  }
191
195
 
196
+ // ../play-runtime/execute-server-timing.ts
197
+ var EXECUTE_SERVER_TIMING_STAGES = [
198
+ "auth",
199
+ "rate_limit",
200
+ "catalog",
201
+ // Local preparation accumulates compilation/hooks/pricing inside catalog
202
+ // and executor loading before the final provider permit. Do not add it to
203
+ // catalog or total. Credential selection excludes charge admission.
204
+ "credential_plan",
205
+ "provider_prepare",
206
+ "charge_open",
207
+ // Charge admission substages. `charge_open` keeps its original meaning — the
208
+ // TOTAL of all charge-admission work — so an existing reader is unchanged and
209
+ // these four explain work inside it. The authorization action adapter splits
210
+ // its wall into snapshot, remaining action time and application retry gaps.
211
+ // Absent stages are unobserved, not confident zero durations.
212
+ // - `charge_eligibility` canonical wallet and cap authorization snapshot
213
+ // - `charge_cap_preflight` legacy standalone cap work (zero in current path)
214
+ // - `charge_open_mutation` remaining action wall, including transport and
215
+ // failed attempts; legacy name, not mutation CPU
216
+ // - `charge_retry_wait` elapsed gaps before application RPC retries;
217
+ // native Convex logs attribute internal OCC retries
218
+ "charge_eligibility",
219
+ "charge_cap_preflight",
220
+ "charge_open_mutation",
221
+ "charge_retry_wait",
222
+ // Billing completion slices explain the full stage, including transport.
223
+ // Handoff headers/body are nested within billing_handoff, not additive.
224
+ "billing_settle",
225
+ "billing_price",
226
+ "billing_precision",
227
+ "billing_caps",
228
+ "billing_catalog",
229
+ "billing_witness",
230
+ "billing_handoff",
231
+ "billing_handoff_rpc",
232
+ "billing_handoff_headers",
233
+ "billing_handoff_after_headers",
234
+ // Process-wide activity over the RPC interval, including concurrent work.
235
+ // These overlap the wall clocks above; they are not request-attributed CPU.
236
+ "billing_handoff_process_cpu",
237
+ "billing_handoff_event_loop_active",
238
+ "billing_handoff_event_loop_idle",
239
+ "fixture_load",
240
+ "execute",
241
+ "identity",
242
+ "usage_schedule",
243
+ // Managed AI inference slices: `ai_prepare` includes request preparation and
244
+ // contains `ai_billing_authorize`; `ai_provider_work_start` is the durable
245
+ // pre-call transition; `ai_gateway_first_output` is the time to the first
246
+ // model-output part (excluding SDK control chunks); and
247
+ // `ai_gateway_stream_wall` is the end-to-end SDK stream
248
+ // collection wall. Stream wall includes awaited finish callbacks, so evidence
249
+ // persistence and settlement overlap it. First-output and stream wall both
250
+ // include Gateway/network/upstream time; neither claims provider-internal
251
+ // model time.
252
+ "ai_prepare",
253
+ "ai_billing_authorize",
254
+ "ai_provider_work_start",
255
+ "ai_gateway_first_output",
256
+ "ai_gateway_stream_wall",
257
+ "ai_generation_evidence_persist",
258
+ "ai_billing_settlement",
259
+ "ai_output_canonicalization",
260
+ "ai_result_persist",
261
+ // Provider HTTP slices are only nonzero for calls that take this path:
262
+ // admission; `provider_fetch_headers` until response headers arrive (or
263
+ // `provider_fetch_error_wait` for a rejected fetch); retry backoff; body
264
+ // read, JSON parse, result transform and output normalization are separate.
265
+ "provider_admission",
266
+ "provider_fetch_headers",
267
+ "provider_fetch_error_wait",
268
+ "provider_retry_wait",
269
+ "provider_body_read",
270
+ "provider_json_parse",
271
+ "provider_result_transform",
272
+ "provider_output_normalization",
273
+ "total",
274
+ // The execution gateway preserves these disjoint stages beside the app
275
+ // handler's stages. They are deliberately part of this small, additive
276
+ // response diagnostic contract: a long sandbox-to-headers wall is otherwise
277
+ // incorrectly reduced to "network" after the app handler reports quickly.
278
+ "auth_launch",
279
+ "auth_verify",
280
+ "body_parse",
281
+ "dispatch_pool",
282
+ "dispatch_callback",
283
+ // The execution relay measures its own upstream fetch. `upstream_headers`
284
+ // contains the app handler's `total`; subtracting those two clocks leaves
285
+ // only the Vercel/platform-or-transit boundary, never a made-up provider
286
+ // latency. `upstream_body` follows headers while the relay buffers the
287
+ // bounded response before it returns to the runner.
288
+ "upstream_headers",
289
+ "upstream_body"
290
+ ];
291
+ var PUBLIC_EXECUTE_SERVER_TIMING_STAGES = [
292
+ // Route total includes lazy execution-module loading. The handler's
293
+ // public_total keeps its existing meaning so older readers remain valid.
294
+ "public_load",
295
+ "public_route",
296
+ // Core and recovery initialization overlap authentication. Their wall is
297
+ // included in public_total; these stages are not additive.
298
+ "public_core_load",
299
+ "public_recovery_load",
300
+ "public_auth",
301
+ "public_claim",
302
+ "public_claim_headers",
303
+ "public_claim_after_headers",
304
+ "public_core",
305
+ "public_prepare",
306
+ "public_r2",
307
+ "public_complete",
308
+ "public_complete_headers",
309
+ "public_complete_after_headers",
310
+ "public_total"
311
+ ];
312
+ var ALL_EXECUTE_SERVER_TIMING_STAGES = /* @__PURE__ */ (() => [
313
+ ...EXECUTE_SERVER_TIMING_STAGES,
314
+ ...PUBLIC_EXECUTE_SERVER_TIMING_STAGES
315
+ ])();
316
+ function parseExecuteServerTiming(header) {
317
+ if (typeof header !== "string" || !header.trim()) return null;
318
+ const durations = /* @__PURE__ */ new Map();
319
+ for (const rawEntry of header.split(",")) {
320
+ const parts = rawEntry.split(";");
321
+ const name = parts[0]?.trim().toLowerCase();
322
+ if (!name) continue;
323
+ for (const parameter of parts.slice(1)) {
324
+ const separator = parameter.indexOf("=");
325
+ if (separator < 0) continue;
326
+ const key = parameter.slice(0, separator).trim().toLowerCase();
327
+ if (key !== "dur") continue;
328
+ const value = parameter.slice(separator + 1).trim().replace(/^"|"$/g, "");
329
+ const parsed = value === "" ? Number.NaN : Number(value);
330
+ if (Number.isFinite(parsed) && parsed >= 0) {
331
+ durations.set(name, parsed);
332
+ }
333
+ }
334
+ }
335
+ const timing = Object.fromEntries(
336
+ ALL_EXECUTE_SERVER_TIMING_STAGES.map((stage) => [
337
+ stage,
338
+ durations.get(stage) ?? null
339
+ ])
340
+ );
341
+ return ALL_EXECUTE_SERVER_TIMING_STAGES.some(
342
+ (stage) => timing[stage] !== null
343
+ ) ? timing : null;
344
+ }
345
+
192
346
  // ../integration-contracts/theirstack/execution-policy.ts
193
347
  var THEIRSTACK_SLOW_JOB_SEARCH_PROVIDER_TIMEOUT_MS = 135e3;
194
348
  var THEIRSTACK_SLOW_JOB_SEARCH_CLIENT_TIMEOUT_MS = 6e4 + THEIRSTACK_SLOW_JOB_SEARCH_PROVIDER_TIMEOUT_MS + 15e3;
@@ -430,13 +584,6 @@ var ToolCalls = class {
430
584
  inputSnapshot,
431
585
  this.configuredRequestTimeoutMs
432
586
  );
433
- if (idempotencyKey !== void 0) {
434
- await this.lookupExecutionByKey(
435
- idempotencyKey,
436
- timeoutMs,
437
- options?.cliInvocationId
438
- );
439
- }
440
587
  const isApifySync = toolId.trim().toLowerCase() === "apify_run_actor_sync";
441
588
  const headers = {
442
589
  [EXECUTE_RESPONSE_CONTRACT_HEADER]: RAW_V2_EXECUTE_RESPONSE_CONTRACT,
@@ -463,38 +610,91 @@ var ToolCalls = class {
463
610
  if (recovery === "running" || recovery === "outcome_unknown") return;
464
611
  const state = isRecord(body) ? body.status : null;
465
612
  const terminal = state === "completed" || state === "failed" || state === "cancelled" ? state : state === "canceled" ? "cancelled" : failure ? "failed" : null;
466
- if (terminal)
467
- options?.onResponseReceived?.({
613
+ if (!terminal || !options?.onResponseReceived) return;
614
+ const reportObservationFailure = () => console.error("[tools.execute] response observation failed");
615
+ try {
616
+ const notification = options.onResponseReceived({
468
617
  receivedAtMs: lastResponse.receivedAtMs,
469
618
  httpStatus: lastResponse.httpStatus,
470
619
  state: terminal
471
620
  });
621
+ void Promise.resolve(notification).catch(reportObservationFailure);
622
+ } catch {
623
+ reportObservationFailure();
624
+ }
472
625
  };
473
- const request = async (requestTimeoutMs) => {
474
- lastResponse = null;
475
- try {
476
- return await this.http.post(
477
- `/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
478
- {
479
- payload: inputSnapshot,
480
- ...metadataSnapshot ? { metadata: metadataSnapshot } : {}
481
- },
482
- headers,
483
- {
484
- timeout: requestTimeoutMs,
485
- maxRetries: idempotencyKey ? 0 : options?.maxRetries ?? 0,
486
- retryApiErrors: options?.retryApiErrors === true,
487
- exactUrlOnly: true,
488
- toolId,
489
- idempotencyKey,
490
- onResponse: () => {
491
- lastResponse = null;
492
- },
493
- onResponseBody: (observation) => {
494
- lastResponse = observation;
626
+ const legacyEndpoint = `/api/v2/integrations/${encodeURIComponent(toolId)}/execute`;
627
+ let useLegacyEndpoint = idempotencyKey === void 0;
628
+ const atomicToolOnly = options?.atomicToolOnly === true;
629
+ const dispatchRequest = async (requestTimeoutMs) => {
630
+ let recoverySupported = false;
631
+ let atomicToolsSupported = false;
632
+ const post = () => this.http.post(
633
+ atomicToolOnly ? `/api/v2/tools/${encodeURIComponent(toolId)}/execute` : useLegacyEndpoint ? legacyEndpoint : `/api/v2/executions/by-key/${encodeURIComponent(idempotencyKey)}?tool_id=${encodeURIComponent(toolId)}`,
634
+ {
635
+ payload: inputSnapshot,
636
+ ...metadataSnapshot ? { metadata: metadataSnapshot } : {}
637
+ },
638
+ headers,
639
+ {
640
+ timeout: requestTimeoutMs,
641
+ maxRetries: idempotencyKey ? 0 : options?.maxRetries ?? 0,
642
+ ...atomicToolOnly && !idempotencyKey && options?.maxRetries === void 0 ? { executePolicyMaxRetries: 2 } : {},
643
+ retryApiErrors: atomicToolOnly || options?.retryApiErrors === true,
644
+ retryFromExecutePolicy: atomicToolOnly,
645
+ exactUrlOnly: true,
646
+ toolId,
647
+ idempotencyKey,
648
+ onResponse: (response2) => {
649
+ lastResponse = null;
650
+ atomicToolsSupported = response2.headers.get(ATOMIC_TOOL_EXECUTE_SUPPORTED_HEADER) === "true";
651
+ recoverySupported = response2.headers.get("X-Deepline-Idempotency-Supported") === "true";
652
+ if (options?.onServerTiming) {
653
+ try {
654
+ const notification = options.onServerTiming(
655
+ parseExecuteServerTiming(
656
+ response2.headers.get("server-timing")
657
+ )
658
+ );
659
+ void Promise.resolve(notification).catch(() => void 0);
660
+ } catch {
661
+ }
495
662
  }
663
+ },
664
+ onResponseBody: (observation) => {
665
+ lastResponse = observation;
666
+ }
667
+ }
668
+ );
669
+ try {
670
+ return await post();
671
+ } catch (error) {
672
+ if (atomicToolOnly) {
673
+ if (!atomicToolsSupported && !recoverySupported && !(error instanceof ToolExecutionError) && error instanceof DeeplineError && (error.statusCode === 404 || error.statusCode === 405)) {
674
+ throw new DeeplineError(
675
+ "This server does not support atomic tool execution.",
676
+ error.statusCode,
677
+ "ATOMIC_TOOL_EXECUTE_UNSUPPORTED"
678
+ );
496
679
  }
680
+ throw error;
681
+ }
682
+ if (useLegacyEndpoint || recoverySupported || !(error instanceof DeeplineError) || error.statusCode !== 404 && error.statusCode !== 405)
683
+ throw error;
684
+ lastResponse = null;
685
+ await this.lookupExecutionByKey(
686
+ idempotencyKey,
687
+ requestTimeoutMs,
688
+ options?.cliInvocationId
497
689
  );
690
+ useLegacyEndpoint = true;
691
+ return post();
692
+ }
693
+ };
694
+ const request = async (requestTimeoutMs) => {
695
+ lastResponse = null;
696
+ try {
697
+ return await dispatchRequest(requestTimeoutMs);
498
698
  } catch (error) {
499
699
  observeTerminal({ error });
500
700
  throw error;
@@ -639,6 +839,309 @@ var ToolCalls = class {
639
839
  }
640
840
  };
641
841
 
842
+ // ../play-runtime/runtime-environment.ts
843
+ var PLAY_RUNTIME_ENVIRONMENTS = ["preview"];
844
+ var PLAY_RUNTIME_NAMESPACE_PATTERN = /^[a-z][a-z0-9-]{0,30}$/;
845
+ var PLAY_RUNTIME_SANDBOX_PLACEMENT_POLICY_PATTERN = /^[a-z][a-z0-9_]*@[1-9][0-9]*$/;
846
+ function normalizePlayRuntimeNamespace(value) {
847
+ if (typeof value !== "string") return null;
848
+ const normalized = value.trim();
849
+ return PLAY_RUNTIME_NAMESPACE_PATTERN.test(normalized) ? normalized : null;
850
+ }
851
+ function normalizePlayRuntimeSandboxPlacementPolicy(value) {
852
+ if (typeof value !== "string") return null;
853
+ const normalized = value.trim();
854
+ return PLAY_RUNTIME_SANDBOX_PLACEMENT_POLICY_PATTERN.test(normalized) ? normalized : null;
855
+ }
856
+ function normalizePlayRuntimeSelection(value) {
857
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
858
+ const record = value;
859
+ if (Object.keys(record).some(
860
+ (key) => key !== "environment" && key !== "namespace" && key !== "backend" && key !== "sandboxPlacementPolicy"
861
+ ) || record.environment !== "preview") {
862
+ return null;
863
+ }
864
+ const namespace = normalizePlayRuntimeNamespace(record.namespace);
865
+ if (!namespace) return null;
866
+ const sandboxPlacementPolicy = record.sandboxPlacementPolicy === void 0 ? void 0 : normalizePlayRuntimeSandboxPlacementPolicy(
867
+ record.sandboxPlacementPolicy
868
+ );
869
+ if (record.sandboxPlacementPolicy !== void 0 && sandboxPlacementPolicy === null) {
870
+ return null;
871
+ }
872
+ if (record.backend !== void 0 && record.backend !== PLAY_RUNTIME_BACKENDS.daytona && record.backend !== PLAY_RUNTIME_BACKENDS.modal) {
873
+ return null;
874
+ }
875
+ return {
876
+ environment: "preview",
877
+ namespace,
878
+ ...record.backend === void 0 ? {} : { backend: record.backend },
879
+ ...sandboxPlacementPolicy ? { sandboxPlacementPolicy } : {}
880
+ };
881
+ }
882
+ function normalizePlayRuntimeEnvironment(value) {
883
+ return typeof value === "string" && PLAY_RUNTIME_ENVIRONMENTS.includes(value) ? value : null;
884
+ }
885
+
886
+ // src/plays/client.ts
887
+ function normalizePlayRunIntegrationMode(value) {
888
+ if (value === "live" || value === "eval_stub" || value === "fixture") {
889
+ return value;
890
+ }
891
+ return void 0;
892
+ }
893
+ function resolvePlayRunIntegrationMode(request, env) {
894
+ return normalizePlayRunIntegrationMode(
895
+ request.integrationMode ?? env.DEEPLINE_EVAL_INTEGRATION_MODE
896
+ );
897
+ }
898
+ function resolvePlayRunRuntimeSelection(request, env) {
899
+ if (request.runtime !== void 0) {
900
+ const runtime = normalizePlayRuntimeSelection(request.runtime);
901
+ if (!runtime) {
902
+ throw new DeeplineError(
903
+ 'runtime must be { environment: "preview", namespace, backend?: "daytona" | "modal", sandboxPlacementPolicy?: "policy_name@1" } with namespace matching ^[a-z][a-z0-9-]{0,30}$.',
904
+ void 0,
905
+ "INVALID_RUNTIME_SELECTION"
906
+ );
907
+ }
908
+ return runtime;
909
+ }
910
+ const configured = env.DEEPLINE_RUNTIME_ENVIRONMENT?.trim();
911
+ const configuredNamespace = env.DEEPLINE_RUNTIME_NAMESPACE?.trim();
912
+ const configuredToken = env.DEEPLINE_RUNTIME_ENVIRONMENT_TOKEN?.trim();
913
+ const configuredPlacementPolicy = env.DEEPLINE_RUNTIME_SANDBOX_PLACEMENT_POLICY?.trim();
914
+ if (!configured) {
915
+ if (configuredNamespace || configuredToken || configuredPlacementPolicy) {
916
+ const present = [];
917
+ if (configuredNamespace) present.push("DEEPLINE_RUNTIME_NAMESPACE");
918
+ if (configuredToken) present.push("DEEPLINE_RUNTIME_ENVIRONMENT_TOKEN");
919
+ if (configuredPlacementPolicy) {
920
+ present.push("DEEPLINE_RUNTIME_SANDBOX_PLACEMENT_POLICY");
921
+ }
922
+ const isPlural = present.length > 1;
923
+ throw new DeeplineError(
924
+ [
925
+ `Incomplete play runtime selection: ${present.join(" and ")} ${isPlural ? "are" : "is"} set but DEEPLINE_RUNTIME_ENVIRONMENT is not.`,
926
+ "Preview routing requires DEEPLINE_RUNTIME_ENVIRONMENT=preview together with DEEPLINE_RUNTIME_NAMESPACE; the token alone authorizes nothing.",
927
+ `If you did not export ${isPlural ? "these" : "this"} yourself, ${isPlural ? "they were" : "it was"} most likely auto-loaded from a .env or .env.local in the current directory (bun loads those automatically, and in a git worktree .env.local is usually a symlink to the main checkout).`,
928
+ `To run against the app-native runtime, clear ${isPlural ? "them" : "it"} for this command: ${present.map((name) => `${name}=`).join(" ")} <your deepline command>`
929
+ ].join("\n"),
930
+ void 0,
931
+ "INVALID_RUNTIME_ENVIRONMENT"
932
+ );
933
+ }
934
+ return void 0;
935
+ }
936
+ const environment = normalizePlayRuntimeEnvironment(configured);
937
+ if (!environment) {
938
+ throw new DeeplineError(
939
+ `DEEPLINE_RUNTIME_ENVIRONMENT supports only explicit preview selection. Received "${configured}". Omit it to use the app-native runtime.`,
940
+ void 0,
941
+ "INVALID_RUNTIME_ENVIRONMENT"
942
+ );
943
+ }
944
+ const namespace = normalizePlayRuntimeNamespace(configuredNamespace);
945
+ if (!namespace) {
946
+ throw new DeeplineError(
947
+ "DEEPLINE_RUNTIME_NAMESPACE is required for preview and must match ^[a-z][a-z0-9-]{0,30}$.",
948
+ void 0,
949
+ "INVALID_RUNTIME_NAMESPACE"
950
+ );
951
+ }
952
+ const configuredBackendValue = env.DEEPLINE_PLAY_RUNNER_BACKEND?.trim();
953
+ const configuredBackend = configuredBackendValue === "daytona" || configuredBackendValue === "modal" ? configuredBackendValue : void 0;
954
+ if (configuredBackendValue && !configuredBackend) {
955
+ throw new DeeplineError(
956
+ `DEEPLINE_PLAY_RUNNER_BACKEND must be daytona or modal for preview runtime selection. Received "${configuredBackendValue}".`,
957
+ void 0,
958
+ "INVALID_RUNTIME_BACKEND"
959
+ );
960
+ }
961
+ if (configuredPlacementPolicy && !normalizePlayRuntimeSandboxPlacementPolicy(configuredPlacementPolicy)) {
962
+ throw new DeeplineError(
963
+ `DEEPLINE_RUNTIME_SANDBOX_PLACEMENT_POLICY must be a versioned placement policy id such as daytona_only@1. Received "${configuredPlacementPolicy}".`,
964
+ void 0,
965
+ "INVALID_RUNTIME_SANDBOX_PLACEMENT_POLICY"
966
+ );
967
+ }
968
+ return {
969
+ environment,
970
+ namespace,
971
+ ...configuredBackend ? { backend: configuredBackend } : {},
972
+ ...configuredPlacementPolicy ? { sandboxPlacementPolicy: configuredPlacementPolicy } : {}
973
+ };
974
+ }
975
+ function resolveStartPlayRunRequest(request, env) {
976
+ const integrationMode = resolvePlayRunIntegrationMode(request, env);
977
+ const testPolicyOverrides = request.testPolicyOverrides ?? parseEnvTestPolicyOverrides(env);
978
+ const runtime = resolvePlayRunRuntimeSelection(request, env);
979
+ const resolved = { ...request };
980
+ delete resolved.integrationMode;
981
+ delete resolved.runtime;
982
+ delete resolved.testPolicyOverrides;
983
+ if (integrationMode) resolved.integrationMode = integrationMode;
984
+ if (runtime) resolved.runtime = runtime;
985
+ if (testPolicyOverrides) resolved.testPolicyOverrides = testPolicyOverrides;
986
+ return { request: resolved, runtime };
987
+ }
988
+ function runtimeSelectionHeaders(runtime, env) {
989
+ if (!runtime) return void 0;
990
+ const token = env.DEEPLINE_RUNTIME_ENVIRONMENT_TOKEN?.trim();
991
+ if (!token) {
992
+ throw new DeeplineError(
993
+ "DEEPLINE_RUNTIME_ENVIRONMENT_TOKEN is required for explicit preview runtime selection.",
994
+ void 0,
995
+ "RUNTIME_ENVIRONMENT_TOKEN_REQUIRED"
996
+ );
997
+ }
998
+ const receivers = env.DEEPLINE_RUNTIME_RETAINED_PIPELINE_RECEIVERS?.trim();
999
+ return {
1000
+ [RUNTIME_ENVIRONMENT_TOKEN_HEADER]: token,
1001
+ ...receivers ? { [RETAINED_PIPELINE_RECEIVERS_HEADER]: receivers } : {}
1002
+ };
1003
+ }
1004
+ function normalizeTestPolicyOverrides(value, source) {
1005
+ if (value && typeof value === "object" && !Array.isArray(value)) {
1006
+ return value;
1007
+ }
1008
+ throw new DeeplineError(
1009
+ `${source} must be a JSON object.`,
1010
+ void 0,
1011
+ "INVALID_TEST_POLICY_OVERRIDES"
1012
+ );
1013
+ }
1014
+ function parseEnvTestPolicyOverrides(env) {
1015
+ const raw = env.DEEPLINE_TEST_POLICY_OVERRIDES;
1016
+ const trimmed = raw?.trim();
1017
+ if (!trimmed) return void 0;
1018
+ let parsed;
1019
+ try {
1020
+ parsed = JSON.parse(trimmed);
1021
+ } catch (error) {
1022
+ const detail = error instanceof Error ? ` ${error.message}` : "";
1023
+ throw new DeeplineError(
1024
+ `DEEPLINE_TEST_POLICY_OVERRIDES must be valid JSON.${detail}`,
1025
+ void 0,
1026
+ "INVALID_TEST_POLICY_OVERRIDES"
1027
+ );
1028
+ }
1029
+ return normalizeTestPolicyOverrides(parsed, "DEEPLINE_TEST_POLICY_OVERRIDES");
1030
+ }
1031
+ function isRecord2(value) {
1032
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
1033
+ }
1034
+ function normalizePlayStatus(raw) {
1035
+ const runPackage = isPlayRunPackage(raw) ? raw : isPlayRunPackage(raw.package) ? raw.package : null;
1036
+ const packageRun = runPackage?.run;
1037
+ const rawStatus = typeof raw.status === "string" ? raw.status : typeof packageRun?.status === "string" ? packageRun.status : null;
1038
+ const status = decodePlayRunPublicStatus(rawStatus);
1039
+ if (!status) {
1040
+ throw new Error(
1041
+ `Invalid play run lifecycle status in API response: ${JSON.stringify(rawStatus)}.`
1042
+ );
1043
+ }
1044
+ const runId = typeof raw.runId === "string" ? raw.runId : typeof raw.workflowId === "string" ? raw.workflowId : packageRun?.id ?? "";
1045
+ const legacyOverview = runPackage?.overview ?? raw.overview;
1046
+ const runStatus = runPackage?.runStatus ?? raw.runStatus;
1047
+ return {
1048
+ ...raw,
1049
+ runId,
1050
+ ...isRecord2(legacyOverview) ? {
1051
+ overview: legacyOverview
1052
+ } : {},
1053
+ ...isRecord2(runStatus) ? { runStatus } : {},
1054
+ ...runPackage ? {
1055
+ package: runPackage,
1056
+ outputs: runPackage.outputs
1057
+ } : {},
1058
+ status
1059
+ };
1060
+ }
1061
+ var PlayClient = class {
1062
+ constructor(http, environment = process.env) {
1063
+ this.http = http;
1064
+ this.environment = environment;
1065
+ }
1066
+ async startPlayRun(request, options) {
1067
+ const resolved = resolveStartPlayRunRequest(request, this.environment);
1068
+ const headers = runtimeSelectionHeaders(resolved.runtime, this.environment);
1069
+ return startPlayRun(
1070
+ this.http,
1071
+ resolved.request,
1072
+ options?.cliAdmission ? { ...headers, "X-Deepline-API-Major": String(SDK_API_MAJOR) } : headers,
1073
+ options
1074
+ );
1075
+ }
1076
+ async getPlayStatus(workflowId, options) {
1077
+ const params = new URLSearchParams();
1078
+ if (options?.billing === false) {
1079
+ params.set("billing", "false");
1080
+ }
1081
+ if (options?.full === true) {
1082
+ params.set("full", "true");
1083
+ }
1084
+ const query = params.size > 0 ? `?${params.toString()}` : "";
1085
+ const response = await this.http.get(
1086
+ `/api/v2/runs/${encodeURIComponent(workflowId)}${query}`
1087
+ );
1088
+ return normalizePlayStatus(response);
1089
+ }
1090
+ async cancelPlay(workflowId) {
1091
+ await this.http.request(
1092
+ `/api/v2/runs/${encodeURIComponent(workflowId)}/stop`,
1093
+ { method: "POST" }
1094
+ );
1095
+ }
1096
+ async stopPlay(workflowId, options) {
1097
+ return this.http.post(
1098
+ `/api/v2/runs/${encodeURIComponent(workflowId)}/stop`,
1099
+ options?.reason ? { reason: options.reason } : {}
1100
+ );
1101
+ }
1102
+ async listPlayRuns(playName) {
1103
+ const encodedName = encodeURIComponent(playName);
1104
+ const response = await this.http.get(
1105
+ `/api/v2/plays/${encodedName}/runs`
1106
+ );
1107
+ return response.runs ?? [];
1108
+ }
1109
+ async getPlay(name, options) {
1110
+ const encodedName = encodeURIComponent(name);
1111
+ const query = options?.source ? `?include=source&revision=${encodeURIComponent(options.source)}` : options?.guidance ? "?include=guidance" : "";
1112
+ return this.http.get(`/api/v2/plays/${encodedName}${query}`);
1113
+ }
1114
+ async clearPlayHistory(name, request = {}) {
1115
+ const encodedName = encodeURIComponent(name);
1116
+ return this.http.post(
1117
+ `/api/v2/plays/${encodedName}/history/clear`,
1118
+ request
1119
+ );
1120
+ }
1121
+ async listPlayVersions(name, options) {
1122
+ const encodedName = encodeURIComponent(name);
1123
+ const suffix = options?.full ? "?full=true" : "";
1124
+ const response = await this.http.get(
1125
+ `/api/v2/plays/${encodedName}/versions${suffix}`
1126
+ );
1127
+ return response.versions ?? [];
1128
+ }
1129
+ async publishPlayVersion(name, request = {}) {
1130
+ const encodedName = encodeURIComponent(name);
1131
+ return this.http.post(
1132
+ `/api/v2/plays/${encodedName}/live`,
1133
+ request
1134
+ );
1135
+ }
1136
+ };
1137
+ function createPlayClient(options) {
1138
+ const config = resolveConfig(options);
1139
+ return new PlayClient(
1140
+ new HttpClient(config),
1141
+ config.environment ?? process.env
1142
+ );
1143
+ }
1144
+
642
1145
  // ../product-notifications/contract.ts
643
1146
  var PRODUCT_NOTIFICATION_EVENT_CATALOG = [
644
1147
  {
@@ -846,6 +1349,9 @@ function stripLeadingTimestamp(line) {
846
1349
 
847
1350
  // src/runs/read-errors.ts
848
1351
  function isTransientPlayRunReadError(error) {
1352
+ if (error instanceof DeeplineError && error.code === "PLAY_START_OUTCOME_UNKNOWN") {
1353
+ return false;
1354
+ }
849
1355
  if (error instanceof DeeplineError && error.code === "PLAY_STREAM_NETWORK_ABORTED") {
850
1356
  return true;
851
1357
  }
@@ -1886,57 +2392,15 @@ var TERMINAL_RESERVE_BYTES = 64 * 1024;
1886
2392
  var encoder = new TextEncoder();
1887
2393
 
1888
2394
  // ../play-runtime/secret-redaction.ts
1889
- var SECRET_REDACTION_PLACEHOLDER = "[REDACTED_SECRET]";
1890
- var BEARER_TOKEN_RE = /\bBearer\s+[A-Za-z0-9._~+/=-]{12,}\b/g;
1891
- var BASIC_CREDENTIAL_RE = /\bBasic\s+[A-Za-z0-9+/]{4,}={0,2}\b/g;
1892
- var JWT_RE = /\b[A-Za-z0-9_-]{8,16384}\.[A-Za-z0-9_-]{1,16384}\.[A-Za-z0-9_-]{8,16384}\b/g;
1893
- var EMAIL_LOCAL_CHAR_RE = /[A-Za-z0-9.!#$%&'*+/=?^_`{|}~-]/;
1894
- var EMAIL_DOMAIN_RE = /^[A-Za-z0-9](?:[A-Za-z0-9.-]{0,251}[A-Za-z0-9])?\.[A-Za-z]{2,63}\b/;
1895
- function isEmailLocalPart(value, start, end) {
1896
- let localStart = start;
1897
- while (localStart > 0 && EMAIL_LOCAL_CHAR_RE.test(value[localStart - 1])) {
1898
- localStart -= 1;
1899
- }
1900
- let localEnd = end;
1901
- while (localEnd < value.length && EMAIL_LOCAL_CHAR_RE.test(value[localEnd])) {
1902
- localEnd += 1;
1903
- }
1904
- return localStart < localEnd && localEnd - localStart <= 64 && value[localEnd] === "@" && EMAIL_DOMAIN_RE.test(value.slice(localEnd + 1));
1905
- }
1906
- function redactTokenUnlessEmail(match, offset, source) {
1907
- return isEmailLocalPart(source, offset, offset + match.length) ? match : SECRET_REDACTION_PLACEHOLDER;
1908
- }
1909
- var PRIVATE_KEY_RE = /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z0-9 ]*PRIVATE KEY-----/g;
1910
2395
  var SENSITIVE_FIELD_NAME = "authorization|cookie|password|(?:client[_-]?)?secret|(?:access|refresh)[_-]?token|(?:x[_-]?)?api[_-]?key|private[_-]?key|token|credentials";
1911
- var COMMON_SECRET_RE = /\b(?:[prs]k_(?:live|test)_|(?:pat|ghp|github_pat)_|xox[baprs]-)[A-Za-z0-9_./+=:-]{12,}\b/gi;
1912
- var GENERIC_PREFIXED_SECRET_RE = /\b(?:key|token|secret|api[_-]?key)_[A-Za-z0-9_./+=:-]{12,}\b/gi;
1913
- var URL_SECRET_VALUE_RE = /\b(?:[spr]k|pat|ghp|github_pat|xox)[A-Za-z0-9_./+=:-]{12,}\b/i;
1914
- var GENERIC_SECRET_ASSIGNMENT_RE = /\b(?:api[_-]?key|token|secret|password)\b\s*[:=]\s*["']?[^"',\s]{12,}["']?/gi;
1915
- var HIGH_ENTROPY_ASSIGNMENT_RE = /\b(?:api[_-]?key|token|secret|password|authorization|(?:access|refresh)[_-]?token)\b\s*[:=]\s*["']?[^"',\s]{16,}["']?/gi;
1916
- var SENSITIVE_URL_PARAM_RE = /[?#&](?:\w*(?:key|token)|secret|password|authorization|credential|signature|sig)(?:=|$)/i;
1917
2396
  var SENSITIVE_FIELD_NAME_RE = new RegExp(
1918
2397
  `(^|[._-])(?:${SENSITIVE_FIELD_NAME})$`,
1919
2398
  "i"
1920
2399
  );
1921
- var SENSITIVE_FIELD_NAME_CACHE_MAX_ENTRIES = 4096;
1922
- var sensitiveFieldNameCache = /* @__PURE__ */ new Map();
1923
- function isSensitiveFieldName(key) {
1924
- const cached = sensitiveFieldNameCache.get(key);
1925
- if (cached !== void 0) return cached;
1926
- const sensitive = SENSITIVE_FIELD_NAME_RE.test(key);
1927
- if (sensitiveFieldNameCache.size >= SENSITIVE_FIELD_NAME_CACHE_MAX_ENTRIES) {
1928
- sensitiveFieldNameCache.clear();
1929
- }
1930
- sensitiveFieldNameCache.set(key, sensitive);
1931
- return sensitive;
1932
- }
1933
2400
  var SERIALIZED_SECRET_FIELD_RE = new RegExp(
1934
2401
  `"(${SENSITIVE_FIELD_NAME})"\\s*:\\s*(?:"[^"\\\\]*"|\\{[^{}]*\\})`,
1935
2402
  "gi"
1936
2403
  );
1937
- function escapeRegExp(value) {
1938
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1939
- }
1940
2404
  var REDACTION_TRIGGER_RE = new RegExp(
1941
2405
  [
1942
2406
  // Sensitive field names, in object keys and in `name=`/`"name":` text.
@@ -1971,147 +2435,6 @@ var REDACTION_TRIGGER_RE = new RegExp(
1971
2435
  ].join("|"),
1972
2436
  "i"
1973
2437
  );
1974
- function mightContainRedactableSecret(value) {
1975
- return REDACTION_TRIGGER_RE.test(value);
1976
- }
1977
- function redactSecretLikeString(value) {
1978
- if (!mightContainRedactableSecret(value)) return value;
1979
- const serializedRedacted = value.replace(
1980
- SERIALIZED_SECRET_FIELD_RE,
1981
- `"$1":"${SECRET_REDACTION_PLACEHOLDER}"`
1982
- );
1983
- const trimmed = serializedRedacted.trim();
1984
- if (/^https?:\/\/\S+$/i.test(trimmed)) {
1985
- if (/^https?:\/\/[^/?#@]+@/i.test(trimmed) || SENSITIVE_URL_PARAM_RE.test(trimmed)) {
1986
- return SECRET_REDACTION_PLACEHOLDER;
1987
- }
1988
- if (!URL_SECRET_VALUE_RE.test(serializedRedacted)) {
1989
- return serializedRedacted;
1990
- }
1991
- }
1992
- return serializedRedacted.replace(PRIVATE_KEY_RE, SECRET_REDACTION_PLACEHOLDER).replace(BEARER_TOKEN_RE, `Bearer ${SECRET_REDACTION_PLACEHOLDER}`).replace(BASIC_CREDENTIAL_RE, `Basic ${SECRET_REDACTION_PLACEHOLDER}`).replace(JWT_RE, redactTokenUnlessEmail).replace(COMMON_SECRET_RE, redactTokenUnlessEmail).replace(GENERIC_PREFIXED_SECRET_RE, redactTokenUnlessEmail).replace(GENERIC_SECRET_ASSIGNMENT_RE, SECRET_REDACTION_PLACEHOLDER).replace(HIGH_ENTROPY_ASSIGNMENT_RE, (match) => {
1993
- const separator = match.includes("=") ? "=" : ":";
1994
- const [key] = match.split(separator, 1);
1995
- return `${key.trim()}${separator}${SECRET_REDACTION_PLACEHOLDER}`;
1996
- });
1997
- }
1998
- function createSecretRedactionContext(initialValues = []) {
1999
- const exactSecrets = /* @__PURE__ */ new Set();
2000
- const exactSecretMatchers = /* @__PURE__ */ new Map();
2001
- function register(value) {
2002
- if (value.length > 0) exactSecrets.add(value);
2003
- }
2004
- function matchersForSecret(secret) {
2005
- const cached = exactSecretMatchers.get(secret);
2006
- if (cached) return cached;
2007
- const literals = [secret];
2008
- try {
2009
- const encoded = encodeURIComponent(secret);
2010
- if (encoded !== secret) literals.push(encoded);
2011
- } catch {
2012
- }
2013
- const built = literals.map((literal) => ({
2014
- literal,
2015
- matcher: new RegExp(escapeRegExp(literal), "g")
2016
- }));
2017
- exactSecretMatchers.set(secret, built);
2018
- return built;
2019
- }
2020
- for (const value of initialValues) register(value);
2021
- function containsRegisteredSecret(value, {
2022
- includeEncoded = false,
2023
- minimumLength = 1
2024
- } = {}) {
2025
- for (const secret of exactSecrets) {
2026
- if (secret.length < minimumLength) continue;
2027
- if (value.includes(secret)) return true;
2028
- if (includeEncoded) {
2029
- try {
2030
- const encoded = encodeURIComponent(secret);
2031
- if (encoded !== secret && value.includes(encoded)) return true;
2032
- } catch {
2033
- }
2034
- }
2035
- }
2036
- return false;
2037
- }
2038
- function matchesRegisteredSecret(value) {
2039
- return exactSecrets.has(value);
2040
- }
2041
- function redactRegisteredSecrets(value) {
2042
- if (exactSecrets.size === 0) return value;
2043
- let output = value;
2044
- for (const secret of exactSecrets) {
2045
- for (const { literal, matcher } of matchersForSecret(secret)) {
2046
- if (!output.includes(literal)) continue;
2047
- matcher.lastIndex = 0;
2048
- output = output.replace(matcher, SECRET_REDACTION_PLACEHOLDER);
2049
- }
2050
- }
2051
- return output;
2052
- }
2053
- function redactString(value) {
2054
- const output = redactRegisteredSecrets(value);
2055
- return redactSecretLikeString(output);
2056
- }
2057
- function redact(value) {
2058
- if (typeof value === "string") return redactString(value);
2059
- if (Array.isArray(value)) {
2060
- let changed = false;
2061
- const next = new Array(value.length);
2062
- for (let index = 0; index < value.length; index += 1) {
2063
- const entry = value[index];
2064
- const redacted = redact(entry);
2065
- next[index] = redacted;
2066
- if (redacted !== entry) changed = true;
2067
- }
2068
- return changed ? next : value;
2069
- }
2070
- if (value && typeof value === "object") {
2071
- const prototype = Object.getPrototypeOf(value);
2072
- let changed = prototype !== Object.prototype && prototype !== null;
2073
- const source = value;
2074
- const keys = Object.keys(source);
2075
- const redactedValues = new Array(keys.length);
2076
- for (let index = 0; index < keys.length; index += 1) {
2077
- const key = keys[index];
2078
- const entry = source[key];
2079
- const redacted = isSensitiveFieldName(key) ? SECRET_REDACTION_PLACEHOLDER : redact(entry);
2080
- redactedValues[index] = redacted;
2081
- if (redacted !== entry) changed = true;
2082
- }
2083
- if (!changed) return value;
2084
- return Object.fromEntries(
2085
- keys.map((key, index) => [key, redactedValues[index]])
2086
- );
2087
- }
2088
- return value;
2089
- }
2090
- function redactKnownSecrets(value) {
2091
- if (typeof value === "string") return redactRegisteredSecrets(value);
2092
- if (Array.isArray(value)) {
2093
- return value.map((entry) => redactKnownSecrets(entry));
2094
- }
2095
- if (value && typeof value === "object") {
2096
- return Object.fromEntries(
2097
- Object.entries(value).map(([key, entry]) => [
2098
- key,
2099
- redactKnownSecrets(entry)
2100
- ])
2101
- );
2102
- }
2103
- return value;
2104
- }
2105
- return {
2106
- register,
2107
- containsRegisteredSecret,
2108
- matchesRegisteredSecret,
2109
- redactRegisteredSecrets,
2110
- redactString,
2111
- redactKnownSecrets,
2112
- redact
2113
- };
2114
- }
2115
2438
 
2116
2439
  // ../play-runtime/output-size-limits.ts
2117
2440
  var TERMINAL_RUN_RESULT_MAX_BYTES = 6 * 1024 * 1024;
@@ -2127,9 +2450,6 @@ var RUNTIME_RECEIPT_WRITER_TARGET_BATCH_BYTES = 8 * 1024 * 1024;
2127
2450
  var RUNNER_POST_TERMINAL_DIAGNOSTIC_MAX_BYTES = 64 * 1024;
2128
2451
  var JSON_SIZE_LIMIT_REACHED = Symbol("JSON_SIZE_LIMIT_REACHED");
2129
2452
 
2130
- // ../play-runtime/ledger-safe-payload.ts
2131
- var ledgerIngressRedactor = createSecretRedactionContext();
2132
-
2133
2453
  // ../play-runtime/docflow-node-io.ts
2134
2454
  var utf8Encoder = new TextEncoder();
2135
2455
  var PLAY_DATASET_BRAND = Symbol.for("deepline.play.dataset");
@@ -2550,6 +2870,8 @@ function diffPlayRunStreamEvents(input) {
2550
2870
 
2551
2871
  // src/runs/watch-polling.ts
2552
2872
  var RUN_WATCH_POLL_INTERVAL_MS = 2e3;
2873
+ var RUN_WATCH_STARTUP_POLL_INTERVAL_MS = 500;
2874
+ var RUN_WATCH_STARTUP_WINDOW_MS = 6e3;
2553
2875
  function waitForNextPoll(ms, signal) {
2554
2876
  if (signal?.aborted) {
2555
2877
  return Promise.resolve(false);
@@ -2576,7 +2898,16 @@ function readSnapshotResponse(response, runId) {
2576
2898
  ) || typeof snapshot.totalLogCount !== "number" || !Array.isArray(snapshot.nodeStates)) {
2577
2899
  throw new Error(`Run watch returned an invalid snapshot for ${runId}.`);
2578
2900
  }
2579
- return { runId, snapshot };
2901
+ if (record.terminalStatus !== void 0 && (!record.terminalStatus || typeof record.terminalStatus !== "object" || Array.isArray(record.terminalStatus) || !isTerminalPlayRunLiveStatus(snapshot.status))) {
2902
+ throw new Error(
2903
+ `Run watch returned an invalid terminal status for ${runId}.`
2904
+ );
2905
+ }
2906
+ return {
2907
+ runId,
2908
+ snapshot,
2909
+ ...record.terminalStatus !== void 0 ? { terminalStatus: record.terminalStatus } : {}
2910
+ };
2580
2911
  }
2581
2912
  async function backfillRotatedLogTail(input) {
2582
2913
  if (!input.previous.runSignature || input.snapshot.logsUnavailable) {
@@ -2613,25 +2944,70 @@ async function backfillRotatedLogTail(input) {
2613
2944
  }
2614
2945
  async function* watchRunEvents(options) {
2615
2946
  const { http, runId, signal } = options;
2616
- const path = `/api/v2/runs/${encodeURIComponent(runId)}?watchSnapshot=true`;
2947
+ const params = new URLSearchParams({ watchSnapshot: "true" });
2948
+ if (options.deferLogs) params.set("deferLogs", "true");
2949
+ if (options.omitTerminalLogs) params.set("omitTerminalLogs", "true");
2950
+ if (options.terminalStatus) {
2951
+ params.set(
2952
+ "terminalStatus",
2953
+ options.terminalStatus.full ? "full" : "compact"
2954
+ );
2955
+ if (options.terminalStatus.billing === false)
2956
+ params.set("billing", "false");
2957
+ if (options.terminalStatus.runStatus)
2958
+ params.set("terminalRunStatus", "true");
2959
+ }
2960
+ const path = `/api/v2/runs/${encodeURIComponent(runId)}?${params}`;
2617
2961
  const streamId = `play-run-watch:${runId}`;
2618
- const pollIntervalMs = options.pollIntervalMs ?? RUN_WATCH_POLL_INTERVAL_MS;
2962
+ const watchStartedAt = Date.now();
2619
2963
  let previous = EMPTY_PLAY_RUN_STREAM_DIFF_STATE;
2620
2964
  let consecutiveFailures = 0;
2621
2965
  let outageNoticeSent = false;
2966
+ let pollCount = 0;
2622
2967
  while (!signal?.aborted) {
2623
2968
  const pollStartedAt = Date.now();
2969
+ const poll = ++pollCount;
2970
+ const pollIntervalMs = options.pollIntervalMs ?? (pollStartedAt - watchStartedAt < RUN_WATCH_STARTUP_WINDOW_MS ? RUN_WATCH_STARTUP_POLL_INTERVAL_MS : RUN_WATCH_POLL_INTERVAL_MS);
2624
2971
  let snapshot;
2972
+ let terminalStatus;
2973
+ let responseStatus;
2974
+ let serverTiming;
2975
+ let pollReported = false;
2976
+ const reportPoll = () => {
2977
+ if (!options.onPoll || pollReported) return;
2978
+ pollReported = true;
2979
+ try {
2980
+ options.onPoll({
2981
+ poll,
2982
+ durationMs: Date.now() - pollStartedAt,
2983
+ ...responseStatus !== void 0 ? { status: responseStatus } : {},
2984
+ ...serverTiming ? { serverTiming } : {}
2985
+ });
2986
+ } catch {
2987
+ }
2988
+ };
2625
2989
  try {
2626
- const response = await http.get(path, { signal });
2627
- snapshot = await backfillRotatedLogTail({
2990
+ const response = await http.get(path, {
2991
+ signal,
2992
+ ...options.onPoll ? {
2993
+ onResponse: (response2) => {
2994
+ responseStatus = response2.status;
2995
+ serverTiming = response2.headers.get("server-timing") ?? void 0;
2996
+ }
2997
+ } : {}
2998
+ });
2999
+ const parsed = readSnapshotResponse(response, runId);
3000
+ terminalStatus = parsed.terminalStatus;
3001
+ snapshot = options.deferLogs ? parsed.snapshot : await backfillRotatedLogTail({
2628
3002
  http,
2629
3003
  runId,
2630
- snapshot: readSnapshotResponse(response, runId).snapshot,
3004
+ snapshot: parsed.snapshot,
2631
3005
  previous,
2632
3006
  signal
2633
3007
  });
3008
+ reportPoll();
2634
3009
  } catch (error) {
3010
+ reportPoll();
2635
3011
  if (signal?.aborted) {
2636
3012
  return;
2637
3013
  }
@@ -2647,7 +3023,7 @@ async function* watchRunEvents(options) {
2647
3023
  reason
2648
3024
  });
2649
3025
  options.onNotice?.(
2650
- `[watch] run snapshot request failed; retrying every ${pollIntervalMs / 1e3}s (${reason})`
3026
+ `[watch] run snapshot request failed; retrying snapshot polling (${reason})`
2651
3027
  );
2652
3028
  outageNoticeSent = true;
2653
3029
  }
@@ -2665,6 +3041,9 @@ async function* watchRunEvents(options) {
2665
3041
  outageNoticeSent = false;
2666
3042
  }
2667
3043
  consecutiveFailures = 0;
3044
+ if (terminalStatus) {
3045
+ await options.terminalStatus?.onStatus(terminalStatus, snapshot.status);
3046
+ }
2668
3047
  const diff = diffPlayRunStreamEvents({
2669
3048
  streamId,
2670
3049
  snapshot,
@@ -2687,51 +3066,34 @@ async function* watchRunEvents(options) {
2687
3066
  }
2688
3067
  }
2689
3068
 
2690
- // ../play-runtime/runtime-environment.ts
2691
- var PLAY_RUNTIME_ENVIRONMENTS = ["preview"];
2692
- var PLAY_RUNTIME_NAMESPACE_PATTERN = /^[a-z][a-z0-9-]{0,30}$/;
2693
- var PLAY_RUNTIME_SANDBOX_PLACEMENT_POLICY_PATTERN = /^[a-z][a-z0-9_]*@[1-9][0-9]*$/;
2694
- function normalizePlayRuntimeNamespace(value) {
2695
- if (typeof value !== "string") return null;
2696
- const normalized = value.trim();
2697
- return PLAY_RUNTIME_NAMESPACE_PATTERN.test(normalized) ? normalized : null;
2698
- }
2699
- function normalizePlayRuntimeSandboxPlacementPolicy(value) {
2700
- if (typeof value !== "string") return null;
2701
- const normalized = value.trim();
2702
- return PLAY_RUNTIME_SANDBOX_PLACEMENT_POLICY_PATTERN.test(normalized) ? normalized : null;
2703
- }
2704
- function normalizePlayRuntimeSelection(value) {
2705
- if (!value || typeof value !== "object" || Array.isArray(value)) return null;
2706
- const record = value;
2707
- if (Object.keys(record).some(
2708
- (key) => key !== "environment" && key !== "namespace" && key !== "backend" && key !== "sandboxPlacementPolicy"
2709
- ) || record.environment !== "preview") {
2710
- return null;
2711
- }
2712
- const namespace = normalizePlayRuntimeNamespace(record.namespace);
2713
- if (!namespace) return null;
2714
- const sandboxPlacementPolicy = record.sandboxPlacementPolicy === void 0 ? void 0 : normalizePlayRuntimeSandboxPlacementPolicy(
2715
- record.sandboxPlacementPolicy
3069
+ // src/client.ts
3070
+ function invalidToolDiscoveryResponse() {
3071
+ return new DeeplineError(
3072
+ "The server returned an invalid tool discovery response.",
3073
+ 502,
3074
+ "INVALID_TOOL_DISCOVERY_RESPONSE"
2716
3075
  );
2717
- if (record.sandboxPlacementPolicy !== void 0 && sandboxPlacementPolicy === null) {
2718
- return null;
2719
- }
2720
- if (record.backend !== void 0 && record.backend !== PLAY_RUNTIME_BACKENDS.daytona && record.backend !== PLAY_RUNTIME_BACKENDS.modal) {
2721
- return null;
2722
- }
2723
- return {
2724
- environment: "preview",
2725
- namespace,
2726
- ...record.backend === void 0 ? {} : { backend: record.backend },
2727
- ...sandboxPlacementPolicy ? { sandboxPlacementPolicy } : {}
2728
- };
2729
3076
  }
2730
- function normalizePlayRuntimeEnvironment(value) {
2731
- return typeof value === "string" && PLAY_RUNTIME_ENVIRONMENTS.includes(value) ? value : null;
3077
+ function readToolCategorySummaries(value) {
3078
+ if (!Array.isArray(value)) throw invalidToolDiscoveryResponse();
3079
+ const seen = /* @__PURE__ */ new Set();
3080
+ return value.map((item) => {
3081
+ if (!item || typeof item !== "object" || !("name" in item) || typeof item.name !== "string" || !item.name.trim() || seen.has(item.name) || !("count" in item) || typeof item.count !== "number" || !Number.isSafeInteger(item.count) || item.count < 0) {
3082
+ throw invalidToolDiscoveryResponse();
3083
+ }
3084
+ seen.add(item.name);
3085
+ return { name: item.name, count: item.count };
3086
+ });
3087
+ }
3088
+ function summarizeToolCategories(tools) {
3089
+ const counts = /* @__PURE__ */ new Map();
3090
+ for (const tool of tools) {
3091
+ for (const category of tool.categories ?? []) {
3092
+ counts.set(category, (counts.get(category) ?? 0) + 1);
3093
+ }
3094
+ }
3095
+ return [...counts].map(([name, count]) => ({ name, count })).sort((a, b) => b.count - a.count || a.name.localeCompare(b.name));
2732
3096
  }
2733
-
2734
- // src/client.ts
2735
3097
  var MAX_NOTIFICATION_GROUP_CREATE_TARGETS = 50;
2736
3098
  function parseNotificationGroupTarget(value) {
2737
3099
  const normalized = value.trim();
@@ -2776,123 +3138,6 @@ var COMPILE_MANIFEST_RETRY_DELAYS_MS = [250, 1e3];
2776
3138
  var REGISTER_PLAY_ARTIFACTS_COMPILE_CONCURRENCY = 3;
2777
3139
  var REGISTER_PLAY_ARTIFACTS_MAX_BATCH_COUNT = 3;
2778
3140
  var REGISTER_PLAY_ARTIFACTS_MAX_BATCH_BYTES = 25e5;
2779
- function normalizePlayRunIntegrationMode(value) {
2780
- if (value === "live" || value === "eval_stub" || value === "fixture") {
2781
- return value;
2782
- }
2783
- return void 0;
2784
- }
2785
- function resolvePlayRunIntegrationMode(request, env) {
2786
- return normalizePlayRunIntegrationMode(
2787
- request.integrationMode ?? env.DEEPLINE_EVAL_INTEGRATION_MODE
2788
- );
2789
- }
2790
- function resolvePlayRunRuntimeSelection(request, env) {
2791
- if (request.runtime !== void 0) {
2792
- const runtime = normalizePlayRuntimeSelection(request.runtime);
2793
- if (!runtime) {
2794
- throw new DeeplineError(
2795
- 'runtime must be { environment: "preview", namespace, backend?: "daytona" | "modal", sandboxPlacementPolicy?: "policy_name@1" } with namespace matching ^[a-z][a-z0-9-]{0,30}$.',
2796
- void 0,
2797
- "INVALID_RUNTIME_SELECTION"
2798
- );
2799
- }
2800
- return runtime;
2801
- }
2802
- const configured = env.DEEPLINE_RUNTIME_ENVIRONMENT?.trim();
2803
- const configuredNamespace = env.DEEPLINE_RUNTIME_NAMESPACE?.trim();
2804
- const configuredToken = env.DEEPLINE_RUNTIME_ENVIRONMENT_TOKEN?.trim();
2805
- const configuredPlacementPolicy = env.DEEPLINE_RUNTIME_SANDBOX_PLACEMENT_POLICY?.trim();
2806
- if (!configured) {
2807
- if (configuredNamespace || configuredToken || configuredPlacementPolicy) {
2808
- const present = [];
2809
- if (configuredNamespace) present.push("DEEPLINE_RUNTIME_NAMESPACE");
2810
- if (configuredToken) present.push("DEEPLINE_RUNTIME_ENVIRONMENT_TOKEN");
2811
- if (configuredPlacementPolicy) {
2812
- present.push("DEEPLINE_RUNTIME_SANDBOX_PLACEMENT_POLICY");
2813
- }
2814
- const isPlural = present.length > 1;
2815
- throw new DeeplineError(
2816
- [
2817
- `Incomplete play runtime selection: ${present.join(" and ")} ${isPlural ? "are" : "is"} set but DEEPLINE_RUNTIME_ENVIRONMENT is not.`,
2818
- "Preview routing requires DEEPLINE_RUNTIME_ENVIRONMENT=preview together with DEEPLINE_RUNTIME_NAMESPACE; the token alone authorizes nothing.",
2819
- `If you did not export ${isPlural ? "these" : "this"} yourself, ${isPlural ? "they were" : "it was"} most likely auto-loaded from a .env or .env.local in the current directory (bun loads those automatically, and in a git worktree .env.local is usually a symlink to the main checkout).`,
2820
- `To run against the app-native runtime, clear ${isPlural ? "them" : "it"} for this command: ${present.map((name) => `${name}=`).join(" ")} <your deepline command>`
2821
- ].join("\n"),
2822
- void 0,
2823
- "INVALID_RUNTIME_ENVIRONMENT"
2824
- );
2825
- }
2826
- return void 0;
2827
- }
2828
- const environment = normalizePlayRuntimeEnvironment(configured);
2829
- if (!environment) {
2830
- throw new DeeplineError(
2831
- `DEEPLINE_RUNTIME_ENVIRONMENT supports only explicit preview selection. Received "${configured}". Omit it to use the app-native runtime.`,
2832
- void 0,
2833
- "INVALID_RUNTIME_ENVIRONMENT"
2834
- );
2835
- }
2836
- const namespace = normalizePlayRuntimeNamespace(configuredNamespace);
2837
- if (!namespace) {
2838
- throw new DeeplineError(
2839
- "DEEPLINE_RUNTIME_NAMESPACE is required for preview and must match ^[a-z][a-z0-9-]{0,30}$.",
2840
- void 0,
2841
- "INVALID_RUNTIME_NAMESPACE"
2842
- );
2843
- }
2844
- const configuredBackendValue = env.DEEPLINE_PLAY_RUNNER_BACKEND?.trim();
2845
- const configuredBackend = configuredBackendValue === "daytona" || configuredBackendValue === "modal" ? configuredBackendValue : void 0;
2846
- if (configuredBackendValue && !configuredBackend) {
2847
- throw new DeeplineError(
2848
- `DEEPLINE_PLAY_RUNNER_BACKEND must be daytona or modal for preview runtime selection. Received "${configuredBackendValue}".`,
2849
- void 0,
2850
- "INVALID_RUNTIME_BACKEND"
2851
- );
2852
- }
2853
- if (configuredPlacementPolicy && !normalizePlayRuntimeSandboxPlacementPolicy(configuredPlacementPolicy)) {
2854
- throw new DeeplineError(
2855
- `DEEPLINE_RUNTIME_SANDBOX_PLACEMENT_POLICY must be a versioned placement policy id such as daytona_only@1. Received "${configuredPlacementPolicy}".`,
2856
- void 0,
2857
- "INVALID_RUNTIME_SANDBOX_PLACEMENT_POLICY"
2858
- );
2859
- }
2860
- return {
2861
- environment,
2862
- namespace,
2863
- ...configuredBackend ? { backend: configuredBackend } : {},
2864
- ...configuredPlacementPolicy ? { sandboxPlacementPolicy: configuredPlacementPolicy } : {}
2865
- };
2866
- }
2867
- function resolveStartPlayRunRequest(request, env) {
2868
- const integrationMode = resolvePlayRunIntegrationMode(request, env);
2869
- const testPolicyOverrides = request.testPolicyOverrides ?? parseEnvTestPolicyOverrides(env);
2870
- const runtime = resolvePlayRunRuntimeSelection(request, env);
2871
- const resolved = { ...request };
2872
- delete resolved.integrationMode;
2873
- delete resolved.runtime;
2874
- delete resolved.testPolicyOverrides;
2875
- if (integrationMode) resolved.integrationMode = integrationMode;
2876
- if (runtime) resolved.runtime = runtime;
2877
- if (testPolicyOverrides) resolved.testPolicyOverrides = testPolicyOverrides;
2878
- return { request: resolved, runtime };
2879
- }
2880
- function runtimeSelectionHeaders(runtime, env) {
2881
- if (!runtime) return void 0;
2882
- const token = env.DEEPLINE_RUNTIME_ENVIRONMENT_TOKEN?.trim();
2883
- if (!token) {
2884
- throw new DeeplineError(
2885
- "DEEPLINE_RUNTIME_ENVIRONMENT_TOKEN is required for explicit preview runtime selection.",
2886
- void 0,
2887
- "RUNTIME_ENVIRONMENT_TOKEN_REQUIRED"
2888
- );
2889
- }
2890
- const receivers = env.DEEPLINE_RUNTIME_RETAINED_PIPELINE_RECEIVERS?.trim();
2891
- return {
2892
- [RUNTIME_ENVIRONMENT_TOKEN_HEADER]: token,
2893
- ...receivers ? { [RETAINED_PIPELINE_RECEIVERS_HEADER]: receivers } : {}
2894
- };
2895
- }
2896
3141
  function isolatedCiRuntimeEventReadHeaders(env) {
2897
3142
  const schema = env.DEEPLINE_RUNTIME_SCHEDULER_SCHEMA?.trim();
2898
3143
  if (env.DEEPLINE_SYNTHETIC_RUN?.trim() !== "1" || !schema || !/^fly_absurd_pr_\d+_\d+(?:_\d+)?$/.test(schema)) {
@@ -2908,33 +3153,6 @@ function isolatedCiRuntimeEventReadHeaders(env) {
2908
3153
  }
2909
3154
  return { [COORDINATOR_INTERNAL_TOKEN_HEADER]: token };
2910
3155
  }
2911
- function normalizeTestPolicyOverrides(value, source) {
2912
- if (value && typeof value === "object" && !Array.isArray(value)) {
2913
- return value;
2914
- }
2915
- throw new DeeplineError(
2916
- `${source} must be a JSON object.`,
2917
- void 0,
2918
- "INVALID_TEST_POLICY_OVERRIDES"
2919
- );
2920
- }
2921
- function parseEnvTestPolicyOverrides(env) {
2922
- const raw = env.DEEPLINE_TEST_POLICY_OVERRIDES;
2923
- const trimmed = raw?.trim();
2924
- if (!trimmed) return void 0;
2925
- let parsed;
2926
- try {
2927
- parsed = JSON.parse(trimmed);
2928
- } catch (error) {
2929
- const detail = error instanceof Error ? ` ${error.message}` : "";
2930
- throw new DeeplineError(
2931
- `DEEPLINE_TEST_POLICY_OVERRIDES must be valid JSON.${detail}`,
2932
- void 0,
2933
- "INVALID_TEST_POLICY_OVERRIDES"
2934
- );
2935
- }
2936
- return normalizeTestPolicyOverrides(parsed, "DEEPLINE_TEST_POLICY_OVERRIDES");
2937
- }
2938
3156
  function sleep(ms) {
2939
3157
  return new Promise((resolve) => setTimeout(resolve, ms));
2940
3158
  }
@@ -3042,7 +3260,7 @@ var MONITOR_JOB_RECONCILIATION_STATES = [
3042
3260
  "partial",
3043
3261
  "mismatched"
3044
3262
  ];
3045
- function isRecord2(value) {
3263
+ function isRecord3(value) {
3046
3264
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
3047
3265
  }
3048
3266
  function isPrebuiltPlayDescription(play) {
@@ -3053,33 +3271,6 @@ function preferPrebuiltPlayDescriptions(plays) {
3053
3271
  (left, right) => Number(right.play.pinned) - Number(left.play.pinned) || Number(isPrebuiltPlayDescription(right.play)) - Number(isPrebuiltPlayDescription(left.play)) || left.index - right.index
3054
3272
  ).map(({ play }) => play);
3055
3273
  }
3056
- function normalizePlayStatus(raw) {
3057
- const runPackage = isPlayRunPackage(raw) ? raw : isPlayRunPackage(raw.package) ? raw.package : null;
3058
- const packageRun = runPackage?.run;
3059
- const rawStatus = typeof raw.status === "string" ? raw.status : typeof packageRun?.status === "string" ? packageRun.status : null;
3060
- const status = decodePlayRunPublicStatus(rawStatus);
3061
- if (!status) {
3062
- throw new Error(
3063
- `Invalid play run lifecycle status in API response: ${JSON.stringify(rawStatus)}.`
3064
- );
3065
- }
3066
- const runId = typeof raw.runId === "string" ? raw.runId : typeof raw.workflowId === "string" ? raw.workflowId : packageRun?.id ?? "";
3067
- const legacyOverview = runPackage?.overview ?? raw.overview;
3068
- const runStatus = runPackage?.runStatus ?? raw.runStatus;
3069
- return {
3070
- ...raw,
3071
- runId,
3072
- ...isRecord2(legacyOverview) ? {
3073
- overview: legacyOverview
3074
- } : {},
3075
- ...isRecord2(runStatus) ? { runStatus } : {},
3076
- ...runPackage ? {
3077
- package: runPackage,
3078
- outputs: runPackage.outputs
3079
- } : {},
3080
- status
3081
- };
3082
- }
3083
3274
  function readStringArray(value) {
3084
3275
  return Array.isArray(value) ? value.filter((line) => typeof line === "string") : [];
3085
3276
  }
@@ -3125,7 +3316,7 @@ function updatePlayLiveStatusState(state, event) {
3125
3316
  }
3126
3317
  const runId = typeof payload.runId === "string" && payload.runId ? payload.runId : isPlayRunPackage(payload) ? payload.run.id : state.runId;
3127
3318
  const status = playLiveRunStatus(event) ?? state.status;
3128
- const progressPayload = isRecord2(payload.progress) ? payload.progress : {};
3319
+ const progressPayload = isRecord3(payload.progress) ? payload.progress : {};
3129
3320
  if (event.type === "play.run.final_status" && state.logs.length === 0 && state.lastLogSeq === 0) {
3130
3321
  const payloadLogs = readStringArray(payload.logs);
3131
3322
  const progressLogs = readStringArray(progressPayload.logs);
@@ -3175,6 +3366,7 @@ function playRunResultFromStatus(status, startedAt, fallbackRunId) {
3175
3366
  var DeeplineClient = class _DeeplineClient {
3176
3367
  http;
3177
3368
  toolCalls;
3369
+ playClient;
3178
3370
  config;
3179
3371
  environment;
3180
3372
  /** Canonical run lifecycle namespace backed by `/api/v2/runs`. */
@@ -3200,6 +3392,7 @@ var DeeplineClient = class _DeeplineClient {
3200
3392
  this.config = resolveConfig(options);
3201
3393
  this.environment = this.config.environment ?? process.env;
3202
3394
  this.http = new HttpClient(this.config);
3395
+ this.playClient = new PlayClient(this.http, this.environment);
3203
3396
  this.toolCalls = new ToolCalls(
3204
3397
  this.http,
3205
3398
  this.config.timeout,
@@ -3240,7 +3433,7 @@ var DeeplineClient = class _DeeplineClient {
3240
3433
  };
3241
3434
  this.db = {
3242
3435
  query: (input) => this.queryDb(input),
3243
- planQuery: (input) => planCustomerDbQuery(input)
3436
+ planQuery: (input) => planCustomerDbQuery(input, this.config.maxRetries)
3244
3437
  };
3245
3438
  this.billing = {
3246
3439
  quote: (input) => this.http.post("/api/v2/billing/quote", input),
@@ -3421,6 +3614,45 @@ var DeeplineClient = class _DeeplineClient {
3421
3614
  );
3422
3615
  return res.tools;
3423
3616
  }
3617
+ /** List category counts without downloading tool definitions. */
3618
+ async listToolCategories() {
3619
+ const response = await this.http.get(
3620
+ "/api/v2/tools?view=categories&compact=true"
3621
+ );
3622
+ if (response.view === "categories") {
3623
+ return readToolCategorySummaries(response.categories);
3624
+ }
3625
+ if (response.view === void 0 && Array.isArray(response.tools)) {
3626
+ return summarizeToolCategories(response.tools);
3627
+ }
3628
+ throw invalidToolDiscoveryResponse();
3629
+ }
3630
+ /** Read one complete category and its live category counts in one request. */
3631
+ async listToolsInCategory(category, options) {
3632
+ const normalizedCategory = category.trim();
3633
+ if (!normalizedCategory || category.includes(",")) {
3634
+ throw new DeeplineError(
3635
+ "Provide one non-empty tool category.",
3636
+ 400,
3637
+ "INVALID_TOOL_CATEGORY"
3638
+ );
3639
+ }
3640
+ const params = new URLSearchParams({
3641
+ view: "category",
3642
+ categories: normalizedCategory,
3643
+ compact: options?.compact === true ? "true" : "false"
3644
+ });
3645
+ const response = await this.http.get(
3646
+ `/api/v2/tools?${params}`
3647
+ );
3648
+ if (!Array.isArray(response.tools) || response.view !== "category" && response.view !== void 0) {
3649
+ throw invalidToolDiscoveryResponse();
3650
+ }
3651
+ return {
3652
+ tools: response.tools,
3653
+ categories: response.view === "category" ? readToolCategorySummaries(response.categories) : await this.listToolCategories()
3654
+ };
3655
+ }
3424
3656
  /** List discoverable providers without requiring a local plugin catalog. */
3425
3657
  async listProviders(options) {
3426
3658
  const suffix = options?.changed ? "?changed=true" : "";
@@ -3559,7 +3791,7 @@ var DeeplineClient = class _DeeplineClient {
3559
3791
  return this.executeTool(toolId, input, options);
3560
3792
  }
3561
3793
  async queryDb(input) {
3562
- return queryCustomerDatabase(this.http, input);
3794
+ return queryCustomerDatabase(this.http, input, this.config.maxRetries);
3563
3795
  }
3564
3796
  /**
3565
3797
  * Run a bounded SQL query against the current mutable customer database.
@@ -3625,13 +3857,7 @@ var DeeplineClient = class _DeeplineClient {
3625
3857
  * ```
3626
3858
  */
3627
3859
  async startPlayRun(request, options) {
3628
- const resolved = resolveStartPlayRunRequest(request, this.environment);
3629
- return startPlayRun(
3630
- this.http,
3631
- resolved.request,
3632
- runtimeSelectionHeaders(resolved.runtime, this.environment),
3633
- options?.onSubmission
3634
- );
3860
+ return this.playClient.startPlayRun(request, options);
3635
3861
  }
3636
3862
  /**
3637
3863
  * Prepare a tenant-scoped start intent without executing it. Its stream
@@ -3665,17 +3891,22 @@ var DeeplineClient = class _DeeplineClient {
3665
3891
  );
3666
3892
  }
3667
3893
  submitted = true;
3668
- for await (const event of http.streamSse(
3669
- PLAY_RUN_START_STREAM_PATH,
3670
- {
3894
+ const streamPath = options?.cliAdmission ? CLI_PLAY_RUN_START_STREAM_PATH : PLAY_RUN_START_STREAM_PATH;
3895
+ try {
3896
+ for await (const event of http.streamSse(streamPath, {
3671
3897
  method: "POST",
3672
3898
  body: JSON.parse(bodyJson),
3673
3899
  headers,
3674
3900
  signal: streamOptions?.signal,
3675
3901
  exactUrlOnly: true
3902
+ })) {
3903
+ if (event.scope === "play") yield event;
3904
+ }
3905
+ } catch (error) {
3906
+ if (options?.cliAdmission && error instanceof DeeplineError && (error.statusCode === 404 || error.statusCode === 405)) {
3907
+ throw new CliPlayAdmissionUnsupportedError(error.statusCode);
3676
3908
  }
3677
- )) {
3678
- if (event.scope === "play") yield event;
3909
+ throw error;
3679
3910
  }
3680
3911
  }
3681
3912
  };
@@ -3991,27 +4222,35 @@ var DeeplineClient = class _DeeplineClient {
3991
4222
  * ```
3992
4223
  */
3993
4224
  async getPlayStatus(workflowId, options) {
3994
- const params = new URLSearchParams();
3995
- if (options?.billing === false) {
3996
- params.set("billing", "false");
3997
- }
3998
- if (options?.full === true) {
3999
- params.set("full", "true");
4000
- }
4001
- const query = params.size > 0 ? `?${params.toString()}` : "";
4002
- const response = await this.http.get(
4003
- `/api/v2/runs/${encodeURIComponent(workflowId)}${query}`
4004
- );
4005
- return normalizePlayStatus(response);
4225
+ return this.playClient.getPlayStatus(workflowId, options);
4006
4226
  }
4007
- /** Poll authenticated run snapshots every two seconds and emit changed events. */
4227
+ /**
4228
+ * Poll authenticated snapshots and emit changes; `deferLogs` delays terminal logs; `omitTerminalLogs` skips them.
4229
+ */
4008
4230
  async *watchRunEvents(workflowId, options) {
4009
4231
  for await (const event of watchRunEvents({
4010
4232
  http: this.http,
4011
4233
  runId: workflowId,
4012
4234
  signal: options?.signal,
4013
4235
  onNotice: options?.onNotice,
4014
- onRetry: options?.onRetry
4236
+ onRetry: options?.onRetry,
4237
+ deferLogs: options?.deferLogs,
4238
+ omitTerminalLogs: options?.omitTerminalLogs,
4239
+ onPoll: options?.onPoll,
4240
+ ...options?.terminalStatus ? {
4241
+ terminalStatus: {
4242
+ ...options.terminalStatus,
4243
+ onStatus: async (raw, expectedStatus) => {
4244
+ const status = normalizePlayStatus(raw);
4245
+ if (status.runId !== workflowId || status.status !== expectedStatus) {
4246
+ throw new Error(
4247
+ `Run watch returned a mismatched terminal status for ${workflowId}.`
4248
+ );
4249
+ }
4250
+ await options.terminalStatus?.onStatus(status);
4251
+ }
4252
+ }
4253
+ } : {}
4015
4254
  })) {
4016
4255
  yield event;
4017
4256
  }
@@ -4051,10 +4290,17 @@ var DeeplineClient = class _DeeplineClient {
4051
4290
  /** Poll snapshots to terminal status and return the durable package. */
4052
4291
  async watchRunToTerminal(runId, options) {
4053
4292
  let terminalObserved = false;
4293
+ let terminalStatus = null;
4054
4294
  for await (const event of this.watchRunEvents(runId, {
4055
4295
  signal: options?.signal,
4056
4296
  onNotice: options?.onNotice,
4057
- onRetry: options?.onRetry ?? options?.onReconnect
4297
+ onRetry: options?.onRetry ?? options?.onReconnect,
4298
+ terminalStatus: {
4299
+ runStatus: true,
4300
+ onStatus: (status) => {
4301
+ terminalStatus = status;
4302
+ }
4303
+ }
4058
4304
  })) {
4059
4305
  options?.onEvent?.(event);
4060
4306
  if (isTerminalPlayRunLifecycleStatus(playLiveRunStatus(event))) {
@@ -4064,7 +4310,7 @@ var DeeplineClient = class _DeeplineClient {
4064
4310
  if (options?.signal?.aborted) {
4065
4311
  throw new DeeplineError("Run watch aborted.", void 0, "ABORTED");
4066
4312
  }
4067
- const refreshed = await this.getRunStatus(runId);
4313
+ const refreshed = terminalStatus ?? await this.getRunStatus(runId);
4068
4314
  if (terminalObserved || isTerminalPlayRunLifecycleStatus(refreshed.status)) {
4069
4315
  return refreshed;
4070
4316
  }
@@ -4101,10 +4347,7 @@ var DeeplineClient = class _DeeplineClient {
4101
4347
  * ```
4102
4348
  */
4103
4349
  async cancelPlay(workflowId) {
4104
- await this.http.request(
4105
- `/api/v2/runs/${encodeURIComponent(workflowId)}/stop`,
4106
- { method: "POST" }
4107
- );
4350
+ return this.playClient.cancelPlay(workflowId);
4108
4351
  }
4109
4352
  /**
4110
4353
  * Stop a running play execution, including open HITL waits.
@@ -4113,10 +4356,7 @@ var DeeplineClient = class _DeeplineClient {
4113
4356
  * @param options.reason - Optional audit/debug reason
4114
4357
  */
4115
4358
  async stopPlay(workflowId, options) {
4116
- return this.http.post(
4117
- `/api/v2/runs/${encodeURIComponent(workflowId)}/stop`,
4118
- options?.reason ? { reason: options.reason } : {}
4119
- );
4359
+ return this.playClient.stopPlay(workflowId, options);
4120
4360
  }
4121
4361
  /**
4122
4362
  * List recent runs for a named play.
@@ -4136,11 +4376,7 @@ var DeeplineClient = class _DeeplineClient {
4136
4376
  * ```
4137
4377
  */
4138
4378
  async listPlayRuns(playName) {
4139
- const encodedName = encodeURIComponent(playName);
4140
- const response = await this.http.get(
4141
- `/api/v2/plays/${encodedName}/runs`
4142
- );
4143
- return response.runs ?? [];
4379
+ return this.playClient.listPlayRuns(playName);
4144
4380
  }
4145
4381
  // ---------------------------------------------------------------------------
4146
4382
  // Legacy workflows (double-shipped). Thin pass-throughs over the live cloud
@@ -5281,9 +5517,7 @@ var DeeplineClient = class _DeeplineClient {
5281
5517
  * ```
5282
5518
  */
5283
5519
  async getPlay(name, options) {
5284
- const encodedName = encodeURIComponent(name);
5285
- const query = options?.source ? `?include=source&revision=${encodeURIComponent(options.source)}` : options?.guidance ? "?include=guidance" : "";
5286
- return this.http.get(`/api/v2/plays/${encodedName}${query}`);
5520
+ return this.playClient.getPlay(name, options);
5287
5521
  }
5288
5522
  /** Read one trigger inventory page without loading run histories. */
5289
5523
  async listPlayTriggerInventory(options) {
@@ -5329,11 +5563,7 @@ var DeeplineClient = class _DeeplineClient {
5329
5563
  * the play definition or revisions.
5330
5564
  */
5331
5565
  async clearPlayHistory(name, request = {}) {
5332
- const encodedName = encodeURIComponent(name);
5333
- return this.http.post(
5334
- `/api/v2/plays/${encodedName}/history/clear`,
5335
- request
5336
- );
5566
+ return this.playClient.clearPlayHistory(name, request);
5337
5567
  }
5338
5568
  /**
5339
5569
  * List saved versions for a named play.
@@ -5345,12 +5575,7 @@ var DeeplineClient = class _DeeplineClient {
5345
5575
  * @returns Version list (newest first)
5346
5576
  */
5347
5577
  async listPlayVersions(name, options) {
5348
- const encodedName = encodeURIComponent(name);
5349
- const suffix = options?.full ? "?full=true" : "";
5350
- const response = await this.http.get(
5351
- `/api/v2/plays/${encodedName}/versions${suffix}`
5352
- );
5353
- return response.versions ?? [];
5578
+ return this.playClient.listPlayVersions(name, options);
5354
5579
  }
5355
5580
  /**
5356
5581
  * Make a play revision live.
@@ -5371,11 +5596,7 @@ var DeeplineClient = class _DeeplineClient {
5371
5596
  * ```
5372
5597
  */
5373
5598
  async publishPlayVersion(name, request = {}) {
5374
- const encodedName = encodeURIComponent(name);
5375
- return this.http.post(
5376
- `/api/v2/plays/${encodedName}/live`,
5377
- request
5378
- );
5599
+ return this.playClient.publishPlayVersion(name, request);
5379
5600
  }
5380
5601
  /**
5381
5602
  * Move an org-owned play to Trash. This disables its active triggers while
@@ -6496,6 +6717,8 @@ export {
6496
6717
  requireBillingIdempotencyKey,
6497
6718
  planCustomerDbQuery,
6498
6719
  validateExecutionIdempotencyKey,
6720
+ normalizePlayStatus,
6721
+ createPlayClient,
6499
6722
  PRODUCT_NOTIFICATION_EVENT_CATALOG,
6500
6723
  resolveLegacySlackNotification,
6501
6724
  RUN_EVENT_SEARCH_KIND_VALUES,