deepline 0.1.312 → 0.1.313

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.
@@ -157,7 +157,7 @@ export const SDK_RELEASE = {
157
157
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
158
158
  // 0.1.254 removes the internal operations tree from the published SDK CLI.
159
159
  // Operators use the checkout-local deepline-admin binary instead.
160
- version: '0.1.312',
160
+ version: '0.1.313',
161
161
  contracts: {
162
162
  api: {
163
163
  name: 'sdk-http-api',
@@ -1291,6 +1291,25 @@ function createPacingResolver(
1291
1291
  export class PlayContextImpl {
1292
1292
  private rowStates = new Map<number, RowState>();
1293
1293
  private toolCallQueue: ToolCallRequest[] = [];
1294
+ /**
1295
+ * Direct durable boundaries (including ctx.fetch) arrive independently from
1296
+ * concurrent map rows. Hold one microtask's worth so they can use the bulk
1297
+ * receipt gateway instead of serial remote claim requests.
1298
+ */
1299
+ private pendingRuntimeReceiptClaims = new Map<
1300
+ string,
1301
+ {
1302
+ scheduled: boolean;
1303
+ requests: Array<{
1304
+ key: string;
1305
+ reclaimRunning: boolean;
1306
+ forceRefresh: boolean;
1307
+ forceFailedRefresh: boolean;
1308
+ resolve: (receipt: RuntimeStepReceipt | null) => void;
1309
+ reject: (error: unknown) => void;
1310
+ }>;
1311
+ }
1312
+ >();
1294
1313
  /**
1295
1314
  * Fixed, non-resetting coalescing deadlines for ready scheduling lanes. A
1296
1315
  * deadline starts when the first request enters an empty batch bucket. New
@@ -2002,6 +2021,14 @@ export class PlayContextImpl {
2002
2021
  forceRefresh = false,
2003
2022
  forceFailedRefresh = false,
2004
2023
  ): Promise<RuntimeStepReceipt | null> {
2024
+ if (this.#options.claimRuntimeStepReceipts) {
2025
+ return await this.enqueueRuntimeStepReceiptClaim({
2026
+ key,
2027
+ reclaimRunning,
2028
+ forceRefresh,
2029
+ forceFailedRefresh,
2030
+ });
2031
+ }
2005
2032
  if (!this.#options.claimRuntimeStepReceipt) {
2006
2033
  return null;
2007
2034
  }
@@ -2025,6 +2052,71 @@ export class PlayContextImpl {
2025
2052
  };
2026
2053
  }
2027
2054
 
2055
+ private async enqueueRuntimeStepReceiptClaim(input: {
2056
+ key: string;
2057
+ reclaimRunning: boolean;
2058
+ forceRefresh: boolean;
2059
+ forceFailedRefresh: boolean;
2060
+ }): Promise<RuntimeStepReceipt | null> {
2061
+ const batchKey = [
2062
+ input.reclaimRunning ? 'reclaim' : 'ordinary',
2063
+ input.forceRefresh ? 'force' : 'cached',
2064
+ input.forceFailedRefresh ? 'failed-force' : 'failed-cached',
2065
+ ].join(':');
2066
+ let batch = this.pendingRuntimeReceiptClaims.get(batchKey);
2067
+ if (!batch) {
2068
+ batch = { scheduled: false, requests: [] };
2069
+ this.pendingRuntimeReceiptClaims.set(batchKey, batch);
2070
+ }
2071
+ return await new Promise<RuntimeStepReceipt | null>((resolve, reject) => {
2072
+ batch!.requests.push({ ...input, resolve, reject });
2073
+ if (batch!.scheduled) return;
2074
+ batch!.scheduled = true;
2075
+ queueMicrotask(
2076
+ () => void this.flushRuntimeStepReceiptClaimBatch(batchKey),
2077
+ );
2078
+ });
2079
+ }
2080
+
2081
+ private async flushRuntimeStepReceiptClaimBatch(
2082
+ batchKey: string,
2083
+ ): Promise<void> {
2084
+ const batch = this.pendingRuntimeReceiptClaims.get(batchKey);
2085
+ if (!batch) return;
2086
+ this.pendingRuntimeReceiptClaims.delete(batchKey);
2087
+ const requests = batch.requests;
2088
+ const first = requests[0];
2089
+ const claimReceipts = this.#options.claimRuntimeStepReceipts;
2090
+ if (!first || !claimReceipts) {
2091
+ for (const request of requests) request.resolve(null);
2092
+ return;
2093
+ }
2094
+ try {
2095
+ const receipts = await this.dispatchChunkedRuntimeReceiptRequest(
2096
+ requests,
2097
+ (chunk) =>
2098
+ claimReceipts({
2099
+ keys: chunk.map((request) => request.key),
2100
+ leaseIds: chunk.map(() => `receipt-lease:${crypto.randomUUID()}`),
2101
+ runId: this.currentReceiptOwnerRunId,
2102
+ runAttempt: this.currentRunAttempt,
2103
+ leaseAware: true,
2104
+ ...(first.reclaimRunning ? { reclaimRunning: true } : {}),
2105
+ ...(first.forceRefresh ? { forceRefresh: true } : {}),
2106
+ ...(first.forceFailedRefresh ? { forceFailedRefresh: true } : {}),
2107
+ }),
2108
+ );
2109
+ for (let index = 0; index < requests.length; index += 1) {
2110
+ const request = requests[index]!;
2111
+ request.resolve(
2112
+ this.normalizeRuntimeStepReceipt(request.key, receipts[index]),
2113
+ );
2114
+ }
2115
+ } catch (error) {
2116
+ for (const request of requests) request.reject(error);
2117
+ }
2118
+ }
2119
+
2028
2120
  private async completeRuntimeStepReceipt(
2029
2121
  key: string,
2030
2122
  _runId: string,
@@ -1,4 +1,9 @@
1
- import type { Daytona } from '@daytonaio/sdk';
1
+ import {
2
+ Image,
3
+ type CreateSandboxFromImageParams,
4
+ type Daytona,
5
+ } from '@daytonaio/sdk';
6
+ import { DAYTONA_DEFAULT_WORKDIR } from '@shared_libs/play-runtime/daytona-runtime-config';
2
7
  import type {
3
8
  PlayRunnerExecutionConfig,
4
9
  PlayRunnerResult,
@@ -8,14 +13,18 @@ import { PLAY_RUNNER_TIMEOUT_SECONDS } from '@shared_libs/play-runtime/runtime-c
8
13
  import { resolvePlaySandboxRuntimeLimits } from '@shared_libs/play-runtime/sandbox-runtime-limits';
9
14
 
10
15
  const DAYTONA_CREATE_TIMEOUT_SECONDS = 10;
16
+ const DAYTONA_CUSTOM_RESOURCE_CREATE_TIMEOUT_SECONDS = 120;
11
17
  const DAYTONA_CREATE_RETRY_DELAYS_MS = [0, 500, 1_500] as const;
12
18
  // Explicit runner deadline + scheduler GC own the normal lifecycle. Daytona's
13
19
  // inactivity stop is a wider crash backstop measured from sandbox creation, so
14
20
  // setup time cannot consume the terminal-flush grace.
15
21
  const DAYTONA_AUTO_ARCHIVE_INTERVAL_MINUTES = 15;
16
22
  const DAYTONA_SANDBOX_LABEL_SOURCE = 'deepline-play-runner';
17
- // Daytona's default image is the fast path. Non-standard resources are applied
18
- // during acquisition, before customer code and before the billing clock starts.
23
+ const DAYTONA_CUSTOM_RESOURCE_IMAGE = Image.base(
24
+ 'node:20-bookworm-slim',
25
+ ).workdir(DAYTONA_DEFAULT_WORKDIR);
26
+ // Daytona's default image is the fast path. Resources are pinned at creation
27
+ // and verified during acquisition, before customer code or billing starts.
19
28
  export const DAYTONA_SANDBOX_CPU = 1;
20
29
  export const DAYTONA_SANDBOX_MEMORY_GIB = 1;
21
30
  export const DAYTONA_SANDBOX_DISK_GIB = 3;
@@ -204,26 +213,47 @@ async function createOneShotDaytonaSandbox(input: {
204
213
  runtimeSchedulerSchema: input.context.runtimeSchedulerSchema ?? null,
205
214
  });
206
215
 
207
- // Intentionally omit image/snapshot so Daytona uses its default fast sandbox
208
- // image; custom images would add build/pull tax to one-shot cold starts.
209
- return input.daytona.create(
210
- {
211
- labels,
212
- ephemeral: true,
213
- autoStopInterval: Math.ceil(
214
- (limits.timeoutSeconds + PLAY_RUNNER_TIMEOUT_SECONDS - 30 * 60) / 60,
215
- ),
216
- autoArchiveInterval: DAYTONA_AUTO_ARCHIVE_INTERVAL_MINUTES,
217
- // A non-empty `networkAllowList` IS the "block all egress except these
218
- // CIDRs" control; Daytona rejects create when `networkBlockAll: true` is
219
- // combined with a non-empty allow-list ("networkBlockAll: true cannot be
220
- // combined with a non-empty networkAllowList or domainAllowList"). Pass
221
- // the allow-list alone so the egress restriction holds without the
222
- // contradictory flag.
223
- ...(networkAllowList ? { networkAllowList } : {}),
224
- },
225
- { timeout: DAYTONA_CREATE_TIMEOUT_SECONDS },
226
- );
216
+ const hasCustomResources =
217
+ limits.cpu !== DAYTONA_SANDBOX_CPU ||
218
+ limits.memoryGiB !== DAYTONA_SANDBOX_MEMORY_GIB ||
219
+ limits.diskGiB !== DAYTONA_SANDBOX_DISK_GIB;
220
+ const commonParams = {
221
+ labels,
222
+ ephemeral: true,
223
+ autoStopInterval: Math.ceil(
224
+ (limits.timeoutSeconds + PLAY_RUNNER_TIMEOUT_SECONDS - 30 * 60) / 60,
225
+ ),
226
+ autoArchiveInterval: DAYTONA_AUTO_ARCHIVE_INTERVAL_MINUTES,
227
+ // A non-empty `networkAllowList` IS the "block all egress except these
228
+ // CIDRs" control; Daytona rejects create when `networkBlockAll: true` is
229
+ // combined with a non-empty allow-list ("networkBlockAll: true cannot be
230
+ // combined with a non-empty networkAllowList or domainAllowList"). Pass
231
+ // the allow-list alone so the egress restriction holds without the
232
+ // contradictory flag.
233
+ ...(networkAllowList ? { networkAllowList } : {}),
234
+ };
235
+ if (hasCustomResources) {
236
+ // Daytona fixes resources into snapshots and rejects a resources field on
237
+ // snapshot/default-image creates. Its supported custom-resource path
238
+ // builds from an OCI image and applies CPU, memory, and disk at creation.
239
+ const createParams: CreateSandboxFromImageParams = {
240
+ ...commonParams,
241
+ image: DAYTONA_CUSTOM_RESOURCE_IMAGE,
242
+ resources: {
243
+ cpu: limits.cpu,
244
+ memory: limits.memoryGiB,
245
+ disk: limits.diskGiB,
246
+ },
247
+ };
248
+ return input.daytona.create(createParams, {
249
+ timeout: DAYTONA_CUSTOM_RESOURCE_CREATE_TIMEOUT_SECONDS,
250
+ });
251
+ }
252
+ // Keep the fast default snapshot for standard resources, including plays
253
+ // that override timeout only.
254
+ return input.daytona.create(commonParams, {
255
+ timeout: DAYTONA_CREATE_TIMEOUT_SECONDS,
256
+ });
227
257
  }
228
258
 
229
259
  async function createRetriedOneShotDaytonaSandbox(input: {
@@ -304,9 +334,9 @@ async function acquireOneShotDaytonaSandbox(input: {
304
334
  diskGiB: result.sandbox.disk,
305
335
  gpu: result.sandbox.gpu ?? 0,
306
336
  };
307
- // Daytona's SDK only accepts resources when a custom image is supplied.
308
- // We deliberately retain the provider's fast default image, then resize
309
- // before any customer code or billing window begins.
337
+ // Compatibility fallback for Daytona targets that ignore create-time
338
+ // resources but still support resize. The hosted target should normally
339
+ // match immediately; either path is verified before code or billing starts.
310
340
  if (
311
341
  typeof result.sandbox.resize === 'function' &&
312
342
  (granted.cpu !== limits.cpu ||
package/dist/cli/index.js CHANGED
@@ -1037,7 +1037,7 @@ var SDK_RELEASE = {
1037
1037
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
1038
1038
  // 0.1.254 removes the internal operations tree from the published SDK CLI.
1039
1039
  // Operators use the checkout-local deepline-admin binary instead.
1040
- version: "0.1.312",
1040
+ version: "0.1.313",
1041
1041
  contracts: {
1042
1042
  api: {
1043
1043
  name: "sdk-http-api",
@@ -1022,7 +1022,7 @@ var SDK_RELEASE = {
1022
1022
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
1023
1023
  // 0.1.254 removes the internal operations tree from the published SDK CLI.
1024
1024
  // Operators use the checkout-local deepline-admin binary instead.
1025
- version: "0.1.312",
1025
+ version: "0.1.313",
1026
1026
  contracts: {
1027
1027
  api: {
1028
1028
  name: "sdk-http-api",
package/dist/index.js CHANGED
@@ -760,7 +760,7 @@ var SDK_RELEASE = {
760
760
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
761
761
  // 0.1.254 removes the internal operations tree from the published SDK CLI.
762
762
  // Operators use the checkout-local deepline-admin binary instead.
763
- version: "0.1.312",
763
+ version: "0.1.313",
764
764
  contracts: {
765
765
  api: {
766
766
  name: "sdk-http-api",
package/dist/index.mjs CHANGED
@@ -686,7 +686,7 @@ var SDK_RELEASE = {
686
686
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
687
687
  // 0.1.254 removes the internal operations tree from the published SDK CLI.
688
688
  // Operators use the checkout-local deepline-admin binary instead.
689
- version: "0.1.312",
689
+ version: "0.1.313",
690
690
  contracts: {
691
691
  api: {
692
692
  name: "sdk-http-api",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepline",
3
- "version": "0.1.312",
3
+ "version": "0.1.313",
4
4
  "description": "Deepline SDK + CLI — B2B data enrichment powered by durable cloud execution",
5
5
  "license": "MIT",
6
6
  "repository": {