deepline 0.1.281 → 0.1.283

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.
@@ -155,7 +155,7 @@ export const SDK_RELEASE = {
155
155
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
156
156
  // 0.1.254 removes the internal operations tree from the published SDK CLI.
157
157
  // Operators use the checkout-local deepline-admin binary instead.
158
- version: '0.1.281',
158
+ version: '0.1.283',
159
159
  contracts: {
160
160
  api: {
161
161
  name: 'sdk-http-api',
@@ -142,6 +142,7 @@ import {
142
142
  isQueryResultDatasetReadRequest,
143
143
  isQueryResultDatasetTool,
144
144
  } from './query-result-dataset';
145
+ import { isAlwaysFreshIntegrationTool } from './tool-cache-policy';
145
146
  import { isRowIsolationExemptError } from './row-isolation';
146
147
  import {
147
148
  createRuntimePersistenceLatch,
@@ -6355,7 +6356,9 @@ export class PlayContextImpl {
6355
6356
  const eventWaitHandler =
6356
6357
  (await this.#options.getIntegrationEventWaitHandler?.(toolId)) ?? null;
6357
6358
  const cacheableToolResult =
6358
- !eventWaitHandler && !isQueryResultDatasetReadRequest(toolId, input);
6359
+ !eventWaitHandler &&
6360
+ !isQueryResultDatasetReadRequest(toolId, input) &&
6361
+ !isAlwaysFreshIntegrationTool(toolId);
6359
6362
  let durableCacheKey = await this.durableToolCallCacheKey({
6360
6363
  toolId,
6361
6364
  requestInput: input,
@@ -10,6 +10,9 @@ const RUNTIME_LIMIT_EXCEEDED_RE = /\bRUNTIME_LIMIT_EXCEEDED\b/i;
10
10
  const RUNTIME_SANDBOX_INSPECTION_UNAVAILABLE_RE =
11
11
  /\bRUNTIME_SANDBOX_INSPECTION_UNAVAILABLE\b/i;
12
12
  const RUNTIME_SANDBOX_LOST_RE = /\bRUNTIME_SANDBOX_LOST\b/i;
13
+ const RUNTIME_SANDBOX_OOM_RE =
14
+ /\bRUNTIME_SANDBOX_OOM\b|(?:javascript heap out of memory|fatal error:.*(?:heap|allocation).*memory)/i;
15
+ const RUNTIME_SANDBOX_KILLED_RE = /\bRUNTIME_SANDBOX_KILLED\b/i;
13
16
  const OUTPUT_TOO_LARGE_RE = /\b(?:OUTPUT_TOO_LARGE|OutputTooLarge)\b/;
14
17
 
15
18
  export const PLATFORM_DEPLOY_INTERRUPTED_MESSAGE =
@@ -23,6 +26,10 @@ export const INTERNAL_RUNTIME_STORAGE_ERROR_MESSAGE =
23
26
  'Internal play runtime storage failed. Please retry the run; if this keeps happening, contact Deepline support with the run ID.';
24
27
  export const RUNTIME_SANDBOX_LOST_MESSAGE =
25
28
  'The execution sandbox became unreachable before returning a terminal result. Re-run the same command; if this keeps happening, contact Deepline support with the run ID.';
29
+ export const RUNTIME_SANDBOX_OOM_MESSAGE =
30
+ 'The execution sandbox ran out of memory. Completed work is durably recorded. Retry the same command safely; Deepline reuses completed steps and does not repeat their provider calls. If it happens again, reduce the batch size or row payload.';
31
+ export const RUNTIME_SANDBOX_KILLED_MESSAGE =
32
+ 'The execution sandbox was killed before it could return a result, and the kill reason is unavailable. Completed work is durably recorded. Retry the same command safely; Deepline reuses completed steps and does not repeat their provider calls.';
26
33
  export const RUNTIME_LIMIT_EXCEEDED_MESSAGE =
27
34
  'The play reached its 30 minute runtime limit and was stopped. Completed row state was preserved; run a smaller batch or continue from the persisted rows.';
28
35
  export const RUNTIME_SANDBOX_INSPECTION_UNAVAILABLE_MESSAGE =
@@ -268,6 +275,24 @@ export function normalizePlayRunFailure(error: unknown): PlayRunFailureDetails {
268
275
  cause,
269
276
  };
270
277
  }
278
+ if (RUNTIME_SANDBOX_OOM_RE.test(rawCause)) {
279
+ return {
280
+ code: 'RUNTIME_SANDBOX_OOM',
281
+ phase: 'infrastructure',
282
+ message: RUNTIME_SANDBOX_OOM_MESSAGE,
283
+ retryable: true,
284
+ cause,
285
+ };
286
+ }
287
+ if (RUNTIME_SANDBOX_KILLED_RE.test(rawCause)) {
288
+ return {
289
+ code: 'RUNTIME_SANDBOX_KILLED',
290
+ phase: 'infrastructure',
291
+ message: RUNTIME_SANDBOX_KILLED_MESSAGE,
292
+ retryable: true,
293
+ cause,
294
+ };
295
+ }
271
296
  if (RUNTIME_SANDBOX_LOST_RE.test(rawCause)) {
272
297
  const stack = error instanceof Error ? boundedFailureStack(error) : null;
273
298
  const causes = error instanceof Error ? failureCauseTexts(error) : [];
@@ -18,6 +18,10 @@ import {
18
18
  RUNNER_POST_TERMINAL_DIAGNOSTIC_MAX_BYTES,
19
19
  RUNNER_TERMINAL_PUSH_MAX_BODY_BYTES,
20
20
  } from '@shared_libs/play-runtime/output-size-limits';
21
+ import {
22
+ RUNTIME_SANDBOX_KILLED_MESSAGE,
23
+ RUNTIME_SANDBOX_OOM_MESSAGE,
24
+ } from '@shared_libs/play-runtime/run-failure';
21
25
  import { compactPlayArtifactForRuntimeTransport } from '@shared_libs/plays/artifact-transport';
22
26
  import type {
23
27
  DaytonaExecutionContext,
@@ -122,6 +126,8 @@ const configPath = process.argv[2];
122
126
  const exitCodeRaw = Number.parseInt(process.argv[3] ?? '', 10);
123
127
  const outputPath = process.argv[4];
124
128
  const runtimeLimitMarkerPath = process.argv[5] || '';
129
+ const oomKillBaselinePath = process.argv[6] || '';
130
+ const memoryEventsPath = process.argv[7] || '/sys/fs/cgroup/memory.events';
125
131
  const exitCode = Number.isFinite(exitCodeRaw) ? exitCodeRaw : null;
126
132
  const MAX_OUTPUT_BYTES = ${DAYTONA_CRASH_PUSHER_OUTPUT_TAIL_MAX_BYTES};
127
133
  const MAX_POST_TERMINAL_DIAGNOSTIC_BYTES = ${RUNNER_POST_TERMINAL_DIAGNOSTIC_MAX_BYTES};
@@ -176,6 +182,31 @@ function readResultFromOutput() {
176
182
  return null;
177
183
  }
178
184
 
185
+ function outputHasExplicitOomSignature() {
186
+ try {
187
+ const stat = fs.statSync(outputPath);
188
+ const start = Math.max(0, stat.size - MAX_OUTPUT_BYTES);
189
+ const fd = fs.openSync(outputPath, 'r');
190
+ const buffer = Buffer.alloc(stat.size - start);
191
+ fs.readSync(fd, buffer, 0, buffer.length, start);
192
+ fs.closeSync(fd);
193
+ return /javascript heap out of memory|fatal error:.*(?:heap|allocation).*memory/i.test(buffer.toString('utf-8'));
194
+ } catch {}
195
+ return false;
196
+ }
197
+
198
+ function cgroupOomKillObserved() {
199
+ try {
200
+ if (!oomKillBaselinePath) return false;
201
+ const baseline = Number.parseInt(fs.readFileSync(oomKillBaselinePath, 'utf8').trim(), 10);
202
+ const events = fs.readFileSync(memoryEventsPath, 'utf8');
203
+ const match = events.match(/^oom_kill\\s+(\\d+)$/m);
204
+ const current = match ? Number.parseInt(match[1], 10) : Number.NaN;
205
+ return Number.isFinite(baseline) && Number.isFinite(current) && current > baseline;
206
+ } catch {}
207
+ return false;
208
+ }
209
+
179
210
  function terminalResultForTransport(result) {
180
211
  if (
181
212
  result &&
@@ -225,12 +256,21 @@ async function main() {
225
256
  const runtimeLimitExceeded = Boolean(
226
257
  runtimeLimitMarkerPath && fs.existsSync(runtimeLimitMarkerPath),
227
258
  );
259
+ // 137 is SIGKILL, not proof of an OOM. A watchdog, platform eviction, or
260
+ // operator action can produce the same status. Only label this an OOM when
261
+ // the captured runtime output carries an explicit V8 allocation signature.
262
+ const sandboxKilled = exitCode === 137;
263
+ const sandboxOom =
264
+ sandboxKilled && (outputHasExplicitOomSignature() || cgroupOomKillObserved());
228
265
  const synthesizedError = runtimeLimitExceeded
229
266
  ? 'RUNTIME_LIMIT_EXCEEDED: The play reached its 30 minute runtime limit and was stopped.'
267
+ : sandboxOom
268
+ ? 'RUNTIME_SANDBOX_OOM: ' + ${JSON.stringify(RUNTIME_SANDBOX_OOM_MESSAGE)}
269
+ : sandboxKilled
270
+ ? 'RUNTIME_SANDBOX_KILLED: ' + ${JSON.stringify(RUNTIME_SANDBOX_KILLED_MESSAGE)}
230
271
  :
231
272
  'Daytona play runner exited with code ' +
232
273
  (exitCode === null ? 'unknown' : exitCode) +
233
- (exitCode === 137 ? ' (sandbox OOM or forced SIGKILL)' : '') +
234
274
  ' without pushing a terminal (crash containment epilogue).';
235
275
  const result = terminalResultForTransport(parsedResult || {
236
276
  status: 'failed',
@@ -244,6 +284,24 @@ async function main() {
244
284
  cause: synthesizedError,
245
285
  }]
246
286
  : undefined,
287
+ ...(sandboxOom
288
+ ? { errors: [{
289
+ code: 'RUNTIME_SANDBOX_OOM',
290
+ phase: 'infrastructure',
291
+ message: ${JSON.stringify(RUNTIME_SANDBOX_OOM_MESSAGE)},
292
+ retryable: true,
293
+ cause: synthesizedError,
294
+ }] }
295
+ : {}),
296
+ ...(sandboxKilled && !sandboxOom
297
+ ? { errors: [{
298
+ code: 'RUNTIME_SANDBOX_KILLED',
299
+ phase: 'infrastructure',
300
+ message: ${JSON.stringify(RUNTIME_SANDBOX_KILLED_MESSAGE)},
301
+ retryable: true,
302
+ cause: synthesizedError,
303
+ }] }
304
+ : {}),
247
305
  logs: [],
248
306
  stats: {},
249
307
  steps: [],
@@ -454,6 +512,7 @@ export async function stageDaytonaRunnerPayload(input: {
454
512
  const outputPath = `${workDir}/deepline-play-output-${randomUUID()}.log`;
455
513
  const crashPusherLogPath = `${workDir}/deepline-play-crash-terminal-${randomUUID()}.log`;
456
514
  const exitCodePath = `${workDir}/deepline-play-exit-${randomUUID()}.txt`;
515
+ const oomKillBaselinePath = `${workDir}/deepline-play-oom-kill-${randomUUID()}.txt`;
457
516
  const runtimeStartedPath = `${workDir}/deepline-play-runtime-started-${randomUUID()}.json`;
458
517
  const runtimeCompletedPath = `${workDir}/deepline-play-runtime-completed-${randomUUID()}.json`;
459
518
  const runtimeLimitMarkerPath = `${workDir}/deepline-play-runtime-limit-${randomUUID()}.txt`;
@@ -491,7 +550,7 @@ export async function stageDaytonaRunnerPayload(input: {
491
550
  // Its own retry diagnostics are kept in a separate log: appending them to
492
551
  // `outputPath` after a near-gateway-sized result could hide that result from
493
552
  // a later bounded-tail recovery read.
494
- const command = `rm -f ${shellQuote(outputPath)} ${shellQuote(crashPusherLogPath)} ${shellQuote(exitCodePath)} ${shellQuote(runtimeStartedPath)} ${shellQuote(runtimeCompletedPath)} ${shellQuote(runtimeLimitMarkerPath)} ${shellQuote(progressEventPath)} ${shellQuote(`${progressEventPath}.*`)}; ( ${runnerCommand} ) > ${shellQuote(outputPath)} 2>&1; code=$?; printf '%s' "$code" > ${shellQuote(exitCodePath)}; node ${shellQuote(crashPusherPath)} ${shellQuote(configPath)} "$code" ${shellQuote(outputPath)} ${shellQuote(runtimeLimitMarkerPath)} > ${shellQuote(crashPusherLogPath)} 2>&1 || true; printf 'deepline runner output captured: %s\\n' ${shellQuote(outputPath)}; exit "$code"`;
553
+ const command = `rm -f ${shellQuote(outputPath)} ${shellQuote(crashPusherLogPath)} ${shellQuote(exitCodePath)} ${shellQuote(oomKillBaselinePath)} ${shellQuote(runtimeStartedPath)} ${shellQuote(runtimeCompletedPath)} ${shellQuote(runtimeLimitMarkerPath)} ${shellQuote(progressEventPath)} ${shellQuote(`${progressEventPath}.*`)}; ( 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)}; node ${shellQuote(crashPusherPath)} ${shellQuote(configPath)} "$code" ${shellQuote(outputPath)} ${shellQuote(runtimeLimitMarkerPath)} ${shellQuote(oomKillBaselinePath)} /sys/fs/cgroup/memory.events > ${shellQuote(crashPusherLogPath)} 2>&1 || true; printf 'deepline runner output captured: %s\\n' ${shellQuote(outputPath)}; exit "$code"`;
495
554
 
496
555
  return {
497
556
  workDir: input.workDir,
@@ -26,6 +26,7 @@ export type RuntimeResourceTerminalReason =
26
26
  | 'completed'
27
27
  | 'runner_failed'
28
28
  | 'sandbox_missing'
29
+ | 'sandbox_killed'
29
30
  | 'sandbox_oom'
30
31
  | 'runtime_limit'
31
32
  | 'cancelled'
@@ -0,0 +1,14 @@
1
+ const ALWAYS_FRESH_INTEGRATION_TOOL_IDS = new Set([
2
+ 'google_workspace_request',
3
+ 'google_workspace_export_dataset',
4
+ // Unique public operation alias for google_workspace_export_dataset.
5
+ 'export_dataset',
6
+ ]);
7
+
8
+ /**
9
+ * These tools read or mutate reviewer-owned live state. Reusing a checkpoint or
10
+ * durable receipt would hide edits or skip the export's conflict checks.
11
+ */
12
+ export function isAlwaysFreshIntegrationTool(toolId: string): boolean {
13
+ return ALWAYS_FRESH_INTEGRATION_TOOL_IDS.has(toolId.trim());
14
+ }
package/dist/cli/index.js CHANGED
@@ -718,7 +718,7 @@ var SDK_RELEASE = {
718
718
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
719
719
  // 0.1.254 removes the internal operations tree from the published SDK CLI.
720
720
  // Operators use the checkout-local deepline-admin binary instead.
721
- version: "0.1.281",
721
+ version: "0.1.283",
722
722
  contracts: {
723
723
  api: {
724
724
  name: "sdk-http-api",
@@ -703,7 +703,7 @@ var SDK_RELEASE = {
703
703
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
704
704
  // 0.1.254 removes the internal operations tree from the published SDK CLI.
705
705
  // Operators use the checkout-local deepline-admin binary instead.
706
- version: "0.1.281",
706
+ version: "0.1.283",
707
707
  contracts: {
708
708
  api: {
709
709
  name: "sdk-http-api",
package/dist/index.js CHANGED
@@ -438,7 +438,7 @@ var SDK_RELEASE = {
438
438
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
439
439
  // 0.1.254 removes the internal operations tree from the published SDK CLI.
440
440
  // Operators use the checkout-local deepline-admin binary instead.
441
- version: "0.1.281",
441
+ version: "0.1.283",
442
442
  contracts: {
443
443
  api: {
444
444
  name: "sdk-http-api",
package/dist/index.mjs CHANGED
@@ -367,7 +367,7 @@ var SDK_RELEASE = {
367
367
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
368
368
  // 0.1.254 removes the internal operations tree from the published SDK CLI.
369
369
  // Operators use the checkout-local deepline-admin binary instead.
370
- version: "0.1.281",
370
+ version: "0.1.283",
371
371
  contracts: {
372
372
  api: {
373
373
  name: "sdk-http-api",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepline",
3
- "version": "0.1.281",
3
+ "version": "0.1.283",
4
4
  "description": "Deepline SDK + CLI — B2B data enrichment powered by durable cloud execution",
5
5
  "license": "MIT",
6
6
  "repository": {