deepline 0.3.477 → 0.3.478

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 (165) hide show
  1. package/dist/bundling-sources/sdk/src/client-core.ts +5 -4
  2. package/dist/bundling-sources/sdk/src/http-core.ts +84 -3
  3. package/dist/bundling-sources/sdk/src/http.ts +2 -0
  4. package/dist/bundling-sources/sdk/src/play.ts +8 -2
  5. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  6. package/dist/bundling-sources/shared_libs/observability/scheduled-jobs.json +18 -0
  7. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +10 -2
  8. package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +11 -1
  9. package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +5 -0
  10. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +77 -4
  11. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-session-execution.ts +83 -1
  12. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +7 -0
  13. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-state.ts +60 -27
  14. package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +13 -12
  15. package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +5 -2
  16. package/dist/bundling-sources/shared_libs/plays/sandbox-runtime-limits.ts +37 -12
  17. package/dist/bundling-sources/shared_libs/product-notifications/billing-sales.ts +1 -0
  18. package/dist/bundling-sources/shared_libs/product-notifications/catalog.ts +29 -0
  19. package/dist/bundling-sources/shared_libs/product-notifications/events.ts +15 -0
  20. package/dist/cli/{auth-XVXRRWIQ.mjs → auth-SA4DJCSO.mjs} +5 -5
  21. package/dist/cli/auth-U5SNVC5F.js +41 -0
  22. package/dist/cli/{billing-2H7GFJDT.js → billing-462SAHWX.js} +107 -87
  23. package/dist/cli/{billing-EOPUJ4JI.mjs → billing-Z4D35MV5.mjs} +34 -14
  24. package/dist/cli/{chunk-SG2SZQ4K.mjs → chunk-2FTUPGTN.mjs} +2 -2
  25. package/dist/cli/{chunk-NO7ZC2GV.mjs → chunk-2JS5DNVX.mjs} +5 -5
  26. package/dist/cli/{chunk-3UKKM6OS.js → chunk-2MSKG4ER.js} +7 -6
  27. package/dist/cli/{chunk-ISEUOYCB.mjs → chunk-3LTLQAU4.mjs} +4 -4
  28. package/dist/cli/{chunk-C2V2HQDW.mjs → chunk-3V7INABZ.mjs} +2 -2
  29. package/dist/cli/{chunk-C2AN2HTB.mjs → chunk-4C3TMIF3.mjs} +4 -4
  30. package/dist/cli/{chunk-2YNLW43I.mjs → chunk-4RJRGQ5Y.mjs} +2 -2
  31. package/dist/cli/{chunk-M2UL4ZS5.js → chunk-4RV7U3RP.js} +7 -7
  32. package/dist/cli/{chunk-PQQNMMWM.mjs → chunk-645RNF2Y.mjs} +1 -1
  33. package/dist/cli/{chunk-THMGJOR4.js → chunk-6EEMW7YP.js} +8 -8
  34. package/dist/cli/{chunk-YZA5UJXM.mjs → chunk-75DAGCEX.mjs} +1 -1
  35. package/dist/cli/{chunk-QODW5ILA.mjs → chunk-7HTWT3Z4.mjs} +2 -2
  36. package/dist/cli/chunk-7I4B6V2S.mjs +127 -0
  37. package/dist/cli/{chunk-MZSLFWOT.js → chunk-7UFFWPQ2.js} +13 -9
  38. package/dist/cli/{chunk-23EEVP5W.js → chunk-A5LPDB6N.js} +6 -6
  39. package/dist/cli/{chunk-NJMYX2TP.mjs → chunk-ABBP5ZCI.mjs} +3 -3
  40. package/dist/cli/{chunk-347EE4YE.js → chunk-AQYSZYNE.js} +8 -8
  41. package/dist/cli/{chunk-RGNMMDSJ.js → chunk-AYW242XF.js} +37 -37
  42. package/dist/cli/{chunk-6IJJCRE5.mjs → chunk-BLAZVZVD.mjs} +23 -5
  43. package/dist/cli/{chunk-ZDPL5AIK.js → chunk-D2LKEQKI.js} +5 -5
  44. package/dist/cli/{chunk-RLCAECZH.mjs → chunk-F2SJYUBW.mjs} +5 -5
  45. package/dist/cli/{chunk-XQLYPHYW.mjs → chunk-F44O4PEP.mjs} +1 -1
  46. package/dist/cli/{chunk-7CILZKLE.js → chunk-HMUBLAT4.js} +5 -5
  47. package/dist/cli/{chunk-UYA7LJGC.mjs → chunk-IL5AF6AR.mjs} +4 -2
  48. package/dist/cli/{chunk-4RAENEAX.js → chunk-IWYWZXCA.js} +53 -53
  49. package/dist/cli/{chunk-2QT6KN54.js → chunk-J3ZYRN5L.js} +10 -10
  50. package/dist/cli/{chunk-RHIFH3GD.mjs → chunk-JH775FF6.mjs} +1 -1
  51. package/dist/cli/{chunk-ACDXGOXE.js → chunk-KBBZXWXF.js} +63 -11
  52. package/dist/cli/{chunk-LJYTYNQZ.mjs → chunk-M5USVQ33.mjs} +55 -3
  53. package/dist/cli/{chunk-2GZFIWSA.mjs → chunk-N573OK4W.mjs} +4 -4
  54. package/dist/cli/{chunk-WFVEMDQL.js → chunk-OKAZHDYN.js} +4 -2
  55. package/dist/cli/{chunk-DFKHXZQ7.mjs → chunk-PHBOI24Y.mjs} +10 -10
  56. package/dist/cli/{chunk-WRHLAAGN.mjs → chunk-PLJV6SX4.mjs} +1 -1
  57. package/dist/cli/{chunk-C46DWB56.mjs → chunk-PPHHPAQV.mjs} +1 -1
  58. package/dist/cli/{chunk-UBNS6GSV.js → chunk-PZHRNSMU.js} +21 -21
  59. package/dist/cli/{chunk-KMXBZIKO.mjs → chunk-QB3Q73LP.mjs} +2 -2
  60. package/dist/cli/{chunk-255O6APS.js → chunk-QVZNUQAP.js} +2 -2
  61. package/dist/cli/{chunk-6KKDIISC.js → chunk-S3BKOCVB.js} +1 -1
  62. package/dist/cli/{chunk-JS4UO6G4.mjs → chunk-S7UEKKOX.mjs} +2 -1
  63. package/dist/cli/{chunk-KWQNNPMH.js → chunk-SCHE5QRN.js} +3 -3
  64. package/dist/cli/{chunk-E2CUSH56.js → chunk-SXY4DOVA.js} +16 -16
  65. package/dist/cli/{chunk-SVU2VR6Q.mjs → chunk-TLF3KGWG.mjs} +2 -2
  66. package/dist/cli/{chunk-LXKGZKTL.js → chunk-TQWI7LJX.js} +26 -26
  67. package/dist/cli/{chunk-NC52JG66.js → chunk-TW3TTKW5.js} +3 -3
  68. package/dist/cli/{chunk-R5FNCFEK.js → chunk-UXQVSDFP.js} +3 -3
  69. package/dist/cli/{chunk-DMWHEYZM.js → chunk-V7I65ME7.js} +2 -2
  70. package/dist/cli/{chunk-CH4VMDB5.js → chunk-VE25VUTE.js} +70 -52
  71. package/dist/cli/{chunk-3W6E3WVU.js → chunk-XFY3JOP2.js} +9 -9
  72. package/dist/cli/{chunk-75NLWBYQ.js → chunk-XOORR6P3.js} +93 -93
  73. package/dist/cli/chunk-YGCKMI5P.js +127 -0
  74. package/dist/cli/{chunk-3A7TDUZV.mjs → chunk-ZBSUXXZS.mjs} +13 -9
  75. package/dist/cli/{chunk-DS3DZUES.mjs → chunk-ZSU3C4ST.mjs} +2 -2
  76. package/dist/cli/command-docs.js +2 -2
  77. package/dist/cli/command-docs.mjs +1 -1
  78. package/dist/cli/{csv-IDRPTSSC.mjs → csv-3SNWBO5F.mjs} +4 -4
  79. package/dist/cli/{csv-ECB7TYR2.js → csv-LAYTDYIF.js} +13 -13
  80. package/dist/cli/{db-FOBZD44N.mjs → db-72JKGQSK.mjs} +6 -6
  81. package/dist/cli/{db-S74XZZ77.js → db-OOUOSURL.js} +27 -27
  82. package/dist/cli/describe-7HYV44MO.js +48 -0
  83. package/dist/cli/{describe-KMTPPI2J.mjs → describe-KDPNOEUJ.mjs} +17 -17
  84. package/dist/cli/describe-core.d.mts +3 -3
  85. package/dist/cli/describe-core.d.ts +3 -3
  86. package/dist/cli/{executions-4JAIVE2E.mjs → executions-O6JS3UKX.mjs} +6 -6
  87. package/dist/cli/{executions-ZE3JMNA4.js → executions-RJNMGJEE.js} +8 -8
  88. package/dist/cli/{feedback-W6VA47A3.js → feedback-FXB6NV3Y.js} +8 -8
  89. package/dist/cli/{feedback-EUX5PDBA.mjs → feedback-T3JH3CWC.mjs} +4 -4
  90. package/dist/cli/git-5LTKPXGN.js +25 -0
  91. package/dist/cli/{git-UOVG4PLH.mjs → git-6KAQSH5J.mjs} +5 -5
  92. package/dist/cli/index.js +161 -142
  93. package/dist/cli/index.mjs +65 -46
  94. package/dist/cli/{legacy-noops-ZBWUNFWV.mjs → legacy-noops-7HDGEGTK.mjs} +5 -5
  95. package/dist/cli/{legacy-noops-3H35EH5Y.js → legacy-noops-PAOGRKUK.js} +8 -8
  96. package/dist/cli/{monitors-PI3KPLR2.js → monitors-KQFI7TVJ.js} +91 -91
  97. package/dist/cli/{monitors-AYSGSUAK.mjs → monitors-LE2AYK3Z.mjs} +7 -7
  98. package/dist/cli/{org-MVS3Y6BK.js → org-J6XFHDJE.js} +72 -72
  99. package/dist/cli/{org-2BIG7U4V.mjs → org-Z4DMJ72I.mjs} +6 -6
  100. package/dist/cli/{pages-4TRRL24G.mjs → pages-5LYS6Y4I.mjs} +6 -6
  101. package/dist/cli/{pages-VB6FOVDE.js → pages-FIXBZQJG.js} +17 -17
  102. package/dist/cli/{play-BLT6IT2O.js → play-D7WPXADH.js} +585 -678
  103. package/dist/cli/{play-JR3VUOZM.mjs → play-GILXXIN7.mjs} +26 -119
  104. package/dist/cli/{providers-BBXIAGW4.js → providers-KN7YACT6.js} +9 -9
  105. package/dist/cli/{providers-547DPK4S.mjs → providers-OQWL4Q4W.mjs} +6 -6
  106. package/dist/cli/{quickstart-NTLXD7HS.mjs → quickstart-2LRIORFM.mjs} +4 -4
  107. package/dist/cli/{quickstart-RNPI5B6J.js → quickstart-M2OBP3W3.js} +6 -6
  108. package/dist/cli/{run-BCCEXBOG.js → run-6YZZO7VM.js} +36 -36
  109. package/dist/cli/{run-QZ4SFECD.mjs → run-GYRBKQZT.mjs} +22 -22
  110. package/dist/cli/search-OVKWA4AV.js +32 -0
  111. package/dist/cli/{search-PYNELH4C.mjs → search-VUPK6BNU.mjs} +7 -7
  112. package/dist/cli/{secrets-VD35LMYJ.js → secrets-RE3UMZB7.js} +15 -15
  113. package/dist/cli/{secrets-O5WGREQ7.mjs → secrets-ULRGU7NM.mjs} +6 -6
  114. package/dist/cli/{sessions-2TL6Z5J5.mjs → sessions-2ZZVJ3P2.mjs} +5 -5
  115. package/dist/cli/{sessions-NGGIBYXF.js → sessions-T4UMHNDW.js} +6 -6
  116. package/dist/cli/{settings-ERFNZRHO.js → settings-BRV6AYFM.js} +78 -78
  117. package/dist/cli/{settings-6KM74AJT.mjs → settings-PDY4TJU6.mjs} +11 -11
  118. package/dist/cli/{setup-Y3BN6XRS.mjs → setup-CXL6C5Z5.mjs} +9 -9
  119. package/dist/cli/{setup-6X7EJ6RG.js → setup-JONYJYBK.js} +44 -44
  120. package/dist/cli/{skills-OUBQASHI.mjs → skills-TOMVQD5O.mjs} +7 -7
  121. package/dist/cli/skills-UK5AK35I.js +33 -0
  122. package/dist/cli/{tools-EELZ5FF5.mjs → tools-B2LJK4MM.mjs} +16 -16
  123. package/dist/cli/tools-HH6E5G7J.js +55 -0
  124. package/dist/cli/{update-TX7SW32Y.mjs → update-CD6T2REC.mjs} +9 -9
  125. package/dist/cli/update-EGXT3EIJ.js +33 -0
  126. package/dist/cli/{workflow-5AI2BJA7.js → workflow-PZML43E2.js} +17 -17
  127. package/dist/cli/{workflow-PUNQG7LG.mjs → workflow-ZAOBP6I7.mjs} +7 -7
  128. package/dist/{client-AjmfA5nR.d.mts → client-B1s1yJdT.d.mts} +1 -1
  129. package/dist/{client-DMyY-QL8.d.ts → client-DqUseRzd.d.ts} +1 -1
  130. package/dist/{client-core-C705vFyV.d.ts → client-core-Bd7tP9Uz.d.ts} +45 -6
  131. package/dist/{client-core-CpBbvLEX.d.mts → client-core-BijQEQ70.d.mts} +45 -6
  132. package/dist/{compiler-manifest-hqIAukR0.d.mts → compiler-manifest-B1m5tRzz.d.mts} +23 -17
  133. package/dist/{compiler-manifest-hqIAukR0.d.ts → compiler-manifest-B1m5tRzz.d.ts} +23 -17
  134. package/dist/{errors-CRO2qQ6k.d.ts → errors-3g71OMXy.d.ts} +1 -1
  135. package/dist/{errors-DIHVetNY.d.mts → errors-QiC6am-q.d.mts} +1 -1
  136. package/dist/{index-DVZ_X4Gb.d.mts → index-Biz5toO4.d.mts} +1 -1
  137. package/dist/{index-B8DVoeYt.d.ts → index-CIBiwzhK.d.ts} +1 -1
  138. package/dist/index.d.mts +15 -9
  139. package/dist/index.d.ts +15 -9
  140. package/dist/index.js +9 -9
  141. package/dist/index.mjs +6 -6
  142. package/dist/install-integrity.json +112 -110
  143. package/dist/plays/bundle-play-file.d.mts +3 -3
  144. package/dist/plays/bundle-play-file.d.ts +3 -3
  145. package/dist/plays/bundle-play-file.mjs +2 -1
  146. package/dist/plays/play-description.d.mts +2 -2
  147. package/dist/plays/play-description.d.ts +2 -2
  148. package/dist/plays/run-request.d.mts +2 -2
  149. package/dist/plays/run-request.d.ts +2 -2
  150. package/dist/release.d.mts +1 -1
  151. package/dist/release.d.ts +1 -1
  152. package/dist/release.js +2 -2
  153. package/dist/release.mjs +1 -1
  154. package/dist/server.d.mts +4 -4
  155. package/dist/server.d.ts +4 -4
  156. package/dist/server.js +5 -5
  157. package/dist/server.mjs +3 -3
  158. package/package.json +1 -1
  159. package/dist/cli/auth-QITP34OX.js +0 -41
  160. package/dist/cli/describe-PI7HCT7K.js +0 -48
  161. package/dist/cli/git-NDOGCQZY.js +0 -25
  162. package/dist/cli/search-YHEICYQL.js +0 -32
  163. package/dist/cli/skills-F7MPYC4P.js +0 -33
  164. package/dist/cli/tools-MCWDAK53.js +0 -55
  165. package/dist/cli/update-G5BRJOVT.js +0 -33
@@ -12,6 +12,7 @@ import type {
12
12
  PlaySettingsPatch,
13
13
  PlaySettingsPathsPage,
14
14
  } from '../../shared_libs/plays/settings-contract';
15
+ import type { PlaySandboxRuntimeDeclaration } from '../../shared_libs/plays/sandbox-runtime-limits';
15
16
  import type {
16
17
  PlayTriggerConfigUpdate,
17
18
  PlayTriggerConfigUpdateResult,
@@ -2419,6 +2420,8 @@ export type BillingRecentUsageEntry = {
2419
2420
  export type BillingUsageOptions = {
2420
2421
  /** Return the one exact call identified by `executeTool().job_id`. */
2421
2422
  requestId?: string;
2423
+ /** Return only the workspace identity and recent activity page. */
2424
+ recentOnly?: boolean;
2422
2425
  /** Bound the recent-call page. Ignored for an exact requestId lookup. */
2423
2426
  limit?: number;
2424
2427
  offset?: number;
@@ -2555,10 +2558,7 @@ export type BillingNamespace = {
2555
2558
 
2556
2559
  export type BillingQuoteRequest = {
2557
2560
  quantity: number;
2558
- sandbox_runtime: {
2559
- timeout?: string;
2560
- size?: 'standard' | 'large' | 'xlarge';
2561
- };
2561
+ sandbox_runtime: PlaySandboxRuntimeDeclaration;
2562
2562
  };
2563
2563
 
2564
2564
  export type BillingQuoteResult = {
@@ -6466,6 +6466,7 @@ export class DeeplineClient {
6466
6466
  }
6467
6467
  params.set('request_id', requestId);
6468
6468
  } else {
6469
+ if (options.recentOnly) params.set('recent_only', 'true');
6469
6470
  if (options.limit !== undefined) {
6470
6471
  params.set('recent_limit', String(options.limit));
6471
6472
  }
@@ -218,14 +218,95 @@ function mapNetworkError(
218
218
  code: 'NETWORK_ABORTED',
219
219
  };
220
220
  }
221
+ const causeCode = networkCauseCode(error);
221
222
  return {
222
- message: error?.message
223
- ? `Unable to connect to ${baseUrl}. ${error.message}`
224
- : `Unable to connect to ${baseUrl}. Is the computer able to access the url?`,
223
+ message: connectionFailureMessage(baseUrl, error),
225
224
  code: 'NETWORK_ERROR',
225
+ ...(causeCode ? { details: { causeCode } } : {}),
226
226
  };
227
227
  }
228
228
 
229
+ const BLOCKED_EGRESS_CAUSE_CODES = new Set([
230
+ 'EPERM',
231
+ 'EACCES',
232
+ 'ENOTFOUND',
233
+ 'EAI_AGAIN',
234
+ 'ENETUNREACH',
235
+ 'EHOSTUNREACH',
236
+ 'ETIMEDOUT',
237
+ 'UND_ERR_CONNECT_TIMEOUT',
238
+ ]);
239
+
240
+ /**
241
+ * The first system error code in a transport failure's cause chain. Node's
242
+ * fetch reports only "fetch failed"; the reason (ENOTFOUND, ECONNREFUSED,
243
+ * EPERM, ...) is on `cause`, or on `errors` when every address was tried.
244
+ */
245
+ export function networkCauseCode(error: unknown): string | undefined {
246
+ const seen = new Set<unknown>();
247
+ const queue: unknown[] = [error];
248
+ while (queue.length > 0) {
249
+ const current = queue.shift();
250
+ if (!current || typeof current !== 'object' || seen.has(current)) continue;
251
+ seen.add(current);
252
+ const record = current as Record<string, unknown>;
253
+ if (
254
+ typeof record.code === 'string' &&
255
+ /^[A-Z][A-Z0-9_]+$/.test(record.code)
256
+ )
257
+ return record.code;
258
+ queue.push(record.cause);
259
+ if (Array.isArray(record.errors)) queue.push(...record.errors);
260
+ }
261
+ return undefined;
262
+ }
263
+
264
+ function isLoopbackHost(hostname: string): boolean {
265
+ return (
266
+ hostname === 'localhost' ||
267
+ hostname === '[::1]' ||
268
+ hostname.startsWith('127.') ||
269
+ hostname.endsWith('.localhost')
270
+ );
271
+ }
272
+
273
+ /**
274
+ * One line naming the host, the transport error, its underlying cause code,
275
+ * and — when the cause is how blocked egress presents — what to check.
276
+ */
277
+ export function connectionFailureMessage(
278
+ baseUrl: string,
279
+ error: Error | null,
280
+ ): string {
281
+ if (!error?.message) {
282
+ return `Unable to connect to ${baseUrl}. Is the computer able to access the url?`;
283
+ }
284
+ const causeCode = networkCauseCode(error);
285
+ let hostname = '';
286
+ try {
287
+ hostname = new URL(baseUrl).hostname;
288
+ } catch {
289
+ // An unparseable base URL still reports the transport failure.
290
+ }
291
+ const blocked =
292
+ hostname !== '' &&
293
+ !isLoopbackHost(hostname) &&
294
+ causeCode !== undefined &&
295
+ (BLOCKED_EGRESS_CAUSE_CODES.has(causeCode) || causeCode === 'ECONNREFUSED');
296
+ // Without a code, the cause's own message is still more than "fetch failed".
297
+ const causeMessage =
298
+ error.cause instanceof Error ? error.cause.message.slice(0, 200) : '';
299
+ const cause = causeCode ?? causeMessage;
300
+ return [
301
+ `Unable to connect to ${baseUrl}. ${error.message}${cause && !error.message.includes(cause) ? ` (${cause})` : ''}.`,
302
+ ...(blocked
303
+ ? [
304
+ `A sandbox, firewall, or proxy may be blocking ${hostname}; allow outbound HTTPS to it and retry.`,
305
+ ]
306
+ : []),
307
+ ].join(' ');
308
+ }
309
+
229
310
  function describeRequestTarget(path: string): string {
230
311
  const toolExecuteMatch = path.match(
231
312
  /^\/api\/v2\/integrations\/([^/?#]+)\/execute(?:[/?#]|$)/,
@@ -3,6 +3,8 @@ import { HttpClient as HttpClientCore } from './http-core';
3
3
  import { readSdkSkillsLocalVersion } from './skills-version';
4
4
  import type { ResolvedConfig } from './types';
5
5
 
6
+ export { connectionFailureMessage, networkCauseCode } from './http-core';
7
+
6
8
  export class HttpClient extends HttpClientCore {
7
9
  constructor(config: ResolvedConfig) {
8
10
  super(config, readSdkSkillsLocalVersion);
@@ -185,8 +185,14 @@ export type PlayFetchResponse = PlayAuthoringFetchResponse;
185
185
  * The changed row is delivered as the listener event's `after` in the play function's input.
186
186
  *
187
187
  * The default Play runtime is 30 minutes with the `standard` 1 GiB / 1 CPU
188
- * profile. Select `large` (4 GiB / 2 CPU) or `xlarge` (8 GiB / 4 CPU)
189
- * through `runtime.size` when a step needs more memory.
188
+ * profile. Set `runtime.size` to `standard` or `1gb` (1 GiB / 1 CPU),
189
+ * `large` or `4gb` (4 GiB / 2 CPU), or `xlarge` or `8gb` (8 GiB / 4 CPU).
190
+ * Each alias selects the same resource profile and compute price.
191
+ *
192
+ * ```ts
193
+ * export const config = { runtime: { size: '4gb' } };
194
+ * ```
195
+ *
190
196
  * Compute pricing follows the workspace plan; use `deepline billing quote`
191
197
  * for the session ceiling.
192
198
  * Set `runtime.timeout` for longer bounded runs; duration values are whole
@@ -205,7 +205,7 @@ export const SDK_RELEASE = {
205
205
  // getters keep their established compatibility behavior.
206
206
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
207
207
  // 0.3.90 is the first deliberately versioned SDK release for API v3.
208
- version: '0.3.477',
208
+ version: '0.3.478',
209
209
  updateSummary: {
210
210
  introducedIn: '0.3.90',
211
211
  summary:
@@ -255,6 +255,24 @@
255
255
  "failureAlert": "github_workflow_run",
256
256
  "domainReportRequired": true
257
257
  },
258
+ {
259
+ "id": "github.prebuilt-benchmarks",
260
+ "name": "Prebuilt benchmarks",
261
+ "scheduler": "github_actions",
262
+ "cadence": "0 14 * * 1",
263
+ "timezone": "UTC",
264
+ "source": ".github/workflows/prebuilt-benchmarks.yml",
265
+ "locator": "schedule:",
266
+ "owner": "evals",
267
+ "criticality": "reporting",
268
+ "purpose": "Run each prebuilt play on its fixed labeled sample in a disposable Production workspace with the published CLI and publish found, exact, recall, precision and settled credits.",
269
+ "successDefinition": "Every benchmark exports its rows, scores them, settles within its credit cap, and the summary uploads; the disposable workspace is deleted.",
270
+ "failureDefinition": "Workspace bootstrap, dataset load, a play run, export, scoring, a credit-cap overrun, or workspace deletion fails.",
271
+ "startGraceMs": 600000,
272
+ "runTimeoutMs": 7200000,
273
+ "failureAlert": "github_workflow_run",
274
+ "domainReportRequired": true
275
+ },
258
276
  {
259
277
  "id": "github.fly-pr-preview-cleanup",
260
278
  "name": "Reconcile Fly PR Preview Capacity",
@@ -19656,11 +19656,19 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
19656
19656
  statusCode: 504,
19657
19657
  },
19658
19658
  ));
19659
+ // The gateway admits a recovery key only under the executor token's
19660
+ // run, which is the root run even inside an inline child. Root runs
19661
+ // keep their original key (executor run == logical run); an inline
19662
+ // child keeps its own logical identity inside the invocation key.
19663
+ const asyncRecoveryScope = this.currentExecutionScope;
19659
19664
  const asyncRecoveryKey = (attempt: number) =>
19660
19665
  asyncRecoveryInvocationKey
19661
19666
  ? buildRuntimeExecutionRecoveryKey({
19662
- parentRunId: this.currentRunId,
19663
- invocationKey: `${asyncRecoveryInvocationKey}:attempt:${attempt}`,
19667
+ parentRunId: asyncRecoveryScope.placement.executorRunId,
19668
+ invocationKey:
19669
+ asyncRecoveryScope.placement.kind === 'inline_child'
19670
+ ? `${asyncRecoveryInvocationKey}:inline-child:${asyncRecoveryScope.logical.runId}:attempt:${attempt}`
19671
+ : `${asyncRecoveryInvocationKey}:attempt:${attempt}`,
19664
19672
  })
19665
19673
  : null;
19666
19674
  // This is deliberately a claim about only the immediately preceding
@@ -1090,7 +1090,17 @@ export class AppRuntimeRateStateBackend implements RateStateBackend {
1090
1090
  : rawResponse.waitMs,
1091
1091
  }
1092
1092
  : rawResponse;
1093
- this.acknowledgeSuccessFeedback(successKey, feedback);
1093
+ const acknowledged = response.acknowledgedObservationBatch;
1094
+ const submitted = feedback.concurrencyObservationBatch;
1095
+ if (
1096
+ acknowledged === undefined ||
1097
+ (submitted &&
1098
+ acknowledged?.observerId === submitted.observerId &&
1099
+ acknowledged.laneId === submitted.laneId &&
1100
+ acknowledged.sequence === submitted.sequence)
1101
+ ) {
1102
+ this.acknowledgeSuccessFeedback(successKey, feedback);
1103
+ }
1094
1104
  if (response.successFeedbackSupported === true) {
1095
1105
  this.successFeedbackSupported.add(bucketId);
1096
1106
  } else {
@@ -92,6 +92,11 @@ export type PlayRunnerRateStateAcquireResult = {
92
92
  dispatchValidationSupported?: boolean;
93
93
  /** This receiver acknowledges replay-safe success batches on release. */
94
94
  successFeedbackSupported?: boolean;
95
+ /**
96
+ * Exact committed feedback identity; null leaves the batch pending for replay.
97
+ * Absent preserves implicit acknowledgement from older receivers.
98
+ */
99
+ acknowledgedObservationBatch?: PlayRunnerRateStateConcurrencyObservationBatch | null;
95
100
  /**
96
101
  * Gateway clock used to produce `scheduledAtMs`. Runners translate the
97
102
  * schedule into relative delays so cross-machine clock skew cannot compress
@@ -83,6 +83,23 @@ export type StagedDaytonaPayload = {
83
83
  startupDiagnosticPath: string;
84
84
  };
85
85
 
86
+ /** No payload or customer command has been sent when this is raised. */
87
+ export class DaytonaNodeUnavailableError extends Error {
88
+ constructor(
89
+ readonly diagnostic: {
90
+ reason: 'command_failed' | 'empty_output';
91
+ exitCode: number | null;
92
+ attempts: number;
93
+ responseBytes: number;
94
+ },
95
+ ) {
96
+ super(
97
+ `Daytona Node executable was not ready before payload staging (${diagnostic.reason}, exit ${diagnostic.exitCode ?? 'unknown'}).`,
98
+ );
99
+ this.name = 'DaytonaNodeUnavailableError';
100
+ }
101
+ }
102
+
86
103
  export type RemoteRunnerPayloadSandbox = {
87
104
  id: string;
88
105
  /** Daytona target (region) the sandbox runs in, when the provider reports it. */
@@ -129,10 +146,11 @@ function nodeMaterializePayloadCommand(input: {
129
146
  artifactCodePath: string;
130
147
  artifactSourceMapPath: string;
131
148
  crashPusherPath: string;
149
+ nodeBinaryPath?: string;
132
150
  }): string {
133
151
  const script = buildDaytonaPayloadMaterializerSource();
134
152
  return [
135
- 'node',
153
+ input.nodeBinaryPath ? shellQuote(input.nodeBinaryPath) : 'node',
136
154
  '-e',
137
155
  shellQuote(script),
138
156
  shellQuote(input.envelopePath),
@@ -583,6 +601,8 @@ export function withRunnerSandboxProvider(
583
601
  export async function stageRunnerPayload(input: {
584
602
  sandbox: RemoteRunnerPayloadSandbox;
585
603
  bundlePromise: Promise<string>;
604
+ /** A provider-resolved executable, independent of detached-shell PATH. */
605
+ nodeBinaryPath?: string;
586
606
  config: PlayRunnerExecutionConfig;
587
607
  /** Provider-selected burst ceiling; the stored profile remains a reservation. */
588
608
  memoryLimitMiB?: number;
@@ -736,8 +756,11 @@ export async function stageRunnerPayload(input: {
736
756
  typeof sandboxTarget === 'string' && sandboxTarget.trim()
737
757
  ? `DEEPLINE_SANDBOX_TARGET=${shellQuote(sandboxTarget.trim())} `
738
758
  : '';
759
+ const nodeCommand = input.nodeBinaryPath
760
+ ? shellQuote(input.nodeBinaryPath)
761
+ : 'node';
739
762
  const watchedRunnerCommand = [
740
- 'node',
763
+ nodeCommand,
741
764
  '-e',
742
765
  shellQuote(
743
766
  buildDaytonaRuntimeWatchdogSource(
@@ -766,6 +789,7 @@ export async function stageRunnerPayload(input: {
766
789
  artifactCodePath,
767
790
  artifactSourceMapPath,
768
791
  crashPusherPath,
792
+ nodeBinaryPath: input.nodeBinaryPath,
769
793
  })} && DEEPLINE_PLAY_RUNNER_RUNTIME_STARTED_PATH=${shellQuote(runtimeStartedPath)} DEEPLINE_PLAY_RUNNER_RUNTIME_COMPLETED_PATH=${shellQuote(runtimeCompletedPath)} DEEPLINE_PLAY_RUNNER_PROGRESS_EVENT_PATH=${shellQuote(progressEventPath)} DEEPLINE_PLAY_RUNNER_STARTUP_DIAGNOSTIC_PATH=${shellQuote(startupDiagnosticPath)} DEEPLINE_PLAY_RUNNER_STDOUT_EVENT_MODE=compact UV_THREADPOOL_SIZE=${PLAY_RUNNER_UV_THREADPOOL_SIZE} ${runtimeReliabilityEnv}${runnerTraceEnv}${sandboxTargetEnv}${watchedRunnerCommand}`;
770
794
  // Crash-containment epilogue: runs UNCONDITIONALLY after the runner exits and
771
795
  // pushes an unacknowledged parsed (or synthesized) terminal so the parked
@@ -775,7 +799,7 @@ export async function stageRunnerPayload(input: {
775
799
  // Its own retry diagnostics are kept in a separate log: appending them to
776
800
  // `outputPath` after a near-gateway-sized result could hide that result from
777
801
  // a later bounded-tail recovery read.
778
- const command = `rm -f ${shellQuote(outputPath)} ${shellQuote(crashPusherLogPath)} ${shellQuote(exitCodePath)} ${shellQuote(oomKillBaselinePath)} ${shellQuote(runnerPidPath)} ${shellQuote(runtimeStartedPath)} ${shellQuote(runtimeCompletedPath)} ${shellQuote(runtimeLimitMarkerPath)} ${shellQuote(terminationDiagnosticPath)} ${shellQuote(memoryLedgerPath)} ${shellQuote(`${memoryLedgerPath}.tmp`)} ${shellQuote(memoryPressurePath)} ${shellQuote(progressEventPath)} ${shellQuote(`${progressEventPath}.*`)} ${shellQuote(startupDiagnosticPath)}; ( awk '$1 == "oom_kill" { print $2 }' /sys/fs/cgroup/memory.events 2>/dev/null || true ) > ${shellQuote(oomKillBaselinePath)}; ( ${runnerCommand} ) > ${shellQuote(outputPath)} 2>&1; code=$?; printf '%s' "$code" > ${shellQuote(exitCodePath)}; ${runtimeReliabilityEnv}node ${shellQuote(crashPusherPath)} ${shellQuote(configPath)} "$code" ${shellQuote(outputPath)} ${shellQuote(runtimeLimitMarkerPath)} ${shellQuote(oomKillBaselinePath)} /sys/fs/cgroup/memory.events ${shellQuote(terminationDiagnosticPath)} ${shellQuote(runtimeCompletedPath)} > ${shellQuote(crashPusherLogPath)} 2>&1 || true; printf 'deepline runner output captured: %s\\n' ${shellQuote(outputPath)}; exit "$code"`;
802
+ const command = `rm -f ${shellQuote(outputPath)} ${shellQuote(crashPusherLogPath)} ${shellQuote(exitCodePath)} ${shellQuote(oomKillBaselinePath)} ${shellQuote(runnerPidPath)} ${shellQuote(runtimeStartedPath)} ${shellQuote(runtimeCompletedPath)} ${shellQuote(runtimeLimitMarkerPath)} ${shellQuote(terminationDiagnosticPath)} ${shellQuote(memoryLedgerPath)} ${shellQuote(`${memoryLedgerPath}.tmp`)} ${shellQuote(memoryPressurePath)} ${shellQuote(progressEventPath)} ${shellQuote(`${progressEventPath}.*`)} ${shellQuote(startupDiagnosticPath)}; ( awk '$1 == "oom_kill" { print $2 }' /sys/fs/cgroup/memory.events 2>/dev/null || true ) > ${shellQuote(oomKillBaselinePath)}; ( ${runnerCommand} ) > ${shellQuote(outputPath)} 2>&1; code=$?; printf '%s' "$code" > ${shellQuote(exitCodePath)}; ${runtimeReliabilityEnv}${nodeCommand} ${shellQuote(crashPusherPath)} ${shellQuote(configPath)} "$code" ${shellQuote(outputPath)} ${shellQuote(runtimeLimitMarkerPath)} ${shellQuote(oomKillBaselinePath)} /sys/fs/cgroup/memory.events ${shellQuote(terminationDiagnosticPath)} ${shellQuote(runtimeCompletedPath)} > ${shellQuote(crashPusherLogPath)} 2>&1 || true; printf 'deepline runner output captured: %s\\n' ${shellQuote(outputPath)}; exit "$code"`;
779
803
 
780
804
  return {
781
805
  workDir: input.workDir,
@@ -793,12 +817,61 @@ export async function stageRunnerPayload(input: {
793
817
  }
794
818
 
795
819
  export async function stageDaytonaRunnerPayload(
796
- input: Omit<Parameters<typeof stageRunnerPayload>[0], 'sandbox'> & {
820
+ input: Omit<
821
+ Parameters<typeof stageRunnerPayload>[0],
822
+ 'sandbox' | 'nodeBinaryPath'
823
+ > & {
797
824
  sandbox: DaytonaSandbox;
798
825
  },
799
826
  ): Promise<StagedDaytonaPayload> {
827
+ // Toolbox commands and detached sessions need not share PATH. Bind the
828
+ // provider's actual Node executable before constructing either launcher.
829
+ const nodeStartedAt = Date.now();
830
+ // A started sandbox may still be bringing its toolbox/runtime online. Retry
831
+ // only an unsuccessful or empty readiness read on this same sandbox before
832
+ // handing the pre-code failure back to the existing startup recovery owner.
833
+ const retryDelaysMs = [0, 150, 350] as const;
834
+ let node!: Awaited<ReturnType<DaytonaSandbox['process']['executeCommand']>>;
835
+ for (const [attempt, delayMs] of retryDelaysMs.entries()) {
836
+ if (delayMs) await new Promise((resolve) => setTimeout(resolve, delayMs));
837
+ node = await input.sandbox.process.executeCommand(
838
+ 'node -p process.execPath',
839
+ input.workDir,
840
+ {},
841
+ 5,
842
+ );
843
+ if (node.exitCode === 0 && node.result.trim() !== '') break;
844
+ const diagnostic = {
845
+ reason: node.exitCode === 0 ? 'empty_output' : 'command_failed',
846
+ exitCode:
847
+ typeof node.exitCode === 'number' && Number.isInteger(node.exitCode)
848
+ ? node.exitCode
849
+ : null,
850
+ attempts: attempt + 1,
851
+ responseBytes: Buffer.byteLength(node.result),
852
+ } satisfies DaytonaNodeUnavailableError['diagnostic'];
853
+ input.emitStage('runtime:node_unavailable', diagnostic);
854
+ if (attempt === retryDelaysMs.length - 1) {
855
+ throw new DaytonaNodeUnavailableError(diagnostic);
856
+ }
857
+ }
858
+ const nodeBinaryPath = node.result.trim();
859
+ if (
860
+ node.exitCode !== 0 ||
861
+ !nodeBinaryPath.startsWith('/') ||
862
+ /[\r\n\0]/.test(nodeBinaryPath) ||
863
+ nodeBinaryPath.length > 4_096
864
+ ) {
865
+ throw new Error(
866
+ 'Daytona did not confirm an absolute Node executable path for this sandbox.',
867
+ );
868
+ }
869
+ input.emitStage('runtime:node_resolved', {
870
+ elapsedMs: Date.now() - nodeStartedAt,
871
+ });
800
872
  return await stageRunnerPayload({
801
873
  ...input,
874
+ nodeBinaryPath,
802
875
  sandbox: {
803
876
  id: input.sandbox.id,
804
877
  target:
@@ -29,6 +29,18 @@ export type DetachedDaytonaStartupDiagnostic = {
29
29
  errorCode?: string;
30
30
  transportAttemptId?: string;
31
31
  } | null;
32
+ /** Fixed summaries only: raw runner output can contain customer secrets. */
33
+ launcherFailure?: {
34
+ reason:
35
+ | 'node_not_found'
36
+ | 'node_loader_failed'
37
+ | 'permission_denied'
38
+ | 'argument_list_too_long'
39
+ | 'exec_format_error'
40
+ | 'unknown_exit_127'
41
+ | 'output_unavailable';
42
+ nodeCheckExitCode: number;
43
+ };
32
44
  errors: string[];
33
45
  };
34
46
 
@@ -124,6 +136,7 @@ function summarizeProcesses(value: unknown): {
124
136
  * Process names are reduced to a Node count before logging; runner logs,
125
137
  * output, and arbitrary process names are deliberately excluded because no
126
138
  * heuristic redactor can guarantee removal of arbitrary customer secrets.
139
+ * Exit-127 shell failures are classified inside the sandbox into fixed enums.
127
140
  */
128
141
  export async function inspectDetachedDaytonaStartup(input: {
129
142
  sandbox: DaytonaSandbox;
@@ -131,6 +144,7 @@ export async function inspectDetachedDaytonaStartup(input: {
131
144
  cmdId: string;
132
145
  exitCodePath: string;
133
146
  startupDiagnosticPath?: string;
147
+ outputPath?: string;
134
148
  }): Promise<DetachedDaytonaStartupDiagnostic> {
135
149
  const errors: string[] = [];
136
150
  const diagnostic: DetachedDaytonaStartupDiagnostic = {
@@ -189,6 +203,70 @@ export async function inspectDetachedDaytonaStartup(input: {
189
203
  .catch(() => errors.push('process_snapshot_failed')),
190
204
  ]);
191
205
 
206
+ if (
207
+ input.outputPath &&
208
+ diagnostic.exitCodeFile === 127 &&
209
+ !diagnostic.runnerStartup
210
+ ) {
211
+ // The failed shell is retired immediately after this inspection. Classify
212
+ // its bounded output inside the sandbox, returning only known enum values
213
+ // and an exit code. Never ship arbitrary output through a heuristic redactor.
214
+ const quotedPath = "'" + input.outputPath.replace(/'/g, "'\\''") + "'";
215
+ try {
216
+ const result = await input.sandbox.process.executeCommand(
217
+ `
218
+ if [ -r ${quotedPath} ]; then
219
+ startup_tail="$(tail -c 4096 -- ${quotedPath} 2>/dev/null)"
220
+ case "$startup_tail" in
221
+ *"error while loading shared libraries"*) startup_reason=node_loader_failed ;;
222
+ *"not found: node"*|*node*"not found"*|*node*"No such file"*) startup_reason=node_not_found ;;
223
+ *"Permission denied"*) startup_reason=permission_denied ;;
224
+ *"Argument list too long"*) startup_reason=argument_list_too_long ;;
225
+ *"Exec format error"*) startup_reason=exec_format_error ;;
226
+ *) startup_reason=unknown_exit_127 ;;
227
+ esac
228
+ else
229
+ startup_reason=output_unavailable
230
+ fi
231
+ node --version >/dev/null 2>&1
232
+ startup_node_code=$?
233
+ printf '{"reason":"%s","nodeCheckExitCode":%s}\\n' "$startup_reason" "$startup_node_code"
234
+ `,
235
+ undefined,
236
+ {},
237
+ 5,
238
+ );
239
+ const value = JSON.parse(result.result) as Record<string, unknown>;
240
+ const reasons = [
241
+ 'node_not_found',
242
+ 'node_loader_failed',
243
+ 'permission_denied',
244
+ 'argument_list_too_long',
245
+ 'exec_format_error',
246
+ 'unknown_exit_127',
247
+ 'output_unavailable',
248
+ ];
249
+ if (
250
+ typeof value.reason !== 'string' ||
251
+ !reasons.includes(value.reason) ||
252
+ typeof value.nodeCheckExitCode !== 'number' ||
253
+ !Number.isInteger(value.nodeCheckExitCode) ||
254
+ value.nodeCheckExitCode < 0 ||
255
+ value.nodeCheckExitCode > 255
256
+ ) {
257
+ errors.push('launcher_diagnostic_invalid');
258
+ } else {
259
+ diagnostic.launcherFailure = {
260
+ reason: value.reason as NonNullable<
261
+ DetachedDaytonaStartupDiagnostic['launcherFailure']
262
+ >['reason'],
263
+ nodeCheckExitCode: value.nodeCheckExitCode,
264
+ };
265
+ }
266
+ } catch {
267
+ errors.push('launcher_diagnostic_unavailable');
268
+ }
269
+ }
192
270
  return diagnostic;
193
271
  }
194
272
 
@@ -225,6 +303,7 @@ export type DetachedDaytonaRunnerSupervisor = {
225
303
  command: string;
226
304
  exitCodePath: string;
227
305
  startupDiagnosticPath?: string;
306
+ outputPath?: string;
228
307
  }): Promise<Extract<DetachedDaytonaRunnerState, { phase: 'ready' }>>;
229
308
  };
230
309
 
@@ -394,7 +473,7 @@ export function prepareDetachedDaytonaRunner(input: {
394
473
  let started = false;
395
474
 
396
475
  return {
397
- async start({ command, exitCodePath, startupDiagnosticPath }) {
476
+ async start({ command, exitCodePath, startupDiagnosticPath, outputPath }) {
398
477
  if (started) {
399
478
  throw new Error(
400
479
  `Detached Daytona runner session ${input.sessionId} was already started.`,
@@ -435,6 +514,7 @@ export function prepareDetachedDaytonaRunner(input: {
435
514
  cmdId: state.cmdId,
436
515
  exitCodePath,
437
516
  startupDiagnosticPath,
517
+ outputPath,
438
518
  readiness: input.readiness,
439
519
  baselineHeartbeatAt: state.baselineHeartbeatAt,
440
520
  runControl: input.runControl,
@@ -461,6 +541,7 @@ export async function confirmDetachedDaytonaRunnerReady(input: {
461
541
  cmdId: string;
462
542
  exitCodePath: string;
463
543
  startupDiagnosticPath?: string;
544
+ outputPath?: string;
464
545
  readiness: DetachedRunnerReadinessPort;
465
546
  baselineHeartbeatAt: string | null;
466
547
  runControl?: <T>(
@@ -517,6 +598,7 @@ export async function confirmDetachedDaytonaRunnerReady(input: {
517
598
  cmdId: input.cmdId,
518
599
  exitCodePath: input.exitCodePath,
519
600
  startupDiagnosticPath: input.startupDiagnosticPath,
601
+ outputPath: input.outputPath,
520
602
  });
521
603
  const diagnostic = await (input.runControl
522
604
  ? input.runControl(inspect)
@@ -67,6 +67,7 @@ import {
67
67
  recoveryForRunnerStartupNotLive,
68
68
  } from '../../play-run-recovery-policy';
69
69
  import {
70
+ DaytonaNodeUnavailableError,
70
71
  stageDaytonaRunnerPayload,
71
72
  withRunnerSandboxProvider,
72
73
  } from './daytona-payload-transport';
@@ -1506,6 +1507,7 @@ const DAYTONA_UNANSWERED_REQUEST_CODES: ReadonlySet<string> = new Set([
1506
1507
  * loud too, because its input may be too large for any sandbox.
1507
1508
  */
1508
1509
  function isFailedDaytonaRunnerStart(error: unknown): boolean {
1510
+ if (error instanceof DaytonaNodeUnavailableError) return true;
1509
1511
  if (error instanceof DaytonaControlTimeoutError) return true;
1510
1512
  if (!isDaytonaProviderError(error)) return false;
1511
1513
  const status = daytonaErrorStatusForLog(error);
@@ -1536,6 +1538,9 @@ function startFailureForLog(error: unknown) {
1536
1538
  errorName: error instanceof Error ? error.name : null,
1537
1539
  providerStatus: daytonaErrorStatusForLog(error),
1538
1540
  providerCode: daytonaErrorCodeForLog(error),
1541
+ ...(error instanceof DaytonaNodeUnavailableError
1542
+ ? { nodeReadiness: error.diagnostic }
1543
+ : {}),
1539
1544
  ...(diagnostic
1540
1545
  ? {
1541
1546
  // Retain the already-sanitized evidence before sandbox retirement.
@@ -1563,6 +1568,7 @@ function startFailureForLog(error: unknown) {
1563
1568
  transportAttemptId: startup.transportAttemptId,
1564
1569
  }
1565
1570
  : null,
1571
+ launcherFailure: diagnostic.launcherFailure,
1566
1572
  inspectionErrorCount: diagnostic.errors.length,
1567
1573
  },
1568
1574
  }
@@ -2092,6 +2098,7 @@ export const daytonaPlayRunnerBackend: PlayRunnerBackend = {
2092
2098
  command: stagedPayload.command,
2093
2099
  exitCodePath: stagedPayload.exitCodePath,
2094
2100
  startupDiagnosticPath: stagedPayload.startupDiagnosticPath,
2101
+ outputPath: stagedPayload.outputPath,
2095
2102
  });
2096
2103
  } catch (error) {
2097
2104
  if (