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
@@ -76,6 +76,7 @@ export type SdkCompatibilitySkillsUpdate = {
76
76
  };
77
77
 
78
78
  const CHECK_TIMEOUT_MS = 2_000;
79
+ const SDK_CLI_AUTO_UPDATE_CAPABILITY = 'safe-continuation-v1';
79
80
  /**
80
81
  * A cached clean policy younger than this is trusted outright: the command
81
82
  * runs without a live request. Policy floors move on a release cadence; the
@@ -132,12 +133,14 @@ function shouldSkipCompatibilityCheck(): boolean {
132
133
 
133
134
  export function sdkCompatibilityCachePath(
134
135
  baseUrl: string,
135
- homeDir = homedir(),
136
+ homeDir = process.env.HOME?.trim() || homedir(),
136
137
  ): string {
137
138
  return join(sdkCliStateDirPath(baseUrl, homeDir), 'compat-cache.json');
138
139
  }
139
140
 
140
- function legacySdkCompatibilityCachePath(homeDir = homedir()): string {
141
+ function legacySdkCompatibilityCachePath(
142
+ homeDir = process.env.HOME?.trim() || homedir(),
143
+ ): string {
141
144
  return join(homeDir, '.cache', 'deepline', 'sdk-compat-cache.json');
142
145
  }
143
146
 
@@ -229,6 +232,36 @@ function isSavedWithin(savedAt: unknown, windowMs: number, now: number) {
229
232
  return Number.isFinite(age) && age >= 0 && age <= windowMs;
230
233
  }
231
234
 
235
+ const UPDATE_ADVISORY_INTERVAL_MS = 24 * 60 * 60 * 1000;
236
+
237
+ /**
238
+ * True when the optional "update available" / "deprecated" notice is due for
239
+ * this host: at most once a day, so a loop of commands prints it once rather
240
+ * than once per iteration. A state directory that cannot be written repeats
241
+ * the notice instead of hiding it.
242
+ */
243
+ export function claimUpdateAdvisory(
244
+ baseUrl: string,
245
+ now = Date.now(),
246
+ ): boolean {
247
+ const path = join(sdkCliStateDirPath(baseUrl), 'update-advisory.json');
248
+ try {
249
+ const { shownAt } = JSON.parse(readFileSync(path, 'utf8')) as {
250
+ shownAt?: unknown;
251
+ };
252
+ if (isSavedWithin(shownAt, UPDATE_ADVISORY_INTERVAL_MS, now)) return false;
253
+ } catch {
254
+ // No marker yet, or an unreadable one: the notice is due.
255
+ }
256
+ try {
257
+ mkdirSync(dirname(path), { recursive: true });
258
+ writeFileSync(path, `${JSON.stringify({ shownAt: now })}\n`);
259
+ } catch {
260
+ // Read-only state directory.
261
+ }
262
+ return true;
263
+ }
264
+
232
265
  function readCachedCompatibility(
233
266
  baseUrl: string,
234
267
  command: string | null | undefined,
@@ -348,6 +381,8 @@ export async function checkSdkCompatibility(
348
381
  /** Optional read-only catalog result carried by the same live check. */
349
382
  catalog?: {
350
383
  toolId: string;
384
+ cliInvocationId?: string;
385
+ purpose?: 'execution_warnings';
351
386
  acceptMetadata: (metadata: unknown) => void;
352
387
  };
353
388
  } = {},
@@ -378,6 +413,9 @@ export async function checkSdkCompatibility(
378
413
  }
379
414
  if (options.catalog) {
380
415
  url.searchParams.set('catalog_tool_id', options.catalog.toolId);
416
+ if (options.catalog.purpose) {
417
+ url.searchParams.set('catalog_purpose', options.catalog.purpose);
418
+ }
381
419
  }
382
420
  const response = await fetch(url, {
383
421
  method: 'GET',
@@ -388,9 +426,13 @@ export async function checkSdkCompatibility(
388
426
  'X-Deepline-API-Major': String(SDK_API_MAJOR),
389
427
  'X-Deepline-API-Contract': SDK_API_CONTRACT,
390
428
  'X-Deepline-CLI-Update-Preferences': '1',
429
+ 'X-Deepline-CLI-Auto-Update-Capability': SDK_CLI_AUTO_UPDATE_CAPABILITY,
391
430
  ...(options.catalog
392
431
  ? { Authorization: `Bearer ${resolveApiKeyForBaseUrl(baseUrl)}` }
393
432
  : {}),
433
+ ...(options.catalog?.cliInvocationId
434
+ ? { 'X-Deepline-CLI-Invocation-ID': options.catalog.cliInvocationId }
435
+ : {}),
394
436
  },
395
437
  signal: controller.signal,
396
438
  });
@@ -416,6 +458,7 @@ export async function checkSdkCompatibility(
416
458
  );
417
459
  }
418
460
  if (
461
+ data.ok &&
419
462
  options.catalog &&
420
463
  catalogMetadata &&
421
464
  typeof catalogMetadata === 'object' &&
@@ -1,9 +1,6 @@
1
- import {
2
- CustomerDbQueryOutcomeUnknownError,
3
- DeeplineError,
4
- } from '../errors';
5
- import type { HttpClient } from '../http';
6
- import type { CustomerDbQueryResult } from '../types';
1
+ import { CustomerDbQueryOutcomeUnknownError, DeeplineError } from '../errors';
2
+ import type { HttpClient } from '../http.js';
3
+ import type { CustomerDbQueryResult } from '../types.js';
7
4
 
8
5
  export type CustomerDbQueryOptions = {
9
6
  sql: string;
@@ -24,12 +21,13 @@ export type CustomerDbQueryPlan = {
24
21
  creditSpending: 'none';
25
22
  recovery: 'original_operation_key' | 'unkeyed_unknown_outcome';
26
23
  };
27
- transport: { maxRetries: 0; urlFailover: 'disabled' };
24
+ transport: { maxRetries: number; urlFailover: 'disabled' | 'loopback_only' };
28
25
  };
29
26
 
30
27
  /** Validate and describe SQL execution without sending it. */
31
28
  export function planCustomerDbQuery(
32
29
  input: CustomerDbQueryOptions,
30
+ maxRetries = 3,
33
31
  ): CustomerDbQueryPlan {
34
32
  if (typeof input.sql !== 'string' || !input.sql.trim())
35
33
  throw new DeeplineError('sql must not be empty.', 400, 'INVALID_SQL');
@@ -87,8 +85,8 @@ export function planCustomerDbQuery(
87
85
  : 'original_operation_key',
88
86
  },
89
87
  transport: {
90
- maxRetries: 0,
91
- urlFailover: 'disabled',
88
+ maxRetries: operationKey === undefined ? 0 : maxRetries,
89
+ urlFailover: operationKey === undefined ? 'disabled' : 'loopback_only',
92
90
  },
93
91
  };
94
92
  }
@@ -96,8 +94,9 @@ export function planCustomerDbQuery(
96
94
  export async function queryCustomerDatabase(
97
95
  http: HttpClient,
98
96
  input: CustomerDbQueryOptions,
97
+ maxRetries: number,
99
98
  ): Promise<CustomerDbQueryResult> {
100
- const plan = planCustomerDbQuery(input);
99
+ const plan = planCustomerDbQuery(input, maxRetries);
101
100
  let result: CustomerDbQueryResult;
102
101
  try {
103
102
  result = await http.post<CustomerDbQueryResult>(
@@ -54,6 +54,10 @@ import {
54
54
  WORKER_CALLBACK_URL_OVERRIDE_HEADER,
55
55
  } from '../../shared_libs/play-runtime/coordinator-headers.js';
56
56
  import { PLAY_RUNTIME_TEST_FAULT_HEADER } from '../../shared_libs/play-runtime/test-runtime-seams.js';
57
+ import {
58
+ EXECUTE_RETRY_POLICY_HEADER,
59
+ parseExecuteRetryPolicy,
60
+ } from '../../shared_libs/integration-contracts/execute-retry-contract.js';
57
61
 
58
62
  const MAX_DIAGNOSTIC_HEADER_LENGTH = 120;
59
63
  const COWORK_NETWORK_HINT =
@@ -76,6 +80,10 @@ interface RequestOptions {
76
80
  * most mutating API calls must fail loudly after the first server response.
77
81
  */
78
82
  retryApiErrors?: boolean;
83
+ /** Only explicit server-authorized responses may retry; transport failures never retry. */
84
+ retryFromExecutePolicy?: boolean;
85
+ /** Retry budget enabled only by an explicit safe execute-policy response. */
86
+ executePolicyMaxRetries?: number;
79
87
  /** Per-request retry override. Use 0 for possibly delivered mutating calls. */
80
88
  maxRetries?: number;
81
89
  /** Disable localhost/127.0.0.1 failover for non-idempotent requests. */
@@ -101,10 +109,11 @@ interface RequestOptions {
101
109
 
102
110
  interface StreamOptions {
103
111
  method?: 'GET' | 'POST';
112
+ /** An unkeyed mutation must not try another URL after a lost response. */
113
+ exactUrlOnly?: boolean;
104
114
  body?: unknown;
105
115
  headers?: Record<string, string>;
106
116
  signal?: AbortSignal;
107
- exactUrlOnly?: boolean;
108
117
  }
109
118
 
110
119
  type RequestAbortTaggedError = Error & {
@@ -314,29 +323,23 @@ export class HttpClient {
314
323
  if (agentSessionId) {
315
324
  headers['X-Deepline-Agent-Session-Id'] = agentSessionId;
316
325
  }
317
- const playArtifactR2Prefix =
318
- env.DEEPLINE_PLAY_ARTIFACT_R2_PREFIX;
326
+ const playArtifactR2Prefix = env.DEEPLINE_PLAY_ARTIFACT_R2_PREFIX;
319
327
  if (playArtifactR2Prefix) {
320
328
  headers['x-deepline-play-artifact-r2-prefix'] = playArtifactR2Prefix;
321
329
  }
322
- const coordinatorUrl =
323
- env.DEEPLINE_COORDINATOR_URL;
324
- const coordinatorInternalToken =
325
- env.DEEPLINE_INTERNAL_TOKEN;
326
- const absurdReleaseOverride =
327
- env.DEEPLINE_ABSURD_RELEASE;
330
+ const coordinatorUrl = env.DEEPLINE_COORDINATOR_URL;
331
+ const coordinatorInternalToken = env.DEEPLINE_INTERNAL_TOKEN;
332
+ const absurdReleaseOverride = env.DEEPLINE_ABSURD_RELEASE;
328
333
  const preferredReceiptGatewayMachineId =
329
334
  env.DEEPLINE_PREFERRED_RECEIPT_GATEWAY_MACHINE_ID;
330
335
  if (coordinatorUrl?.trim()) {
331
336
  headers[COORDINATOR_URL_OVERRIDE_HEADER] = coordinatorUrl.trim();
332
337
  }
333
- const workerCallbackUrl =
334
- env.DEEPLINE_WORKER_CALLBACK_URL;
338
+ const workerCallbackUrl = env.DEEPLINE_WORKER_CALLBACK_URL;
335
339
  if (workerCallbackUrl?.trim()) {
336
340
  headers[WORKER_CALLBACK_URL_OVERRIDE_HEADER] = workerCallbackUrl.trim();
337
341
  }
338
- const runtimeSchedulerSchema =
339
- env.DEEPLINE_RUNTIME_SCHEDULER_SCHEMA;
342
+ const runtimeSchedulerSchema = env.DEEPLINE_RUNTIME_SCHEDULER_SCHEMA;
340
343
  if (runtimeSchedulerSchema?.trim()) {
341
344
  headers[RUNTIME_SCHEDULER_SCHEMA_OVERRIDE_HEADER] =
342
345
  runtimeSchedulerSchema.trim();
@@ -344,15 +347,13 @@ export class HttpClient {
344
347
  // Automated test harnesses (e.g. tests/gauntlet-e2e-tests) set DEEPLINE_SYNTHETIC_RUN
345
348
  // so their intentionally failing plays do not page the SDK CLI error
346
349
  // channel. Honored server-side only in non-prod (see plays/run route).
347
- const syntheticRun =
348
- env.DEEPLINE_SYNTHETIC_RUN;
350
+ const syntheticRun = env.DEEPLINE_SYNTHETIC_RUN;
349
351
  if (syntheticRun && syntheticRun.trim() && syntheticRun.trim() !== '0') {
350
352
  headers[SYNTHETIC_RUN_HEADER] = '1';
351
353
  }
352
354
  // Test-only runtime seam. The server validates both environment and value
353
355
  // before honoring this header.
354
- const runtimeTestFault =
355
- env.DEEPLINE_TEST_RUNTIME_FAULT;
356
+ const runtimeTestFault = env.DEEPLINE_TEST_RUNTIME_FAULT;
356
357
  if (runtimeTestFault?.trim()) {
357
358
  headers[PLAY_RUNTIME_TEST_FAULT_HEADER] = runtimeTestFault.trim();
358
359
  }
@@ -441,7 +442,7 @@ export class HttpClient {
441
442
  let retryAfterDelayMs: number | null = null;
442
443
  let totalRetryWaitMs = 0;
443
444
 
444
- const maxRetries = options?.maxRetries ?? this.config.maxRetries;
445
+ let maxRetries = options?.maxRetries ?? this.config.maxRetries;
445
446
 
446
447
  requestAttempts: for (let attempt = 0; attempt <= maxRetries; attempt++) {
447
448
  if (options?.signal?.aborted) {
@@ -529,6 +530,18 @@ export class HttpClient {
529
530
  httpStatus: response.status,
530
531
  body: parsed,
531
532
  });
533
+ const responseRetryAllowed =
534
+ options?.retryFromExecutePolicy !== true ||
535
+ parseExecuteRetryPolicy(
536
+ response.headers.get(EXECUTE_RETRY_POLICY_HEADER),
537
+ ) === 'transient-http-safe';
538
+ if (
539
+ options?.retryFromExecutePolicy === true &&
540
+ responseRetryAllowed &&
541
+ options.executePolicyMaxRetries !== undefined
542
+ ) {
543
+ maxRetries = options.executePolicyMaxRetries;
544
+ }
532
545
 
533
546
  const structuredToolError =
534
547
  options?.toolId && !response.ok
@@ -553,7 +566,7 @@ export class HttpClient {
553
566
  retryAfterMs:
554
567
  failure.retryAfterMs ?? Math.max(0, retryAfter),
555
568
  });
556
- if (attempt < maxRetries) {
569
+ if (responseRetryAllowed && attempt < maxRetries) {
557
570
  retryAfterDelayMs = retryAfter;
558
571
  break;
559
572
  }
@@ -576,7 +589,7 @@ export class HttpClient {
576
589
  }
577
590
  const retryAfter = parseRetryAfter(response);
578
591
  lastError = new RateLimitError(retryAfter);
579
- if (attempt < maxRetries) {
592
+ if (responseRetryAllowed && attempt < maxRetries) {
580
593
  retryAfterDelayMs = retryAfter;
581
594
  break;
582
595
  }
@@ -592,6 +605,7 @@ export class HttpClient {
592
605
  if (!response.ok) {
593
606
  const retryableApiError =
594
607
  options?.retryApiErrors === true &&
608
+ responseRetryAllowed &&
595
609
  isRetryableApiErrorResponse(response.status);
596
610
  // A 5xx that escaped a Worker can return Cloudflare's default HTML
597
611
  // error page. Never surface raw HTML to CLI users: replace the
@@ -693,6 +707,7 @@ export class HttpClient {
693
707
  timeoutMs,
694
708
  timedOut: requestTimedOut,
695
709
  });
710
+ if (options?.retryFromExecutePolicy) break requestAttempts;
696
711
  } finally {
697
712
  clearTimeout(timeoutId);
698
713
  options?.signal?.removeEventListener('abort', abortRequest);
@@ -918,7 +933,10 @@ export class HttpClient {
918
933
  options?: Pick<
919
934
  RequestOptions,
920
935
  | 'retryApiErrors'
936
+ | 'retryFromExecutePolicy'
937
+ | 'executePolicyMaxRetries'
921
938
  | 'timeout'
939
+ | 'signal'
922
940
  | 'maxRetries'
923
941
  | 'exactUrlOnly'
924
942
  | 'toolId'
@@ -78,10 +78,7 @@ export {
78
78
  MONITOR_JOB_RECONCILIATION_STATES,
79
79
  MONITOR_JOB_STATES,
80
80
  } from './client';
81
- export type {
82
- CustomerDbQueryOptions,
83
- CustomerDbQueryPlan,
84
- } from './db/query';
81
+ export type { CustomerDbQueryOptions, CustomerDbQueryPlan } from './db/query';
85
82
  export { planCustomerDbQuery } from './db/query';
86
83
  export type { DeeplineToolCallOptions } from './play';
87
84
  export {
@@ -90,7 +87,7 @@ export {
90
87
  } from './plays/local-play-execution';
91
88
  export type { LocalPlayGraph } from './plays/local-play-execution';
92
89
  export type { PlayDescriptionOptions } from './plays/play-description';
93
- export { CtxFetchHttpError } from './play';
90
+ export { CtxFetchHttpError, CtxFetchAmbiguousWriteError } from './play';
94
91
  export { RunObserveTransportUnavailableError } from './runs/observe-transport';
95
92
  export { RunEventQueryInputError } from '../../shared_libs/play-runtime/run-event-query';
96
93
  export type {
@@ -316,6 +313,8 @@ export type {
316
313
  DeeplineClientOptions,
317
314
  ResolvedConfig,
318
315
  ToolDefinition,
316
+ ToolCategorySummary,
317
+ ToolCategoryListing,
319
318
  ToolMetadata,
320
319
  ToolRequestCondition,
321
320
  ToolRequestWarningRule,
@@ -75,6 +75,7 @@
75
75
  * @module
76
76
  */
77
77
  import { DeeplineClient } from './client';
78
+ import { createPlayClient, type PlayClientOperations } from './plays/client';
78
79
  import { DeeplineError } from './errors';
79
80
  import {
80
81
  attachToolResultListDataset,
@@ -83,7 +84,8 @@ import {
83
84
  } from '../../shared_libs/play-runtime/tool-result.js';
84
85
  export { readValue, readList } from '../../shared_libs/play-runtime/tool-result.js';
85
86
  import { createDeferredPlayDataset } from '../../shared_libs/plays/dataset.js';
86
- export { CtxFetchHttpError } from '../../shared_libs/plays/authoring-contract.js';
87
+ export { CtxFetchHttpError } from '../../shared_libs/plays/fetch-http-error.js';
88
+ export { CtxFetchAmbiguousWriteError } from '../../shared_libs/plays/ambiguous-write-error.js';
87
89
  import {
88
90
  QUERY_RESULT_DATASET_PAGE_SIZE,
89
91
  isCustomerDbDatasetTool,
@@ -955,7 +957,7 @@ class DeeplinePlayJobImpl<TOutput = unknown> implements PlayJob<TOutput> {
955
957
  readonly id: string;
956
958
 
957
959
  constructor(
958
- private readonly client: DeeplineClient,
960
+ private readonly client: PlayClientOperations,
959
961
  runId: string,
960
962
  ) {
961
963
  this.id = runId;
@@ -1006,10 +1008,10 @@ class DeeplinePlayJobImpl<TOutput = unknown> implements PlayJob<TOutput> {
1006
1008
  if (status.package) {
1007
1009
  return status.package as TOutput;
1008
1010
  }
1009
- const payload = status.result as { output?: unknown } | undefined;
1010
- return ((payload && 'output' in payload
1011
+ const payload = status.result;
1012
+ return ((payload && typeof payload === 'object' && 'output' in payload
1011
1013
  ? payload.output
1012
- : status.result) ?? null) as TOutput;
1014
+ : payload) ?? null) as TOutput;
1013
1015
  }
1014
1016
  await new Promise((resolve) => setTimeout(resolve, intervalMs));
1015
1017
  }
@@ -1028,7 +1030,7 @@ function createNamedPlayHandle<
1028
1030
  TInput = Record<string, unknown>,
1029
1031
  TOutput = unknown,
1030
1032
  >(
1031
- clientFactory: () => DeeplineClient,
1033
+ clientFactory: () => PlayClientOperations,
1032
1034
  name: string,
1033
1035
  ): DeeplineNamedPlay<TInput, TOutput> {
1034
1036
  return {
@@ -1062,9 +1064,7 @@ function createNamedPlayHandle<
1062
1064
  }
1063
1065
 
1064
1066
  /**
1065
- * High-level SDK context with tool shortcuts and play handles.
1066
- *
1067
- * Created by {@link Deepline.connect}. Wraps a {@link DeeplineClient} with a friendlier API for common operations.
1067
+ * SDK context with tool shortcuts and play handles, created by {@link Deepline.connect}. Wraps a {@link DeeplineClient} for common operations.
1068
1068
  *
1069
1069
  * @example
1070
1070
  * ```typescript
@@ -1872,7 +1872,7 @@ export function definePlay<TInput, TOutput extends PlayReturnObject>(
1872
1872
  });
1873
1873
 
1874
1874
  const handle = createNamedPlayHandle<TInput, TOutput>(
1875
- () => new DeeplineClient(),
1875
+ () => createPlayClient(),
1876
1876
  name,
1877
1877
  );
1878
1878
  for (const key of [
@@ -1,11 +1,9 @@
1
- import { tmpdir } from 'node:os';
2
- import { dirname, join, resolve } from 'node:path';
1
+ import { dirname, resolve } from 'node:path';
3
2
  import { fileURLToPath } from 'node:url';
4
3
  import { existsSync } from 'node:fs';
5
4
  import { realpath } from 'node:fs/promises';
6
5
  import {
7
6
  bundlePlayFile as bundlePlayFileCore,
8
- PLAY_BUNDLE_CACHE_NAMESPACE,
9
7
  type BundlePlayFileOptions,
10
8
  type BundledPlayFileSuccess,
11
9
  type BundledPlayFileResult,
@@ -126,7 +124,10 @@ export function createSdkPlayBundlingAdapter(): PlayBundlingAdapter {
126
124
  nodeModulesDir: HAS_PACKAGED_BUNDLING_SOURCES
127
125
  ? resolve(SDK_PACKAGE_ROOT, 'node_modules')
128
126
  : resolve(PROJECT_ROOT, 'node_modules'),
129
- cacheDir: join(tmpdir(), PLAY_BUNDLE_CACHE_NAMESPACE),
127
+ // Emit the current SDK/compiler implementation on every invocation. The
128
+ // customer graph hash does not include SDK/compiler source bytes, so reusing an
129
+ // artifact can silently retain an older compiler or SDK implementation.
130
+ cacheDir: null,
130
131
  sdkSourceRoot: SDK_SOURCE_ROOT,
131
132
  sdkPackageJson: SDK_PACKAGE_JSON,
132
133
  sdkEntryFile: SDK_ENTRY_FILE,
@@ -156,6 +157,7 @@ export async function bundlePlayFile(
156
157
  const result = await bundlePlayFileCore(filePath, {
157
158
  target: options.target ?? defaultPlayBundleTarget(),
158
159
  exportName: options.exportName,
160
+ compatibility: options.compatibility,
159
161
  adapter: {
160
162
  ...createSdkPlayBundlingAdapter(),
161
163
  sourceIdentityRoot,
@@ -0,0 +1,114 @@
1
+ import type { BundledPlayFileSuccess } from '../../../shared_libs/plays/bundling/index.js';
2
+ import type { DeeplineClient } from '../client.js';
3
+ import { playAuthoringGraphPath } from './authoring-graph-path.js';
4
+
5
+ type CheckRequest = Parameters<DeeplineClient['checkPlayArtifact']>[0];
6
+ type CheckResult = Awaited<ReturnType<DeeplineClient['checkPlayArtifact']>>;
7
+ type LocalPlayGraph = {
8
+ root: BundledPlayFileSuccess;
9
+ nodes: ReadonlyMap<string, BundledPlayFileSuccess>;
10
+ };
11
+
12
+ /** Source validation changes no customer resources and is safe to repeat. */
13
+ export const LOCAL_PLAY_CHECK_EFFECTS = {
14
+ cloudMutations: [] as readonly string[],
15
+ localWrites: [] as readonly string[],
16
+ creditSpending: 'none',
17
+ recovery: 'repeat_validation',
18
+ } as const;
19
+
20
+ /** Check each future-authored composition under its own artifact and source policy. */
21
+ export async function checkLocalPlayGraph(input: {
22
+ client: Pick<DeeplineClient, 'checkPlayArtifact'>;
23
+ graph: LocalPlayGraph;
24
+ rootPlayName: string;
25
+ exportName: string;
26
+ integrationMode?: CheckRequest['integrationMode'];
27
+ prepareArtifact?: (
28
+ artifact: BundledPlayFileSuccess['artifact'],
29
+ ) => BundledPlayFileSuccess['artifact'];
30
+ }): Promise<{ result: CheckResult; failedChild?: BundledPlayFileSuccess }> {
31
+ const { client, graph } = input;
32
+ const nodesByPath = new Map(
33
+ [...graph.nodes.values()].map((node) => [node.filePath, node]),
34
+ );
35
+ const importedPlaysFor = (node: BundledPlayFileSuccess) => {
36
+ const imported = new Set<string>();
37
+ const collect = (parent: BundledPlayFileSuccess) => {
38
+ for (const dependency of parent.importedPlayDependencies) {
39
+ if (imported.has(dependency.filePath)) continue;
40
+ const child = nodesByPath.get(dependency.filePath);
41
+ if (!child)
42
+ throw new Error(
43
+ `Missing bundled Play dependency ${dependency.filePath}.`,
44
+ );
45
+ imported.add(dependency.filePath);
46
+ collect(child);
47
+ }
48
+ };
49
+ collect(node);
50
+ return [...imported].map((filePath) => {
51
+ const child = nodesByPath.get(filePath)!;
52
+ return {
53
+ playName: child.playName,
54
+ sourceCode: child.sourceCode,
55
+ sourcePath: playAuthoringGraphPath(node, child.filePath),
56
+ };
57
+ });
58
+ };
59
+ const checkNode = (node: BundledPlayFileSuccess) => {
60
+ const name =
61
+ node.playName ?? (node === graph.root ? input.rootPlayName : null);
62
+ if (!name)
63
+ throw new Error(`Bundled Play ${node.filePath} has no declared name.`);
64
+ const importedPlays = importedPlaysFor(node);
65
+ const exportName =
66
+ node === graph.root
67
+ ? input.exportName
68
+ : (node.artifact.exportName ?? 'default');
69
+ return client.checkPlayArtifact({
70
+ name,
71
+ sourceCode: node.sourceCode,
72
+ sourceFiles: node.sourceFiles,
73
+ description: node.playDescription ?? undefined,
74
+ artifact: input.prepareArtifact?.(node.artifact) ?? node.artifact,
75
+ ...(exportName === 'default' ? {} : { exportName }),
76
+ ...(importedPlays.length ? { importedPlays } : {}),
77
+ ...(input.integrationMode
78
+ ? { integrationMode: input.integrationMode }
79
+ : {}),
80
+ });
81
+ };
82
+ if (
83
+ [...graph.nodes.values()].some(
84
+ (node) =>
85
+ (node.artifact.compatibility.authoringContractEdition ?? 1) >= 10,
86
+ )
87
+ ) {
88
+ const checked = new Set<string>();
89
+ const checkChildren = async (
90
+ node: BundledPlayFileSuccess,
91
+ ): Promise<{
92
+ result: CheckResult;
93
+ failedChild: BundledPlayFileSuccess;
94
+ } | null> => {
95
+ if (checked.has(node.filePath)) return null;
96
+ checked.add(node.filePath);
97
+ for (const dependency of node.importedPlayDependencies) {
98
+ const child = nodesByPath.get(dependency.filePath);
99
+ if (!child)
100
+ throw new Error(
101
+ `Missing bundled Play dependency ${dependency.filePath}.`,
102
+ );
103
+ const failure = await checkChildren(child);
104
+ if (failure) return failure;
105
+ }
106
+ if (node === graph.root) return null;
107
+ const result = await checkNode(node);
108
+ return result.valid ? null : { result, failedChild: node };
109
+ };
110
+ const failure = await checkChildren(graph.root);
111
+ if (failure) return failure;
112
+ }
113
+ return { result: await checkNode(graph.root) };
114
+ }