deepline 0.3.473 → 0.3.474

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 (175) hide show
  1. package/dist/bundling-sources/sdk/src/client-core.ts +4 -1
  2. package/dist/bundling-sources/sdk/src/play.ts +7 -2
  3. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  4. package/dist/bundling-sources/shared_libs/integration-contracts/billing-entitlements.ts +3 -1
  5. package/dist/bundling-sources/shared_libs/observability/scheduled-jobs.json +18 -0
  6. package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-machines.ts +101 -2
  7. package/dist/bundling-sources/shared_libs/play-runtime/modal-runtime-config.ts +62 -20
  8. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-launch-transport.ts +2 -2
  9. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +24 -8
  10. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +19 -7
  11. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-runtime-watchdog.ts +196 -10
  12. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +40 -17
  13. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/local-process.ts +4 -1
  14. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/modal-resource-usage.ts +222 -0
  15. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/modal.ts +46 -14
  16. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/types.ts +2 -0
  17. package/dist/bundling-sources/shared_libs/play-runtime/runtime-incident-drills.ts +9 -5
  18. package/dist/bundling-sources/shared_libs/play-runtime/sandbox-compute-usage.ts +8 -0
  19. package/dist/bundling-sources/shared_libs/play-runtime/sandbox-runs/contract.ts +2 -0
  20. package/dist/bundling-sources/shared_libs/play-runtime/sandbox-runs/runner-backend-adapter.ts +2 -0
  21. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +5 -1
  22. package/dist/bundling-sources/shared_libs/play-runtime/worker-api-types.ts +36 -0
  23. package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +18 -8
  24. package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +1 -1
  25. package/dist/bundling-sources/shared_libs/plays/sandbox-runtime-limits.ts +64 -16
  26. package/dist/bundling-sources/shared_libs/plays/tool-result-types.ts +1 -1
  27. package/dist/bundling-sources/shared_libs/product-notifications/billing-sales.ts +14 -1
  28. package/dist/bundling-sources/shared_libs/product-notifications/catalog.ts +40 -0
  29. package/dist/bundling-sources/shared_libs/product-notifications/default-policy.ts +23 -1
  30. package/dist/bundling-sources/shared_libs/product-notifications/events.ts +18 -0
  31. package/dist/bundling-sources/shared_libs/product-notifications/slack-delivery.ts +2 -1
  32. package/dist/cli/auth-O6KAH543.js +41 -0
  33. package/dist/cli/{auth-KLJOQ4DZ.mjs → auth-QMANU5PV.mjs} +5 -5
  34. package/dist/cli/{billing-IQNJXKH5.js → billing-WZ5POBZX.js} +85 -83
  35. package/dist/cli/{billing-DNDYNBQ7.mjs → billing-YJAP4VZN.mjs} +12 -10
  36. package/dist/cli/{chunk-GTH7JORC.mjs → chunk-26CZVDQN.mjs} +2 -2
  37. package/dist/cli/{chunk-VGVSHQN5.mjs → chunk-35SZHTRW.mjs} +5 -5
  38. package/dist/cli/{chunk-GAWP2YF5.mjs → chunk-3MZPWIOE.mjs} +1 -1
  39. package/dist/cli/{chunk-B3BM3PAI.js → chunk-3NRZFMX4.js} +6 -6
  40. package/dist/cli/{chunk-63OOFT6B.js → chunk-3ODBCZLL.js} +93 -93
  41. package/dist/cli/{chunk-D4B3EV55.js → chunk-475V572K.js} +36 -36
  42. package/dist/cli/{chunk-36KEQFWZ.js → chunk-4C34LGI4.js} +10 -10
  43. package/dist/cli/{chunk-6ZTSHKJF.js → chunk-5QS4T4KR.js} +10 -3
  44. package/dist/cli/{chunk-GQBAJTQU.mjs → chunk-7IYOWIEJ.mjs} +2 -2
  45. package/dist/cli/{chunk-3J2N7YF5.js → chunk-AIPTW3EK.js} +3 -3
  46. package/dist/cli/{chunk-YHB6MASP.js → chunk-AWJKYS25.js} +2 -2
  47. package/dist/cli/{chunk-V25CXRQ7.mjs → chunk-BB4RZMWA.mjs} +5 -5
  48. package/dist/cli/{chunk-OHUGXZ7N.js → chunk-CXBEZAPU.js} +16 -16
  49. package/dist/cli/{chunk-Z52WEIBR.js → chunk-DKR7QPEO.js} +11 -7
  50. package/dist/cli/{chunk-X6R5UNDZ.mjs → chunk-DQZBERDL.mjs} +1 -1
  51. package/dist/cli/{chunk-YUNPYX5T.mjs → chunk-DUGZKPRK.mjs} +10 -3
  52. package/dist/cli/{chunk-HQCTPAXP.mjs → chunk-EIKONWW7.mjs} +1 -1
  53. package/dist/cli/{chunk-MREO5LZD.js → chunk-EK2TAZJZ.js} +2 -2
  54. package/dist/cli/{chunk-RVRG57OB.js → chunk-G3UHAYRX.js} +8 -8
  55. package/dist/cli/{chunk-3LFSIUFI.js → chunk-IK3VOGDR.js} +21 -21
  56. package/dist/cli/{chunk-IC6AHOYS.js → chunk-JCNJQH6X.js} +1 -1
  57. package/dist/cli/{chunk-NTFM7JQW.mjs → chunk-JEYX3345.mjs} +3 -3
  58. package/dist/cli/{chunk-IFZ76E5H.mjs → chunk-JJVYUKLY.mjs} +1 -1
  59. package/dist/cli/{chunk-N4ZCMQ54.js → chunk-JSXMJSBE.js} +8 -8
  60. package/dist/cli/{chunk-QG3THSTN.mjs → chunk-KKWQ24VM.mjs} +2 -2
  61. package/dist/cli/{chunk-TUFQFTOC.js → chunk-LHOEWAVD.js} +53 -53
  62. package/dist/cli/{chunk-QD6IW2HP.mjs → chunk-MKSB2FTU.mjs} +4 -4
  63. package/dist/cli/{chunk-6YOGOXOJ.mjs → chunk-MNK4ZK2C.mjs} +10 -10
  64. package/dist/cli/{chunk-ABLEU3QI.mjs → chunk-MXUJVCKV.mjs} +4 -4
  65. package/dist/cli/{chunk-WGQKOLXH.js → chunk-N62MS3XJ.js} +6 -6
  66. package/dist/cli/{chunk-YH5SFKHJ.js → chunk-ORQYKA4I.js} +4 -4
  67. package/dist/cli/{chunk-4U3RNA3J.js → chunk-PGKELBIY.js} +51 -51
  68. package/dist/cli/{chunk-SWAYZST7.mjs → chunk-RGIQ5ZJX.mjs} +2 -2
  69. package/dist/cli/{chunk-UON3WCBD.js → chunk-RLQIGBFK.js} +3 -3
  70. package/dist/cli/{chunk-F4PUDYBY.js → chunk-RXITRC37.js} +3 -3
  71. package/dist/cli/{chunk-DD7E4ZVH.js → chunk-STLJYWUC.js} +7 -7
  72. package/dist/cli/{chunk-3OPQLFUK.mjs → chunk-THDXVSNL.mjs} +4 -4
  73. package/dist/cli/{chunk-TBLBYIKO.mjs → chunk-UGYLGT7B.mjs} +1 -1
  74. package/dist/cli/{chunk-CNCK2D6B.mjs → chunk-UKX4FESF.mjs} +2 -2
  75. package/dist/cli/{chunk-RZHUHL53.mjs → chunk-V4ILQ2DS.mjs} +11 -7
  76. package/dist/cli/{chunk-QI73EL5C.js → chunk-XNP7XTBN.js} +26 -26
  77. package/dist/cli/{chunk-PMJAGTSQ.js → chunk-XP7FAYLN.js} +5 -5
  78. package/dist/cli/{chunk-D22QIUE3.js → chunk-YFDTF2XC.js} +9 -9
  79. package/dist/cli/{chunk-GE5GPLNM.mjs → chunk-YK64UZXN.mjs} +1 -1
  80. package/dist/cli/{chunk-ZVLYUKJU.js → chunk-YKNULB7V.js} +5 -5
  81. package/dist/cli/{chunk-VS7JKS22.mjs → chunk-YVJ6IWVD.mjs} +1 -1
  82. package/dist/cli/{chunk-6QS7522K.mjs → chunk-YX762HFK.mjs} +2 -2
  83. package/dist/cli/{chunk-EUHTZV2E.mjs → chunk-Z27NSLPC.mjs} +1 -1
  84. package/dist/cli/{chunk-EVLUSXQG.mjs → chunk-ZGC2SUAA.mjs} +2 -2
  85. package/dist/cli/{chunk-GISBRE2H.mjs → chunk-ZRWQKTSX.mjs} +3 -3
  86. package/dist/cli/command-docs.js +2 -2
  87. package/dist/cli/command-docs.mjs +1 -1
  88. package/dist/cli/{csv-OVV3HWN4.js → csv-ENKCFCSM.js} +13 -13
  89. package/dist/cli/{csv-NCY5MWS2.mjs → csv-QCLQOIEA.mjs} +4 -4
  90. package/dist/cli/{db-BOELNDS4.js → db-5TLBZDT3.js} +27 -27
  91. package/dist/cli/{db-VPH6JHUD.mjs → db-P25LGP33.mjs} +6 -6
  92. package/dist/cli/describe-44L7OBGM.js +48 -0
  93. package/dist/cli/{describe-FFDCPYZH.mjs → describe-W2H5DHR6.mjs} +17 -17
  94. package/dist/cli/describe-core.d.mts +3 -3
  95. package/dist/cli/describe-core.d.ts +3 -3
  96. package/dist/cli/{executions-5GCCDENS.mjs → executions-VYZCYUBR.mjs} +6 -6
  97. package/dist/cli/{executions-RF44ANF5.js → executions-YHFAWJOJ.js} +8 -8
  98. package/dist/cli/{feedback-OI2A5L6F.mjs → feedback-K2FWYYLZ.mjs} +4 -4
  99. package/dist/cli/{feedback-NJM3V7QV.js → feedback-RJF3QSBB.js} +8 -8
  100. package/dist/cli/git-F3EI5DY5.js +25 -0
  101. package/dist/cli/{git-EOGSZ3T2.mjs → git-S3APUPLL.mjs} +5 -5
  102. package/dist/cli/index.js +139 -139
  103. package/dist/cli/index.mjs +41 -41
  104. package/dist/cli/{legacy-noops-5QM7XGGX.js → legacy-noops-3SWNRE7Z.js} +8 -8
  105. package/dist/cli/{legacy-noops-UYKK7AAB.mjs → legacy-noops-ICYZACTW.mjs} +5 -5
  106. package/dist/cli/{monitors-N3ITC5MP.mjs → monitors-DEZCEQLB.mjs} +7 -7
  107. package/dist/cli/{monitors-52GW2V4V.js → monitors-VRIWTRPG.js} +91 -91
  108. package/dist/cli/{org-KUZUG33W.mjs → org-IQW5ZIAO.mjs} +6 -6
  109. package/dist/cli/{org-ADF47NPV.js → org-NX752DTE.js} +72 -72
  110. package/dist/cli/{pages-C3S5WU3S.mjs → pages-5WQRVVKX.mjs} +6 -6
  111. package/dist/cli/{pages-H3H7ODBN.js → pages-YIBWYGVS.js} +17 -17
  112. package/dist/cli/{play-YWZWBG32.js → play-3EXNH3HD.js} +662 -601
  113. package/dist/cli/{play-SZETTQPV.mjs → play-WOMJXS4O.mjs} +110 -49
  114. package/dist/cli/{providers-V3BP7ZB2.js → providers-C2WYSR2Y.js} +9 -9
  115. package/dist/cli/{providers-FSCD35HB.mjs → providers-XDVPYRTS.mjs} +6 -6
  116. package/dist/cli/{quickstart-ICULVGAR.js → quickstart-7RABZF66.js} +6 -6
  117. package/dist/cli/{quickstart-SYZCT2WI.mjs → quickstart-YMSIL2TK.mjs} +4 -4
  118. package/dist/cli/{run-WUJSKGUM.js → run-5QVVWFGL.js} +36 -36
  119. package/dist/cli/{run-HA5B3AYQ.mjs → run-A4JD6TAR.mjs} +22 -22
  120. package/dist/cli/{search-A27AGYDV.mjs → search-2TOSF2G3.mjs} +7 -7
  121. package/dist/cli/search-772R7YHI.js +32 -0
  122. package/dist/cli/{secrets-2L4VRJ2F.mjs → secrets-BUN4ORE7.mjs} +6 -6
  123. package/dist/cli/{secrets-ODX56QBF.js → secrets-Q7UVOSNM.js} +15 -15
  124. package/dist/cli/{sessions-PRF36DHO.mjs → sessions-IAOORI6X.mjs} +5 -5
  125. package/dist/cli/{sessions-DVZ4FECP.js → sessions-NXQOQ4HX.js} +6 -6
  126. package/dist/cli/{settings-LUATIKJE.mjs → settings-BMI373AI.mjs} +11 -11
  127. package/dist/cli/{settings-RJT5DBWG.js → settings-WD4KSY5S.js} +78 -78
  128. package/dist/cli/{setup-FJSBKCLU.js → setup-3IA7BVPE.js} +44 -44
  129. package/dist/cli/{setup-IQXTYLRH.mjs → setup-55VIWAWR.mjs} +9 -9
  130. package/dist/cli/{skills-XJH4ZC5Y.mjs → skills-GKITMBTM.mjs} +7 -7
  131. package/dist/cli/skills-PUUTFMP3.js +31 -0
  132. package/dist/cli/tools-3WX7TUUG.js +55 -0
  133. package/dist/cli/{tools-IGXLZCKZ.mjs → tools-XHCFC7GP.mjs} +16 -16
  134. package/dist/cli/update-PC3CLODV.js +33 -0
  135. package/dist/cli/{update-AH7G5VSL.mjs → update-SWB24YKE.mjs} +9 -9
  136. package/dist/cli/{workflow-IQ2J7LWM.js → workflow-5QPMFI3Z.js} +17 -17
  137. package/dist/cli/{workflow-WOYSCWVD.mjs → workflow-XO4CKUA7.mjs} +7 -7
  138. package/dist/{client-C9bJ0QXu.d.mts → client-Bgl0_zXi.d.mts} +1 -1
  139. package/dist/{client-CuNH54w3.d.ts → client-_ah6IuhK.d.ts} +1 -1
  140. package/dist/{client-core-CSiXbe2n.d.mts → client-core-Bfsedm0_.d.mts} +44 -3
  141. package/dist/{client-core-C8Q6DKga.d.ts → client-core-C-ZF5vh9.d.ts} +44 -3
  142. package/dist/{compiler-manifest-Zwm9yqOA.d.mts → compiler-manifest-CCOqDGjU.d.mts} +12 -10
  143. package/dist/{compiler-manifest-Zwm9yqOA.d.ts → compiler-manifest-CCOqDGjU.d.ts} +12 -10
  144. package/dist/{errors-nEsqNLL8.d.mts → errors-BLccD7tG.d.mts} +1 -1
  145. package/dist/{errors-CsYwlpn4.d.ts → errors-Cy3kPnsi.d.ts} +1 -1
  146. package/dist/{index-CXF2jtza.d.mts → index-4-j44HuT.d.mts} +1 -1
  147. package/dist/{index-9pUZqkPH.d.ts → index-D5e4h1-H.d.ts} +1 -1
  148. package/dist/index.d.mts +14 -9
  149. package/dist/index.d.ts +14 -9
  150. package/dist/index.js +9 -9
  151. package/dist/index.mjs +6 -6
  152. package/dist/install-integrity.json +111 -110
  153. package/dist/plays/bundle-play-file.d.mts +3 -3
  154. package/dist/plays/bundle-play-file.d.ts +3 -3
  155. package/dist/plays/bundle-play-file.mjs +1 -1
  156. package/dist/plays/play-description.d.mts +2 -2
  157. package/dist/plays/play-description.d.ts +2 -2
  158. package/dist/plays/run-request.d.mts +2 -2
  159. package/dist/plays/run-request.d.ts +2 -2
  160. package/dist/release.d.mts +1 -1
  161. package/dist/release.d.ts +1 -1
  162. package/dist/release.js +2 -2
  163. package/dist/release.mjs +1 -1
  164. package/dist/server.d.mts +4 -4
  165. package/dist/server.d.ts +4 -4
  166. package/dist/server.js +5 -5
  167. package/dist/server.mjs +3 -3
  168. package/package.json +1 -1
  169. package/dist/cli/auth-IJOWKNSB.js +0 -41
  170. package/dist/cli/describe-7SFESEFL.js +0 -48
  171. package/dist/cli/git-4V5ZS5SE.js +0 -25
  172. package/dist/cli/search-XATDPNEM.js +0 -32
  173. package/dist/cli/skills-N6CPBF6T.js +0 -31
  174. package/dist/cli/tools-YMVEVRCE.js +0 -55
  175. package/dist/cli/update-MUCWGTXH.js +0 -33
@@ -2555,7 +2555,10 @@ export type BillingNamespace = {
2555
2555
 
2556
2556
  export type BillingQuoteRequest = {
2557
2557
  quantity: number;
2558
- sandbox_runtime: { timeout?: string; size?: 'standard' };
2558
+ sandbox_runtime: {
2559
+ timeout?: string;
2560
+ size?: 'standard' | 'large' | 'xlarge';
2561
+ };
2559
2562
  };
2560
2563
 
2561
2564
  export type BillingQuoteResult = {
@@ -184,8 +184,13 @@ export type PlayFetchResponse = PlayAuthoringFetchResponse;
184
184
  * `deepline describe <id>` for a tool's streams and row columns).
185
185
  * The changed row is delivered as the listener event's `after` in the play function's input.
186
186
  *
187
- * The default Play runtime is 30 minutes. For bounded long-running batches, add
188
- * `runtime: { timeout: '90m', size: 'standard' }`; duration values are whole minutes or hours, up to `4h`.
187
+ * The default Play runtime is 30 minutes with the `standard` 1 GiB / 1 CPU
188
+ * profile. Preview deployments also support `large` (4 GiB / 2 CPU) and
189
+ * `xlarge` (8 GiB / 4 CPU) through `runtime.size`. Production uses `standard`.
190
+ * Compute pricing follows the workspace plan; use `deepline billing quote`
191
+ * for the session ceiling.
192
+ * Set `runtime.timeout` for longer bounded runs; duration values are whole
193
+ * minutes or hours, up to `4h`.
189
194
  * It differs from `deepline.tools.execute({ timeoutMs })`, which limits one provider call.
190
195
  *
191
196
  * @example Webhook with HMAC verification. `secretEnv` is the Deepline Secret
@@ -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.473',
208
+ version: '0.3.474',
209
209
  updateSummary: {
210
210
  introducedIn: '0.3.90',
211
211
  summary:
@@ -74,7 +74,7 @@ export type BillingEntitlements = {
74
74
  export type IncludedCompute = {
75
75
  /** `unavailable`: the workspace is outside the November pricing rollout. */
76
76
  status: 'on' | 'off' | 'unavailable';
77
- /** Credits per minute of sandbox runtime in force; null at provider cost. */
77
+ /** Credits per standard-equivalent sandbox minute; null at provider cost. */
78
78
  credits_per_minute: number | null;
79
79
  included_minutes: number;
80
80
  /** What turning included compute on gives this workspace's plan. */
@@ -83,6 +83,8 @@ export type IncludedCompute = {
83
83
  period_end_at: string;
84
84
  resets_on: 'calendar_month' | 'billing_cycle';
85
85
  included_minutes_used: number;
86
+ /** Exact Deepline credits consumed from included compute; older servers may omit it. */
87
+ included_credits_used?: number;
86
88
  minutes_used: number;
87
89
  days: Array<{ day: string; minutes: number }>;
88
90
  };
@@ -946,6 +946,24 @@
946
946
  "failureAlert": "convex_action_wrapper",
947
947
  "domainReportRequired": true
948
948
  },
949
+ {
950
+ "id": "convex.observe-large-usage-charges",
951
+ "name": "Observe unusually large usage charges",
952
+ "scheduler": "convex_scheduled_action",
953
+ "cadence": "runAfter(0) from each consumed positive usage-notification batch",
954
+ "timezone": "UTC",
955
+ "source": "packages/convex/scheduledJobsNode.ts",
956
+ "locator": "observeLargeUsageCharges",
957
+ "owner": "billing",
958
+ "criticality": "critical",
959
+ "purpose": "Observe committed individual charges against their tool thresholds, including cumulative async settlements.",
960
+ "successDefinition": "The bounded source batch and each billing-owner total are read independently, and any threshold breach is reported.",
961
+ "failureDefinition": "Committed usage or its billing-owner total cannot be observed; failure remains visible for operator recovery.",
962
+ "startGraceMs": 120000,
963
+ "runTimeoutMs": 1800000,
964
+ "failureAlert": "convex_action_wrapper",
965
+ "domainReportRequired": true
966
+ },
949
967
  {
950
968
  "id": "convex.credit-balance-auto-recharge-trigger",
951
969
  "name": "Credit-balance automatic recharge trigger",
@@ -657,6 +657,20 @@ function assertMinSecretsVersion(value: unknown): asserts value is number {
657
657
  }
658
658
  }
659
659
 
660
+ export type FlyMachinePhaseEvent = Readonly<{
661
+ phase:
662
+ | 'machine-create'
663
+ | 'machine-update'
664
+ | 'machine-start'
665
+ | 'readiness-read';
666
+ component: 'worker';
667
+ source: 'machines-api';
668
+ startedAtMs: number;
669
+ finishedAtMs: number;
670
+ elapsedMs: number;
671
+ outcome: 'success' | 'error';
672
+ }>;
673
+
660
674
  export class FlyMachinesClient {
661
675
  constructor(
662
676
  private readonly app: string,
@@ -666,6 +680,11 @@ export class FlyMachinesClient {
666
680
  wait?: (ms: number) => Promise<void>;
667
681
  retryDelaysMs?: readonly number[];
668
682
  requestTimeoutMs?: number;
683
+ /** Preview CLI diagnostics only; no provider arguments or bodies. */
684
+ phaseTiming?: Readonly<{
685
+ now?: () => number;
686
+ report?: (event: FlyMachinePhaseEvent) => void;
687
+ }>;
669
688
  }> = {},
670
689
  ) {}
671
690
 
@@ -674,6 +693,67 @@ export class FlyMachinesClient {
674
693
  path: string,
675
694
  body?: unknown,
676
695
  leaseNonce?: string,
696
+ ): Promise<unknown> {
697
+ const timing = this.rateLimitOptions.phaseTiming;
698
+ const phase: FlyMachinePhaseEvent['phase'] | undefined =
699
+ method === 'GET' && /^\/machines(?:\/[^/]+)?$/.test(path)
700
+ ? 'readiness-read'
701
+ : method === 'POST' && path === '/machines'
702
+ ? 'machine-create'
703
+ : method === 'POST' && /^\/machines\/[^/]+$/.test(path)
704
+ ? 'machine-update'
705
+ : method === 'POST' && /^\/machines\/[^/]+\/start$/.test(path)
706
+ ? 'machine-start'
707
+ : undefined;
708
+ if (!timing || !phase)
709
+ return this.requestUntimed(method, path, body, leaseNonce);
710
+ const now = timing.now ?? Date.now;
711
+ let startedAtMs: number;
712
+ try {
713
+ startedAtMs = now();
714
+ } catch {
715
+ this.reportTimingUnavailable();
716
+ return this.requestUntimed(method, path, body, leaseNonce);
717
+ }
718
+ let outcome: FlyMachinePhaseEvent['outcome'] = 'error';
719
+ try {
720
+ const result = await this.requestUntimed(method, path, body, leaseNonce);
721
+ outcome = 'success';
722
+ return result;
723
+ } finally {
724
+ // Diagnostic failures cannot change a provider outcome or retry an effect.
725
+ try {
726
+ const finishedAtMs = now();
727
+ const event: FlyMachinePhaseEvent = {
728
+ phase,
729
+ component: 'worker',
730
+ source: 'machines-api',
731
+ startedAtMs,
732
+ finishedAtMs,
733
+ elapsedMs: finishedAtMs - startedAtMs,
734
+ outcome,
735
+ };
736
+ if (timing.report) timing.report(event);
737
+ else console.error(`[runtime-fly-phase] ${JSON.stringify(event)}`);
738
+ } catch {
739
+ this.reportTimingUnavailable();
740
+ }
741
+ }
742
+ }
743
+
744
+ private reportTimingUnavailable(): void {
745
+ try {
746
+ console.error('[runtime-fly-phase] timing_unavailable');
747
+ } catch {
748
+ // A closed diagnostic stream must preserve the original provider result.
749
+ }
750
+ }
751
+
752
+ private async requestUntimed(
753
+ method: string,
754
+ path: string,
755
+ body?: unknown,
756
+ leaseNonce?: string,
677
757
  ): Promise<unknown> {
678
758
  const retryDelaysMs =
679
759
  this.rateLimitOptions.retryDelaysMs ??
@@ -1458,6 +1538,23 @@ export async function replaceLane(input: {
1458
1538
  return replacements;
1459
1539
  }
1460
1540
 
1541
+ function previewMachinePhaseTiming(
1542
+ flags: Record<string, string | boolean>,
1543
+ ): Readonly<{ report: (event: FlyMachinePhaseEvent) => void }> | undefined {
1544
+ if (flags.environment !== 'preview') return undefined;
1545
+ const output =
1546
+ flags['phase-timings-file'] === undefined
1547
+ ? undefined
1548
+ : requireStringFlag(flags, 'phase-timings-file');
1549
+ return {
1550
+ report(event) {
1551
+ const record = JSON.stringify(event);
1552
+ if (output) appendFileSync(output, `${record}\n`);
1553
+ console.error(`[runtime-fly-phase] ${record}`);
1554
+ },
1555
+ };
1556
+ }
1557
+
1461
1558
  async function cmdBoot(flags: Record<string, string | boolean>): Promise<void> {
1462
1559
  const app = resolveApp(flags);
1463
1560
  const releaseId = requireReleaseId(flags);
@@ -1505,6 +1602,7 @@ async function cmdBoot(flags: Record<string, string | boolean>): Promise<void> {
1505
1602
  app,
1506
1603
  requireFlyToken(),
1507
1604
  globalThis.fetch as FetchLike,
1605
+ { phaseTiming: previewMachinePhaseTiming(flags) },
1508
1606
  );
1509
1607
  await bootLane({
1510
1608
  client,
@@ -1549,6 +1647,7 @@ async function cmdReplace(
1549
1647
  app,
1550
1648
  requireFlyToken(),
1551
1649
  globalThis.fetch as FetchLike,
1650
+ { phaseTiming: previewMachinePhaseTiming(flags) },
1552
1651
  );
1553
1652
  await replaceLane({
1554
1653
  client,
@@ -2214,8 +2313,8 @@ const USAGE = `fly-absurd-lane <command> [flags]
2214
2313
  Commands:
2215
2314
  push-image --sha <sha> --release <id> [--app <app>]
2216
2315
  plan-image --release <id> [--image-ref <ref>] [--app <app>]
2217
- boot --release <id> --sha <sha> --count <K> [--image-ref <ref>] [--reuse-existing] [--component-input-hash <sha256>] [--rollback-hold-until-ms <ms>] [--app <app>] [--region <r>]
2218
- replace --release <id> --sha <sha> --count <K> [--image-ref <ref>] --environment preview [--component-input-hash <sha256>] [--app <app>] [--region <r>]
2316
+ boot --release <id> --sha <sha> --count <K> [--image-ref <ref>] [--phase-timings-file <path>] [--reuse-existing] [--component-input-hash <sha256>] [--rollback-hold-until-ms <ms>] [--app <app>] [--region <r>]
2317
+ replace --release <id> --sha <sha> --count <K> [--image-ref <ref>] [--phase-timings-file <path>] --environment preview [--component-input-hash <sha256>] [--app <app>] [--region <r>]
2219
2318
  list [--release <id>] [--json] [--app <app>]
2220
2319
  drain --release <id> [--if-present] [--app <app>]
2221
2320
  stop-claiming --release <id> [--environment production|preview]
@@ -1,8 +1,12 @@
1
1
  import { AsyncLocalStorage } from 'node:async_hooks';
2
2
  import type { ModalClient } from 'modal';
3
- import { isIsolatedRuntimeSchedulerSchema } from './runtime-scheduler-topology';
3
+ import {
4
+ isIsolatedRuntimeSchedulerSchema,
5
+ normalizeRuntimeSchedulerSchema,
6
+ } from './runtime-scheduler-topology';
4
7
  import {
5
8
  STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS,
9
+ modalSandboxMemoryLimitGiBForPlay,
6
10
  validatePlaySandboxRuntimeLimits,
7
11
  type PlaySandboxRuntimeLimits,
8
12
  } from '../plays/sandbox-runtime-limits';
@@ -10,9 +14,47 @@ import {
10
14
  export const MODAL_RUNNER_APP_NAME = 'deepline-play-runner';
11
15
  export const MODAL_RUNNER_IMAGE = 'node:20-bookworm-slim';
12
16
  export const MODAL_RUNNER_WORKDIR = '/root/deepline';
13
- export { MODAL_SANDBOX_CPU_CORES } from './worker-api-types';
17
+ export {
18
+ MODAL_SANDBOX_CPU_CORES,
19
+ modalSandboxResourcesForPlay,
20
+ } from './worker-api-types';
14
21
  export const MODAL_SANDBOX_MEMORY_MIB = 1024;
15
22
 
23
+ export function isModalPreviewEvaluation(input: {
24
+ runtimeSchedulerSchema: string | null | undefined;
25
+ env?: NodeJS.ProcessEnv;
26
+ }): boolean {
27
+ const schema = normalizeRuntimeSchedulerSchema(input.runtimeSchedulerSchema);
28
+ return (
29
+ (input.env ?? process.env).DEEPLINE_RUNTIME_ENVIRONMENT === 'preview' &&
30
+ schema !== null &&
31
+ isIsolatedRuntimeSchedulerSchema(schema)
32
+ );
33
+ }
34
+
35
+ /**
36
+ * Reservation and burst ceiling are separate provider facts. Keep the launch's
37
+ * profile unchanged: it still selects guaranteed resources. Preview evaluates
38
+ * headroom before any Production rollout. Preview workers and a valid isolated
39
+ * scheduler schema are both required; absence keeps the Production policy.
40
+ */
41
+ export function resolveModalSandboxMemoryPolicy(input: {
42
+ limits: PlaySandboxRuntimeLimits;
43
+ runtimeSchedulerSchema: string | null | undefined;
44
+ env?: NodeJS.ProcessEnv;
45
+ }): { reservationMemoryMiB: number; memoryLimitMiB: number } {
46
+ const limits = validatePlaySandboxRuntimeLimits(input.limits);
47
+ const reservationMemoryMiB = limits.memoryGiB * 1024;
48
+ const enableBurst = isModalPreviewEvaluation(input);
49
+ // The retained buffered Node/Python transport completes in 4 GiB. Larger
50
+ // reservations can burst to the existing product ceiling, without increasing
51
+ // their CPU reservation or paying for unused memory headroom.
52
+ const memoryLimitMiB = enableBurst
53
+ ? modalSandboxMemoryLimitGiBForPlay(limits) * 1024
54
+ : reservationMemoryMiB;
55
+ return { reservationMemoryMiB, memoryLimitMiB };
56
+ }
57
+
16
58
  export type ModalRequiredConfig = {
17
59
  client: ModalClient;
18
60
  appName: string;
@@ -105,24 +147,24 @@ export function withModalCallSignal<T>(
105
147
 
106
148
  // Modal runs custom middleware outside its own retry loop, so the signal also
107
149
  // ends a retry or throttle wait.
108
- const cancelModalCallsWithScopedSignal: ModalGrpcMiddleware =
109
- async function* (call, options) {
110
- const signal = modalCallSignal.getStore();
111
- // Every caller shares one auth token refresh, so one read's deadline must
112
- // not cancel it.
113
- if (
114
- !signal ||
115
- call.method.path === '/modal.client.ModalClient/AuthTokenGet'
116
- ) {
117
- return yield* call.next(call.request, options);
118
- }
119
- return yield* call.next(call.request, {
120
- ...options,
121
- signal: options.signal
122
- ? AbortSignal.any([options.signal, signal])
123
- : signal,
124
- });
125
- };
150
+ const cancelModalCallsWithScopedSignal: ModalGrpcMiddleware = async function* (
151
+ call,
152
+ options,
153
+ ) {
154
+ const signal = modalCallSignal.getStore();
155
+ // Every caller shares one auth token refresh, so one read's deadline must
156
+ // not cancel it.
157
+ if (
158
+ !signal ||
159
+ call.method.path === '/modal.client.ModalClient/AuthTokenGet'
160
+ ) {
161
+ return yield* call.next(call.request, options);
162
+ }
163
+ return yield* call.next(call.request, {
164
+ ...options,
165
+ signal: options.signal ? AbortSignal.any([options.signal, signal]) : signal,
166
+ });
167
+ };
126
168
 
127
169
  let sharedModalClient: {
128
170
  tokenId: string;
@@ -12,12 +12,12 @@ import {
12
12
  DaytonaConflictError,
13
13
  DaytonaRateLimitError,
14
14
  DaytonaValidationError,
15
- type CreateSandboxFromSnapshotParams,
16
15
  } from '@daytonaio/sdk';
17
16
  import axios, { type InternalAxiosRequestConfig } from 'axios';
18
17
  import { Readable } from 'node:stream';
19
18
  import { setTimeout as delay } from 'node:timers/promises';
20
19
  import type { DaytonaClientOptions } from '../../daytona-runtime-config';
20
+ import type { CreateSandboxFromSnapshotParams } from '@daytonaio/sdk';
21
21
  import type { DaytonaDeleteRetryAuthorization } from './daytona-lifecycle';
22
22
  import {
23
23
  DAYTONA_CONTROL_TIMEOUT_MS,
@@ -429,7 +429,7 @@ function deadline(timeoutMs: number, signal?: AbortSignal) {
429
429
  };
430
430
  }
431
431
 
432
- /** Deepline's default-snapshot launch contract; image/build orchestration stays in the SDK. */
432
+ /** Deepline's snapshot launch contract; image/build orchestration stays in the SDK. */
433
433
  export function createDaytonaLaunchClient(
434
434
  options: DaytonaClientOptions,
435
435
  cancellation?: AbortSignal,
@@ -20,8 +20,10 @@ import { isIsolatedRuntimeSchedulerSchema } from '../../runtime-scheduler-topolo
20
20
  import { PLAY_RUNNER_TIMEOUT_SECONDS } from '../../runtime-constants';
21
21
  import {
22
22
  STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS,
23
+ playSandboxSizeForLimits,
23
24
  validatePlaySandboxRuntimeLimits,
24
25
  } from '../../../plays/sandbox-runtime-limits';
26
+ import type { PlaySandboxSize } from '../../../plays/sandbox-runtime-limits';
25
27
  import type {
26
28
  PlayRunnerDaytonaCleanupIntent,
27
29
  PlayRunnerRuntimeLifecycleEvent,
@@ -65,8 +67,15 @@ const DAYTONA_ABSENT_OPERATION_RECREATE_AFTER_MS = 3 * 60_000;
65
67
  // inactivity stop is a wider crash backstop measured from sandbox creation, so
66
68
  // setup time cannot consume the terminal-flush grace.
67
69
  const DAYTONA_AUTO_ARCHIVE_INTERVAL_MINUTES = 15;
68
- // Daytona's Deepline-managed default snapshot is the only admitted prebuilt.
69
- // Resources are verified during acquisition, before customer code or billing.
70
+ // Daytona snapshots carry fixed resource allocations. The API rejects resource
71
+ // fields on snapshot creates, so select the provider snapshot that matches each
72
+ // Deepline profile instead of trying to resize after creation.
73
+ export const DAYTONA_SNAPSHOT_BY_PLAY_SANDBOX_SIZE = {
74
+ standard: 'daytona-small',
75
+ large: 'daytona-medium',
76
+ xlarge: 'daytona-large',
77
+ } as const satisfies Record<PlaySandboxSize, string>;
78
+
70
79
  export const DAYTONA_SANDBOX_CPU = STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS.cpu;
71
80
  export const DAYTONA_SANDBOX_MEMORY_GIB =
72
81
  STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS.memoryGiB;
@@ -98,6 +107,7 @@ export type DaytonaClient = {
98
107
  timeoutMs: number,
99
108
  ): { sandbox: Sandbox; dispose(): void };
100
109
  };
110
+
101
111
  export type DaytonaSandbox = Awaited<ReturnType<DaytonaClient['create']>>;
102
112
  export type DaytonaExecutionContext = PlayRunnerExecutionConfig['context'];
103
113
  export type DaytonaStageEmitter = (
@@ -569,6 +579,12 @@ async function createOneShotDaytonaSandbox(input: {
569
579
  ...STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS,
570
580
  },
571
581
  );
582
+ const sandboxSize = playSandboxSizeForLimits(limits);
583
+ if (!sandboxSize) {
584
+ throw new Error(
585
+ 'Validated Daytona sandbox profile has no snapshot mapping.',
586
+ );
587
+ }
572
588
  const orgId = normalizeLabelValue(input.orgId);
573
589
  const workflowId = normalizeLabelValue(input.context.workflowId);
574
590
  const playId = normalizeLabelValue(input.context.playId);
@@ -596,6 +612,7 @@ async function createOneShotDaytonaSandbox(input: {
596
612
  const commonParams = {
597
613
  name: input.sandboxName,
598
614
  labels,
615
+ snapshot: DAYTONA_SNAPSHOT_BY_PLAY_SANDBOX_SIZE[sandboxSize],
599
616
  // This is also the provider-owned backstop for an HTTP create timeout
600
617
  // whose named sandbox never becomes lookup-addressable to this process:
601
618
  // Daytona deletes an ephemeral sandbox immediately when it stops.
@@ -1227,8 +1244,8 @@ async function acquireOneShotDaytonaSandbox(input: {
1227
1244
  cancellationSignal: input.cancellationSignal,
1228
1245
  });
1229
1246
  if (
1230
- granted.cpu !== limits.cpu ||
1231
- granted.memoryGiB !== limits.memoryGiB ||
1247
+ granted.cpu > limits.cpu ||
1248
+ granted.memoryGiB > limits.memoryGiB ||
1232
1249
  granted.diskGiB !== limits.diskGiB ||
1233
1250
  granted.gpu !== DAYTONA_SANDBOX_GPU
1234
1251
  ) {
@@ -1298,10 +1315,9 @@ async function acquireOneShotDaytonaSandbox(input: {
1298
1315
  elapsedMs: billingStartedAt - input.startedAt,
1299
1316
  admissionWaitMs,
1300
1317
  attemptElapsedMs: result.attemptElapsedMs,
1301
- // Daytona's default snapshot is intentionally provider-managed. Record the
1302
- // resolved immutable snapshot identifier so a slow acquisition can be
1303
- // distinguished from a bad warm-pool/default-snapshot selection without
1304
- // logging provider credentials or customer-code details.
1318
+ // Record the resolved immutable snapshot identifier so slow acquisition
1319
+ // can be distinguished from a bad profile snapshot without logging
1320
+ // provider credentials or customer-code details.
1305
1321
  snapshot: sandbox.snapshot?.trim() || null,
1306
1322
  cpu: granted.cpu,
1307
1323
  memoryGiB: granted.memoryGiB,
@@ -383,17 +383,21 @@ async function main() {
383
383
  runtimeLimitMarkerPath && fs.existsSync(runtimeLimitMarkerPath),
384
384
  );
385
385
  // 137 is SIGKILL, not proof of an OOM. A watchdog, platform eviction, or
386
- // operator action can produce the same status. Only label this an OOM when
387
- // the captured runtime output carries an explicit V8 allocation signature.
386
+ // operator action can produce the same status. OOM requires an explicit V8
387
+ // allocation signature or the sandbox's cgroup OOM-kill evidence.
388
388
  // The shell sees the watchdog's exit code, not necessarily the nested
389
389
  // runner child's signal. Read the watchdog's strict marker so a child
390
390
  // SIGKILL does not collapse into the wrapper's generic exit code 1.
391
391
  const sandboxKilled = exitCode === 137 || watchdogObservedChildSigkill();
392
392
  const memoryAbort = watchdogMemoryAbortDiagnostic();
393
+ const explicitOomSignature =
394
+ (sandboxKilled || exitCode === 134) && outputHasExplicitOomSignature();
393
395
  const sandboxOom =
394
396
  !memoryAbort &&
395
- sandboxKilled &&
396
- (outputHasExplicitOomSignature() || cgroupOomKillObserved());
397
+ ((sandboxKilled && (explicitOomSignature || cgroupOomKillObserved())) ||
398
+ // V8 aborts itself on heap exhaustion. SIGABRT alone is also authored
399
+ // program output, so 134 needs the explicit allocation signature.
400
+ (exitCode === 134 && explicitOomSignature));
397
401
  const synthesizedError = runtimeLimitExceeded
398
402
  ? 'RUNTIME_LIMIT_EXCEEDED: ' + ${JSON.stringify(runtimeLimitMessage)}
399
403
  : memoryAbort
@@ -580,6 +584,8 @@ export async function stageRunnerPayload(input: {
580
584
  sandbox: RemoteRunnerPayloadSandbox;
581
585
  bundlePromise: Promise<string>;
582
586
  config: PlayRunnerExecutionConfig;
587
+ /** Provider-selected burst ceiling; the stored profile remains a reservation. */
588
+ memoryLimitMiB?: number;
583
589
  workDir: string;
584
590
  startedAt: number;
585
591
  emitStage: DaytonaStageEmitter;
@@ -674,11 +680,12 @@ export async function stageRunnerPayload(input: {
674
680
  materializedFiles: remoteMaterializedFiles,
675
681
  context: remoteRuntimeContextForDaytona(input.config.context),
676
682
  };
677
- const runtimeLimitSeconds = validatePlaySandboxRuntimeLimits(
683
+ const sandboxRuntimeLimits = validatePlaySandboxRuntimeLimits(
678
684
  input.config.context.sandboxRuntimeLimits ?? {
679
685
  ...STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS,
680
686
  },
681
- ).timeoutSeconds;
687
+ );
688
+ const runtimeLimitSeconds = sandboxRuntimeLimits.timeoutSeconds;
682
689
 
683
690
  const envelopeUpload = input.bundlePromise.then((bundle) =>
684
691
  timedRunnerPayloadUpload({
@@ -732,7 +739,12 @@ export async function stageRunnerPayload(input: {
732
739
  const watchedRunnerCommand = [
733
740
  'node',
734
741
  '-e',
735
- shellQuote(buildDaytonaRuntimeWatchdogSource()),
742
+ shellQuote(
743
+ buildDaytonaRuntimeWatchdogSource(
744
+ sandboxRuntimeLimits,
745
+ input.memoryLimitMiB,
746
+ ),
747
+ ),
736
748
  shellQuote(runnerPath),
737
749
  shellQuote(configPath),
738
750
  shellQuote(runtimeStartedPath),