deepline 0.2.55 → 0.2.56

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 (41) hide show
  1. package/dist/bundling-sources/sdk/src/client.ts +14 -0
  2. package/dist/bundling-sources/sdk/src/http.ts +19 -1
  3. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  4. package/dist/bundling-sources/sdk/src/stream-reconnect.ts +6 -0
  5. package/dist/bundling-sources/sdk/src/types.ts +7 -0
  6. package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +146 -12
  7. package/dist/bundling-sources/shared_libs/play-runtime/batch-runtime.ts +6 -3
  8. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +1631 -748
  9. package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +20 -0
  10. package/dist/bundling-sources/shared_libs/play-runtime/durable-receipt-execution.ts +81 -52
  11. package/dist/bundling-sources/shared_libs/play-runtime/fixture-behavior.ts +146 -6
  12. package/dist/bundling-sources/shared_libs/play-runtime/gateway-auth-session.ts +56 -22
  13. package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +14 -11
  14. package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +21 -2
  15. package/dist/bundling-sources/shared_libs/play-runtime/governor/policy.ts +81 -21
  16. package/dist/bundling-sources/shared_libs/play-runtime/output-size-limits.ts +7 -0
  17. package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +3 -0
  18. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +21 -0
  19. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +27 -2
  20. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-runtime-watchdog.ts +49 -10
  21. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +96 -3
  22. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +341 -67
  23. package/dist/bundling-sources/shared_libs/play-runtime/runtime-capacity-policy.ts +10 -0
  24. package/dist/bundling-sources/shared_libs/play-runtime/runtime-contract.ts +8 -1
  25. package/dist/bundling-sources/shared_libs/play-runtime/runtime-pg-driver-pg.ts +17 -2
  26. package/dist/bundling-sources/shared_libs/play-runtime/runtime-pg-driver.ts +4 -1
  27. package/dist/bundling-sources/shared_libs/play-runtime/runtime-receipt-store-adapter.ts +17 -1
  28. package/dist/bundling-sources/shared_libs/play-runtime/runtime-receipt-writer.ts +16 -2
  29. package/dist/bundling-sources/shared_libs/play-runtime/runtime-sheet-row-writer.ts +25 -0
  30. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +3 -0
  31. package/dist/bundling-sources/shared_libs/play-runtime/suspension.ts +2 -0
  32. package/dist/bundling-sources/shared_libs/play-runtime/test-runtime-seams.ts +80 -1
  33. package/dist/bundling-sources/shared_libs/security/safe-fetch.ts +9 -0
  34. package/dist/bundling-sources/shared_libs/security/safe-outbound-fetch.ts +85 -48
  35. package/dist/cli/index.js +409 -51
  36. package/dist/cli/index.mjs +389 -25
  37. package/dist/index.d.mts +19 -1
  38. package/dist/index.d.ts +19 -1
  39. package/dist/index.js +29 -2
  40. package/dist/index.mjs +29 -2
  41. package/package.json +1 -1
@@ -1030,7 +1030,7 @@ var SDK_RELEASE = {
1030
1030
  // 0.2.0 makes Dataset Handles uniformly async-only after 0.1.320 briefly
1031
1031
  // exposed storage-dependent synchronous access. This deliberate minor
1032
1032
  // release keeps lazy paging semantics independent of row residency.
1033
- version: "0.2.55",
1033
+ version: "0.2.56",
1034
1034
  contracts: {
1035
1035
  api: {
1036
1036
  name: "sdk-http-api",
@@ -1613,9 +1613,26 @@ var HttpClient = class {
1613
1613
  if (error instanceof AuthError || error instanceof DeeplineError) {
1614
1614
  throw error;
1615
1615
  }
1616
- lastError = error instanceof Error ? error : new Error(String(error));
1616
+ const normalized = error instanceof Error ? error : new Error(String(error));
1617
+ if (isAbortLikeError(normalized) && options?.signal?.aborted) {
1618
+ throw new DeeplineError(
1619
+ `Stream from ${this.config.baseUrl} was aborted by the caller.`,
1620
+ void 0,
1621
+ "ABORTED"
1622
+ );
1623
+ }
1624
+ lastError = normalized;
1617
1625
  }
1618
1626
  }
1627
+ if (lastError && isAbortLikeError(lastError)) {
1628
+ throw new DeeplineError(
1629
+ withCoworkNetworkHint(
1630
+ `Unable to stream from ${this.config.baseUrl}. The remote stream was interrupted.`
1631
+ ),
1632
+ void 0,
1633
+ "PLAY_STREAM_NETWORK_ABORTED"
1634
+ );
1635
+ }
1619
1636
  throw new DeeplineError(
1620
1637
  withCoworkNetworkHint(
1621
1638
  lastError?.message ? `Unable to stream from ${this.config.baseUrl}. ${lastError.message}` : `Unable to stream from ${this.config.baseUrl}.`
@@ -1878,6 +1895,9 @@ function streamReconnectDelayMs(attempt) {
1878
1895
  return Math.max(1, Math.floor(Math.random() * (cappedExponentialMs + 1)));
1879
1896
  }
1880
1897
  function isTransientPlayStreamError(error) {
1898
+ if (error instanceof DeeplineError && error.code === "PLAY_STREAM_NETWORK_ABORTED") {
1899
+ return true;
1900
+ }
1881
1901
  if (error instanceof DeeplineError && typeof error.statusCode === "number") {
1882
1902
  return error.statusCode >= 500 && error.statusCode < 600;
1883
1903
  }
@@ -2030,6 +2050,7 @@ var RUNTIME_RECEIPT_OUTPUT_MAX_BYTES = 10 * 1024 * 1024;
2030
2050
  var RUNTIME_RECEIPT_COMPLETION_BUFFER_MAX_BYTES = 32 * 1024 * 1024;
2031
2051
  var RUNNER_TERMINAL_PUSH_MAX_BODY_BYTES = 16 * 1024 * 1024;
2032
2052
  var RUNTIME_RECEIPT_GATEWAY_BATCH_MAX_BYTES = 12 * 1024 * 1024;
2053
+ var RUNTIME_RECEIPT_WRITER_TARGET_BATCH_BYTES = 4 * 1024 * 1024;
2033
2054
  var RUNNER_POST_TERMINAL_DIAGNOSTIC_MAX_BYTES = 64 * 1024;
2034
2055
 
2035
2056
  // ../shared_libs/play-runtime/ledger-safe-payload.ts
@@ -4612,6 +4633,10 @@ var DeeplineClient = class {
4612
4633
  ...request.packagedFiles?.length ? { packagedFiles: request.packagedFiles } : {},
4613
4634
  ...request.force ? { force: true } : {},
4614
4635
  ...forceToolRefresh ? { forceToolRefresh: true } : {},
4636
+ ...typeof request.maxConcurrentExternalCalls === "number" ? {
4637
+ maxConcurrentExternalCalls: request.maxConcurrentExternalCalls
4638
+ } : {},
4639
+ ...typeof request.maxConcurrentRows === "number" ? { maxConcurrentRows: request.maxConcurrentRows } : {},
4615
4640
  ...typeof request.waitForCompletionMs === "number" ? { waitForCompletionMs: request.waitForCompletionMs } : {},
4616
4641
  // Profile selection is the API's job, not the CLI's. The server
4617
4642
  // defaults to absurd; callers normally omit this field.
@@ -4664,6 +4689,8 @@ var DeeplineClient = class {
4664
4689
  ...request.packagedFiles?.length ? { packagedFiles: request.packagedFiles } : {},
4665
4690
  ...request.force ? { force: true } : {},
4666
4691
  ...forceToolRefresh ? { forceToolRefresh: true } : {},
4692
+ ...typeof request.maxConcurrentExternalCalls === "number" ? { maxConcurrentExternalCalls: request.maxConcurrentExternalCalls } : {},
4693
+ ...typeof request.maxConcurrentRows === "number" ? { maxConcurrentRows: request.maxConcurrentRows } : {},
4667
4694
  ...typeof request.waitForCompletionMs === "number" ? { waitForCompletionMs: request.waitForCompletionMs } : {},
4668
4695
  ...request.profile ? { profile: request.profile } : {},
4669
4696
  ...integrationMode ? { integrationMode } : {},
@@ -8756,14 +8783,14 @@ function invoiceDateText(createdAt) {
8756
8783
  }
8757
8784
  function invoiceLine(entry, compact) {
8758
8785
  const amount = invoiceAmountText(entry.amount_cents, entry.currency);
8759
- const link = entry.url ?? "(no link)";
8786
+ const link2 = entry.url ?? "(no link)";
8760
8787
  if (compact) {
8761
8788
  return [
8762
8789
  entry.id,
8763
8790
  invoiceDateText(entry.created_at),
8764
8791
  amount,
8765
8792
  entry.status,
8766
- link
8793
+ link2
8767
8794
  ].join(" | ");
8768
8795
  }
8769
8796
  return [
@@ -8771,7 +8798,7 @@ function invoiceLine(entry, compact) {
8771
8798
  entry.description,
8772
8799
  amount,
8773
8800
  entry.status,
8774
- link
8801
+ link2
8775
8802
  ].join(" | ");
8776
8803
  }
8777
8804
  async function handleInvoices(options) {
@@ -10925,7 +10952,7 @@ Examples:
10925
10952
  // src/cli/commands/enrich.ts
10926
10953
  import {
10927
10954
  copyFile,
10928
- lstat,
10955
+ lstat as lstat2,
10929
10956
  mkdir as mkdir4,
10930
10957
  mkdtemp,
10931
10958
  readFile as readFile3,
@@ -10940,7 +10967,7 @@ import { basename as basename4, dirname as dirname10, extname as extname3, join
10940
10967
  import { Option } from "commander";
10941
10968
 
10942
10969
  // src/cli/commands/play.ts
10943
- import { createHash as createHash4 } from "crypto";
10970
+ import { createHash as createHash4, randomUUID as randomUUID3 } from "crypto";
10944
10971
  import {
10945
10972
  existsSync as existsSync9,
10946
10973
  readFileSync as readFileSync8,
@@ -10949,6 +10976,13 @@ import {
10949
10976
  statSync as statSync3,
10950
10977
  writeFileSync as writeFileSync9
10951
10978
  } from "fs";
10979
+ import {
10980
+ lstat,
10981
+ link,
10982
+ open,
10983
+ readFile as readFileAsync,
10984
+ unlink
10985
+ } from "fs/promises";
10952
10986
  import { basename as basename3, dirname as dirname9, join as join10, resolve as resolve11 } from "path";
10953
10987
  import { parse as parseCsvSync2 } from "csv-parse/sync";
10954
10988
 
@@ -17623,6 +17657,31 @@ ${hint}`;
17623
17657
  });
17624
17658
  }
17625
17659
 
17660
+ // ../shared_libs/play-runtime/governor/policy.ts
17661
+ var DEFAULT_MAX_CONCURRENT_EXTERNAL_CALLS = 64;
17662
+ var MAX_CONFIGURABLE_CONCURRENT_EXTERNAL_CALLS = 256;
17663
+ var MAX_CONFIGURABLE_CONCURRENT_ROWS = 1e3;
17664
+ function resolveMaxConcurrentExternalCalls(requested) {
17665
+ if (requested === void 0 || requested === null) {
17666
+ return DEFAULT_MAX_CONCURRENT_EXTERNAL_CALLS;
17667
+ }
17668
+ if (!Number.isSafeInteger(requested) || requested < 1 || requested > MAX_CONFIGURABLE_CONCURRENT_EXTERNAL_CALLS) {
17669
+ throw new Error(
17670
+ `maxConcurrentExternalCalls must be a whole number from 1 through ${MAX_CONFIGURABLE_CONCURRENT_EXTERNAL_CALLS}.`
17671
+ );
17672
+ }
17673
+ return requested;
17674
+ }
17675
+ function resolveMaxConcurrentRows(requested) {
17676
+ if (requested === void 0 || requested === null) return null;
17677
+ if (!Number.isSafeInteger(requested) || requested < 1 || requested > MAX_CONFIGURABLE_CONCURRENT_ROWS) {
17678
+ throw new Error(
17679
+ `maxConcurrentRows must be a whole number from 1 through ${MAX_CONFIGURABLE_CONCURRENT_ROWS}.`
17680
+ );
17681
+ }
17682
+ return requested;
17683
+ }
17684
+
17626
17685
  // ../shared_libs/play-runtime/sandbox-runtime-limits.ts
17627
17686
  var PLAY_SANDBOX_SIZE_LIMITS = {
17628
17687
  standard: {
@@ -17847,6 +17906,7 @@ function stripLeadingTimestamp(line) {
17847
17906
  // ../shared_libs/play-runtime/fixture-behavior.ts
17848
17907
  var FIXTURE_BEHAVIOR_VERSION = 1;
17849
17908
  var FIXTURE_BEHAVIOR_RESPONSE_VERSION = 2;
17909
+ var FIXTURE_BEHAVIOR_REPLAY_VERSION = 3;
17850
17910
  var MAX_FIXTURE_RESPONSE_DELAY_SAMPLES = 256;
17851
17911
  var MAX_FIXTURE_RESPONSE_DELAY_MS = 8 * 6e4;
17852
17912
  var MAX_FIXTURE_RESPONSE_ERROR_MESSAGE_LENGTH = 500;
@@ -17858,7 +17918,11 @@ function validateFixtureBehavior(value) {
17858
17918
  return { ok: false, error: "fixtureBehavior must be a JSON object." };
17859
17919
  }
17860
17920
  const record = value;
17861
- const supportedKeys = record.version === FIXTURE_BEHAVIOR_RESPONSE_VERSION ? /* @__PURE__ */ new Set(["version", "responseSamples"]) : /* @__PURE__ */ new Set(["version", "responseDelaySamplesMs"]);
17921
+ const supportedKeys = record.version === FIXTURE_BEHAVIOR_RESPONSE_VERSION || record.version === FIXTURE_BEHAVIOR_REPLAY_VERSION ? /* @__PURE__ */ new Set([
17922
+ "version",
17923
+ "responseSamples",
17924
+ ...record.version === FIXTURE_BEHAVIOR_REPLAY_VERSION ? ["replayBundle"] : []
17925
+ ]) : /* @__PURE__ */ new Set(["version", "responseDelaySamplesMs"]);
17862
17926
  const unknownKeys = Object.keys(record).filter(
17863
17927
  (key) => !supportedKeys.has(key)
17864
17928
  );
@@ -17868,13 +17932,13 @@ function validateFixtureBehavior(value) {
17868
17932
  error: `Unsupported fixtureBehavior field "${unknownKeys[0]}".`
17869
17933
  };
17870
17934
  }
17871
- if (record.version !== FIXTURE_BEHAVIOR_VERSION && record.version !== FIXTURE_BEHAVIOR_RESPONSE_VERSION) {
17935
+ if (record.version !== FIXTURE_BEHAVIOR_VERSION && record.version !== FIXTURE_BEHAVIOR_RESPONSE_VERSION && record.version !== FIXTURE_BEHAVIOR_REPLAY_VERSION) {
17872
17936
  return {
17873
17937
  ok: false,
17874
- error: `fixtureBehavior.version must be ${FIXTURE_BEHAVIOR_VERSION} or ${FIXTURE_BEHAVIOR_RESPONSE_VERSION}.`
17938
+ error: `fixtureBehavior.version must be ${FIXTURE_BEHAVIOR_VERSION} or ${FIXTURE_BEHAVIOR_RESPONSE_VERSION}, or ${FIXTURE_BEHAVIOR_REPLAY_VERSION}.`
17875
17939
  };
17876
17940
  }
17877
- if (record.version === FIXTURE_BEHAVIOR_RESPONSE_VERSION) {
17941
+ if (record.version === FIXTURE_BEHAVIOR_RESPONSE_VERSION || record.version === FIXTURE_BEHAVIOR_REPLAY_VERSION) {
17878
17942
  if (!Array.isArray(record.responseSamples) || record.responseSamples.length === 0 || record.responseSamples.length > MAX_FIXTURE_RESPONSE_DELAY_SAMPLES) {
17879
17943
  return {
17880
17944
  ok: false,
@@ -17981,6 +18045,72 @@ function validateFixtureBehavior(value) {
17981
18045
  ...httpError ? { httpError } : {}
17982
18046
  });
17983
18047
  }
18048
+ let replayBundle;
18049
+ if (record.version === FIXTURE_BEHAVIOR_REPLAY_VERSION) {
18050
+ if (!record.replayBundle || typeof record.replayBundle !== "object" || Array.isArray(record.replayBundle)) {
18051
+ return {
18052
+ ok: false,
18053
+ error: "fixtureBehavior.replayBundle must be an object."
18054
+ };
18055
+ }
18056
+ const replay = record.replayBundle;
18057
+ const replayUnknownKeys = Object.keys(replay).filter(
18058
+ (key) => key !== "bundleId" && key !== "manifestSha256" && key !== "syntheticFallbackToolIds"
18059
+ );
18060
+ if (replayUnknownKeys.length > 0) {
18061
+ return {
18062
+ ok: false,
18063
+ error: `Unsupported fixtureBehavior.replayBundle field "${replayUnknownKeys[0]}".`
18064
+ };
18065
+ }
18066
+ const digestPattern = /^[a-f0-9]{64}$/;
18067
+ if (typeof replay.bundleId !== "string" || !digestPattern.test(replay.bundleId)) {
18068
+ return {
18069
+ ok: false,
18070
+ error: "fixtureBehavior.replayBundle.bundleId must be a lowercase SHA-256 digest."
18071
+ };
18072
+ }
18073
+ if (typeof replay.manifestSha256 !== "string" || !digestPattern.test(replay.manifestSha256)) {
18074
+ return {
18075
+ ok: false,
18076
+ error: "fixtureBehavior.replayBundle.manifestSha256 must be a lowercase SHA-256 digest."
18077
+ };
18078
+ }
18079
+ let syntheticFallbackToolIds;
18080
+ if (replay.syntheticFallbackToolIds !== void 0) {
18081
+ if (!Array.isArray(replay.syntheticFallbackToolIds) || replay.syntheticFallbackToolIds.length === 0 || replay.syntheticFallbackToolIds.length > 32) {
18082
+ return {
18083
+ ok: false,
18084
+ error: "fixtureBehavior.replayBundle.syntheticFallbackToolIds must contain 1-32 tool ids."
18085
+ };
18086
+ }
18087
+ syntheticFallbackToolIds = [];
18088
+ for (const rawToolId of replay.syntheticFallbackToolIds) {
18089
+ if (typeof rawToolId !== "string" || rawToolId !== rawToolId.trim().toLowerCase() || !/^[a-z0-9][a-z0-9_.-]*$/.test(rawToolId) || rawToolId.length > 250 || syntheticFallbackToolIds.includes(rawToolId)) {
18090
+ return {
18091
+ ok: false,
18092
+ error: "fixtureBehavior.replayBundle.syntheticFallbackToolIds contains an invalid tool id."
18093
+ };
18094
+ }
18095
+ syntheticFallbackToolIds.push(rawToolId);
18096
+ }
18097
+ }
18098
+ replayBundle = {
18099
+ bundleId: replay.bundleId,
18100
+ manifestSha256: replay.manifestSha256,
18101
+ ...syntheticFallbackToolIds ? { syntheticFallbackToolIds } : {}
18102
+ };
18103
+ }
18104
+ if (record.version === FIXTURE_BEHAVIOR_REPLAY_VERSION) {
18105
+ return {
18106
+ ok: true,
18107
+ behavior: {
18108
+ version: FIXTURE_BEHAVIOR_REPLAY_VERSION,
18109
+ responseSamples: samples2,
18110
+ replayBundle
18111
+ }
18112
+ };
18113
+ }
17984
18114
  return {
17985
18115
  ok: true,
17986
18116
  behavior: {
@@ -18073,6 +18203,114 @@ var PLAY_RUN_RESERVED_BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
18073
18203
  "--debug-map-latency",
18074
18204
  "--debug-fixture-provider-pacing"
18075
18205
  ]);
18206
+ async function pathExistsIncludingSymlink(path) {
18207
+ try {
18208
+ await lstat(path);
18209
+ return true;
18210
+ } catch (error) {
18211
+ if (error.code === "ENOENT") {
18212
+ return false;
18213
+ }
18214
+ throw error;
18215
+ }
18216
+ }
18217
+ function runIdFileTempPath(destination) {
18218
+ return join10(
18219
+ dirname9(destination),
18220
+ `.${basename3(destination)}.${process.pid}.${randomUUID3()}.tmp`
18221
+ );
18222
+ }
18223
+ async function removeRunIdTempFile(path) {
18224
+ try {
18225
+ await unlink(path);
18226
+ } catch (error) {
18227
+ if (error.code !== "ENOENT") {
18228
+ throw error;
18229
+ }
18230
+ }
18231
+ }
18232
+ async function preflightRunIdFile(path) {
18233
+ const destination = resolve11(path);
18234
+ if (await pathExistsIncludingSymlink(destination)) {
18235
+ throw new Error(
18236
+ `--run-id-file destination already exists: ${destination}. Choose a new path so this run cannot overwrite another run identity.`
18237
+ );
18238
+ }
18239
+ const tempPath = runIdFileTempPath(destination);
18240
+ let handle = null;
18241
+ try {
18242
+ handle = await open(tempPath, "wx", 384);
18243
+ await handle.sync();
18244
+ } catch (error) {
18245
+ throw new Error(
18246
+ `Cannot write --run-id-file destination ${destination}: ${error instanceof Error ? error.message : String(error)}`
18247
+ );
18248
+ } finally {
18249
+ await handle?.close();
18250
+ await removeRunIdTempFile(tempPath);
18251
+ }
18252
+ return destination;
18253
+ }
18254
+ async function readRunIdFile(destination) {
18255
+ if (!await pathExistsIncludingSymlink(destination)) {
18256
+ return null;
18257
+ }
18258
+ let parsed;
18259
+ try {
18260
+ parsed = JSON.parse(await readFileAsync(destination, "utf8"));
18261
+ } catch (error) {
18262
+ throw new Error(
18263
+ `--run-id-file destination contains invalid JSON: ${destination}. Refusing to overwrite it (${error instanceof Error ? error.message : String(error)}).`
18264
+ );
18265
+ }
18266
+ if (!parsed || typeof parsed !== "object" || parsed.version !== 1 || typeof parsed.runId !== "string" || !parsed.runId) {
18267
+ throw new Error(
18268
+ `--run-id-file destination has an unsupported identity record: ${destination}. Refusing to overwrite it.`
18269
+ );
18270
+ }
18271
+ return parsed;
18272
+ }
18273
+ async function writePlayRunIdFile(destination, runId) {
18274
+ const existing = await readRunIdFile(destination);
18275
+ if (existing) {
18276
+ if (existing.runId === runId) return;
18277
+ throw new Error(
18278
+ `--run-id-file destination already records run ${existing.runId}; refusing to replace it with ${runId}: ${destination}`
18279
+ );
18280
+ }
18281
+ const tempPath = runIdFileTempPath(destination);
18282
+ let handle = null;
18283
+ try {
18284
+ handle = await open(tempPath, "wx", 384);
18285
+ await handle.writeFile(
18286
+ `${JSON.stringify({ version: 1, runId })}
18287
+ `,
18288
+ "utf8"
18289
+ );
18290
+ await handle.sync();
18291
+ await handle.close();
18292
+ handle = null;
18293
+ try {
18294
+ await link(tempPath, destination);
18295
+ const directory = await open(dirname9(destination), "r");
18296
+ try {
18297
+ await directory.sync();
18298
+ } finally {
18299
+ await directory.close();
18300
+ }
18301
+ } catch (error) {
18302
+ if (error.code !== "EEXIST") throw error;
18303
+ const raced = await readRunIdFile(destination);
18304
+ if (raced?.runId === runId) return;
18305
+ throw new Error(
18306
+ `--run-id-file destination already records run ${raced?.runId ?? "<invalid>"}; refusing to replace it with ${runId}: ${destination}`
18307
+ );
18308
+ }
18309
+ } finally {
18310
+ await handle?.close();
18311
+ await removeRunIdTempFile(tempPath);
18312
+ }
18313
+ }
18076
18314
  function traceCliSync(phase, fields, run) {
18077
18315
  const startedAt = Date.now();
18078
18316
  try {
@@ -19477,8 +19715,8 @@ function buildPlayDashboardUrl(baseUrl, playName) {
19477
19715
  const encodedPlayName = encodeURIComponent(playName);
19478
19716
  return `${trimmedBase}/dashboard/plays/${encodedPlayName}`;
19479
19717
  }
19480
- function openPlayDashboard(dashboardUrl, open) {
19481
- if (open && dashboardUrl) {
19718
+ function openPlayDashboard(dashboardUrl, open2) {
19719
+ if (open2 && dashboardUrl) {
19482
19720
  openInBrowser(dashboardUrl);
19483
19721
  }
19484
19722
  }
@@ -19514,9 +19752,11 @@ function assertPlayWaitNotTimedOut(input2) {
19514
19752
  );
19515
19753
  }
19516
19754
  }
19755
+ var PLAY_DURABLE_STATUS_PROBE_INTERVAL_MS = 15e3;
19517
19756
  async function waitForPlayCompletionByStream(input2) {
19518
19757
  let lastPhase = null;
19519
19758
  let reconnectAttempt = 0;
19759
+ let nextDurableProbeAt = Date.now() + PLAY_DURABLE_STATUS_PROBE_INTERVAL_MS;
19520
19760
  const withDashboardUrl = (status) => input2.dashboardUrl ? { ...status, dashboardUrl: input2.dashboardUrl } : status;
19521
19761
  const remainingWaitMs = () => input2.waitTimeoutMs === null ? null : input2.waitTimeoutMs - (Date.now() - input2.startedAt);
19522
19762
  const fetchTerminalStatus = async () => {
@@ -19535,6 +19775,14 @@ async function waitForPlayCompletionByStream(input2) {
19535
19775
  };
19536
19776
  const handleLiveEvent = async (event) => {
19537
19777
  assertPlayWaitNotTimedOut({ ...input2, lastPhase });
19778
+ const now = Date.now();
19779
+ if (now >= nextDurableProbeAt) {
19780
+ nextDurableProbeAt = now + PLAY_DURABLE_STATUS_PROBE_INTERVAL_MS;
19781
+ const durableTerminal = await fetchTerminalStatus();
19782
+ if (durableTerminal) {
19783
+ return durableTerminal;
19784
+ }
19785
+ }
19538
19786
  const phase = describeLiveEventPhase(event);
19539
19787
  if (phase) {
19540
19788
  lastPhase = phase;
@@ -19783,7 +20031,7 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
19783
20031
  lastKnownWorkflowId = eventRunId;
19784
20032
  firstRunIdMs ??= Date.now() - startedAt;
19785
20033
  if (runStartedNow) {
19786
- input2.onRunStarted?.(eventRunId);
20034
+ await input2.onRunStarted?.(eventRunId);
19787
20035
  }
19788
20036
  }
19789
20037
  if (eventConfirmsPlayRunLaunch(event) && eventRunId === lastKnownWorkflowId) {
@@ -21062,6 +21310,15 @@ function buildOrdinaryPlayRunCommand(options, resolvedRevisionId) {
21062
21310
  }
21063
21311
  if (options.profile)
21064
21312
  parts.push("--profile", shellSingleQuote(options.profile));
21313
+ if (options.maxConcurrentExternalCalls !== null) {
21314
+ parts.push(
21315
+ "--max-concurrent-external-calls",
21316
+ String(options.maxConcurrentExternalCalls)
21317
+ );
21318
+ }
21319
+ if (options.maxConcurrentRows !== null) {
21320
+ parts.push("--max-concurrent-rows", String(options.maxConcurrentRows));
21321
+ }
21065
21322
  const pinnedRevisionId = options.target.kind === "name" ? resolvedRevisionId ?? options.revisionId : null;
21066
21323
  if (pinnedRevisionId) {
21067
21324
  parts.push("--revision-id", shellSingleQuote(pinnedRevisionId));
@@ -21931,7 +22188,7 @@ function writeStartedPlayRun(input2) {
21931
22188
  );
21932
22189
  }
21933
22190
  function parsePlayRunOptions(args) {
21934
- const usage = "Usage: deepline plays run <play-name> [--input '{...}'] [--no-wait] [--tail-timeout-ms 30000] [--force] [--open] [--full] [--<input> value]\n deepline plays run <play-file.ts> [--input '{...}'] [--no-wait] [--tail-timeout-ms 30000] [--force] [--open] [--full] [--<input> value]\n deepline plays run --file <play-file.ts> [--input '{...}'] [--profile <id>] [--no-wait] [--tail-timeout-ms 30000] [--force] [--open] [--full] [--<input> value]\n deepline plays run --name <name> [--input '{...}'] [--profile <id>] [--live|--latest|--revision-id <id>] [--no-wait] [--tail-timeout-ms 30000] [--force] [--open] [--json] [--full] [--<input> value]\n Runs use the Absurd CJS runtime.\n Unknown --<input> value flags, such as --limit 5, are passed into play input.\nRun `deepline plays run --help` for idempotent call caching and ctx.dataset guidance.";
22191
+ const usage = "Usage: deepline plays run <play-name> [--input '{...}'] [--run-id-file <path>] [--no-wait] [--tail-timeout-ms 30000] [--force] [--open] [--full] [--<input> value]\n deepline plays run <play-file.ts> [--input '{...}'] [--run-id-file <path>] [--no-wait] [--tail-timeout-ms 30000] [--force] [--open] [--full] [--<input> value]\n deepline plays run --file <play-file.ts> [--input '{...}'] [--profile <id>] [--run-id-file <path>] [--no-wait] [--tail-timeout-ms 30000] [--force] [--open] [--full] [--<input> value]\n deepline plays run --name <name> [--input '{...}'] [--profile <id>] [--live|--latest|--revision-id <id>] [--run-id-file <path>] [--no-wait] [--tail-timeout-ms 30000] [--force] [--open] [--json] [--full] [--<input> value]\n Runs use the Absurd CJS runtime.\n Unknown --<input> value flags, such as --limit 5, are passed into play input.\nRun `deepline plays run --help` for idempotent call caching and ctx.dataset guidance.";
21935
22192
  let filePath = null;
21936
22193
  let playName = null;
21937
22194
  let input2 = null;
@@ -21942,15 +22199,18 @@ function parsePlayRunOptions(args) {
21942
22199
  const fullJson = args.includes("--full");
21943
22200
  const emitLogs = !jsonOutput || args.includes("--logs");
21944
22201
  const force = args.includes("--force");
21945
- const open = args.includes("--open");
22202
+ const open2 = args.includes("--open");
21946
22203
  const debugMapLatency = args.includes("--debug-map-latency");
21947
22204
  const debugFixtureProviderPacing = args.includes(
21948
22205
  "--debug-fixture-provider-pacing"
21949
22206
  );
21950
22207
  const verboseLogs = args.includes("--logs") || debugMapLatency;
21951
22208
  let waitTimeoutMs = null;
22209
+ let maxConcurrentExternalCalls = null;
22210
+ let maxConcurrentRows = null;
21952
22211
  let profile = null;
21953
22212
  let fixtureBehavior = null;
22213
+ let runIdFile = null;
21954
22214
  for (let index = 0; index < args.length; index += 1) {
21955
22215
  const arg = args[index];
21956
22216
  if (arg === "--file" && args[index + 1]) {
@@ -21965,6 +22225,23 @@ function parsePlayRunOptions(args) {
21965
22225
  input2 = parseJsonInput(args[++index]);
21966
22226
  continue;
21967
22227
  }
22228
+ if (arg === "--run-id-file") {
22229
+ const value = args[index + 1];
22230
+ if (!value || value.startsWith("--")) {
22231
+ throw new Error("--run-id-file requires a destination path.");
22232
+ }
22233
+ runIdFile = value;
22234
+ index += 1;
22235
+ continue;
22236
+ }
22237
+ if (arg.startsWith("--run-id-file=")) {
22238
+ const value = arg.slice("--run-id-file=".length);
22239
+ if (!value) {
22240
+ throw new Error("--run-id-file requires a destination path.");
22241
+ }
22242
+ runIdFile = value;
22243
+ continue;
22244
+ }
21968
22245
  if (arg === "--revision-id" && args[index + 1]) {
21969
22246
  revisionId = args[++index];
21970
22247
  continue;
@@ -21979,6 +22256,41 @@ function parsePlayRunOptions(args) {
21979
22256
  index += 1;
21980
22257
  continue;
21981
22258
  }
22259
+ if (arg === "--max-concurrent-external-calls") {
22260
+ const value = args[index + 1];
22261
+ if (!value || value.startsWith("--")) {
22262
+ throw new Error(
22263
+ "--max-concurrent-external-calls requires a whole number."
22264
+ );
22265
+ }
22266
+ if (!/^[1-9]\d*$/.test(value)) {
22267
+ throw new Error(
22268
+ "--max-concurrent-external-calls requires a positive whole number."
22269
+ );
22270
+ }
22271
+ maxConcurrentExternalCalls = parsePositiveInteger3(
22272
+ value,
22273
+ "--max-concurrent-external-calls"
22274
+ );
22275
+ resolveMaxConcurrentExternalCalls(maxConcurrentExternalCalls);
22276
+ index += 1;
22277
+ continue;
22278
+ }
22279
+ if (arg === "--max-concurrent-rows") {
22280
+ const value = args[index + 1];
22281
+ if (!value || value.startsWith("--")) {
22282
+ throw new Error("--max-concurrent-rows requires a whole number.");
22283
+ }
22284
+ if (!/^[1-9]\d*$/.test(value)) {
22285
+ throw new Error(
22286
+ "--max-concurrent-rows requires a positive whole number."
22287
+ );
22288
+ }
22289
+ maxConcurrentRows = parsePositiveInteger3(value, "--max-concurrent-rows");
22290
+ resolveMaxConcurrentRows(maxConcurrentRows);
22291
+ index += 1;
22292
+ continue;
22293
+ }
21982
22294
  if (arg === "--fixture-behavior") {
21983
22295
  const value = args[index + 1];
21984
22296
  if (!value) {
@@ -22089,11 +22401,14 @@ function parsePlayRunOptions(args) {
22089
22401
  fullJson,
22090
22402
  waitTimeoutMs,
22091
22403
  force,
22092
- open,
22404
+ maxConcurrentExternalCalls,
22405
+ maxConcurrentRows,
22406
+ open: open2,
22093
22407
  profile,
22094
22408
  debugMapLatency,
22095
22409
  debugFixtureProviderPacing,
22096
- fixtureBehavior
22410
+ fixtureBehavior,
22411
+ runIdFile
22097
22412
  };
22098
22413
  }
22099
22414
  function parsePlayCheckOptions(args) {
@@ -22776,6 +23091,8 @@ async function handleFileBackedRun(options, hooks) {
22776
23091
  ...stagedFileInputs.inputFile ? { inputFile: stagedFileInputs.inputFile } : {},
22777
23092
  ...stagedFileInputs.packagedFiles.length ? { packagedFiles: stagedFileInputs.packagedFiles } : {},
22778
23093
  ...options.force ? { force: true } : {},
23094
+ ...options.maxConcurrentExternalCalls !== null ? { maxConcurrentExternalCalls: options.maxConcurrentExternalCalls } : {},
23095
+ ...options.maxConcurrentRows !== null ? { maxConcurrentRows: options.maxConcurrentRows } : {},
22779
23096
  ...options.profile ? { profile: options.profile } : {},
22780
23097
  ...Object.keys(testPolicyOverrides).length > 0 ? { testPolicyOverrides } : {},
22781
23098
  ...integrationMode ? { integrationMode } : {},
@@ -22836,6 +23153,7 @@ async function handleFileBackedRun(options, hooks) {
22836
23153
  throw await normalizePlayStartError(client2, error, playName);
22837
23154
  })
22838
23155
  );
23156
+ await hooks?.onRunStarted?.(started.workflowId);
22839
23157
  const resolvedDashboardUrl = buildPlayDashboardUrl(client2.baseUrl, playName);
22840
23158
  openPlayDashboard(resolvedDashboardUrl, options.open);
22841
23159
  progress.phase("started run");
@@ -22956,6 +23274,8 @@ async function handleNamedRun(options, hooks) {
22956
23274
  ...stagedFileInputs.inputFile ? { inputFile: stagedFileInputs.inputFile } : {},
22957
23275
  ...stagedFileInputs.packagedFiles.length ? { packagedFiles: stagedFileInputs.packagedFiles } : {},
22958
23276
  ...options.force ? { force: true } : {},
23277
+ ...options.maxConcurrentExternalCalls !== null ? { maxConcurrentExternalCalls: options.maxConcurrentExternalCalls } : {},
23278
+ ...options.maxConcurrentRows !== null ? { maxConcurrentRows: options.maxConcurrentRows } : {},
22959
23279
  ...options.profile ? { profile: options.profile } : {},
22960
23280
  ...Object.keys(testPolicyOverrides).length > 0 ? { testPolicyOverrides } : {},
22961
23281
  ...integrationMode ? { integrationMode } : {},
@@ -23021,6 +23341,7 @@ async function handleNamedRun(options, hooks) {
23021
23341
  });
23022
23342
  })
23023
23343
  );
23344
+ await hooks?.onRunStarted?.(started.workflowId);
23024
23345
  const resolvedDashboardUrl = buildPlayDashboardUrl(client2.baseUrl, playName);
23025
23346
  openPlayDashboard(resolvedDashboardUrl, options.open);
23026
23347
  progress.phase("started run");
@@ -23037,10 +23358,28 @@ async function handleNamedRun(options, hooks) {
23037
23358
  return 0;
23038
23359
  }
23039
23360
  async function handlePlayRun(args, hooks) {
23040
- const options = parsePlayRunOptions(args);
23361
+ const parsedOptions = parsePlayRunOptions(args);
23362
+ const options = parsedOptions.runIdFile ? {
23363
+ ...parsedOptions,
23364
+ runIdFile: await preflightRunIdFile(parsedOptions.runIdFile)
23365
+ } : parsedOptions;
23366
+ const runHooks = options.runIdFile ? {
23367
+ ...hooks,
23368
+ onRunStarted: async (runId) => {
23369
+ try {
23370
+ await writePlayRunIdFile(options.runIdFile, runId);
23371
+ } catch (error) {
23372
+ throw new Error(
23373
+ `Run ${runId} was accepted, but its identity could not be persisted to ${options.runIdFile}: ${error instanceof Error ? error.message : String(error)}. Inspect it with 'deepline runs get ${runId} --full --json'; do not submit a replacement run.`,
23374
+ { cause: error }
23375
+ );
23376
+ }
23377
+ await hooks?.onRunStarted?.(runId);
23378
+ }
23379
+ } : hooks;
23041
23380
  if (options.target.kind === "file") {
23042
23381
  if (isFileTarget(options.target.path)) {
23043
- return handleFileBackedRun(options, hooks);
23382
+ return handleFileBackedRun(options, runHooks);
23044
23383
  }
23045
23384
  const resolved = resolve11(options.target.path);
23046
23385
  console.error(`File not found: ${resolved}`);
@@ -23062,7 +23401,7 @@ async function handlePlayRun(args, hooks) {
23062
23401
  }
23063
23402
  return 1;
23064
23403
  }
23065
- return handleNamedRun(options, hooks);
23404
+ return handleNamedRun(options, runHooks);
23066
23405
  }
23067
23406
  function parseRunIdPositional(args, usage) {
23068
23407
  for (let index = 0; index < args.length; index += 1) {
@@ -24390,10 +24729,18 @@ Notes:
24390
24729
  next commands. --watch and --wait are accepted compatibility aliases for the
24391
24730
  default behavior. Use --no-wait only when you intentionally want
24392
24731
  a fire-and-forget run id.
24732
+ --run-id-file writes the durable run id to a versioned JSON file as soon as
24733
+ the server accepts the run. The destination must not already exist.
24393
24734
  The play page URL is printed when the run starts. Pass --open to open it in a browser.
24394
24735
  Concurrent runs for the same play are allowed.
24395
24736
  --force starts a fresh run graph without refreshing completed provider calls.
24396
24737
  It does not cancel active sibling runs.
24738
+ --max-concurrent-external-calls controls the per-run provider-tool/ctx.fetch
24739
+ resident-work ceiling. Default: ${DEFAULT_MAX_CONCURRENT_EXTERNAL_CALLS}; accepted range: 1-${MAX_CONFIGURABLE_CONCURRENT_EXTERNAL_CALLS}.
24740
+ Provider pacing and sandbox memory limits still apply.
24741
+ --max-concurrent-rows sets the run-wide default and ceiling for live map row
24742
+ resolvers. Accepted range: 1-${MAX_CONFIGURABLE_CONCURRENT_ROWS}. The runtime
24743
+ may lower it when source-row size exceeds the active-row memory budget.
24397
24744
  This command starts cloud work and may spend Deepline credits through tool calls.
24398
24745
 
24399
24746
  Idempotent execution:
@@ -24429,8 +24776,10 @@ Idempotent execution:
24429
24776
  Examples:
24430
24777
  deepline plays run prebuilt/person-linkedin-to-email --input '{"linkedin_url":"..."}'
24431
24778
  deepline plays run long-background-play --no-wait
24779
+ deepline plays run long-background-play --run-id-file ./run-id.json
24432
24780
  deepline plays run my.play.ts --input '{"domain":"stripe.com"}'
24433
24781
  deepline plays run my.play.ts --profile absurd
24782
+ deepline plays run my.play.ts --max-concurrent-external-calls 20
24434
24783
  deepline plays run my.play.ts --input @input.json --json
24435
24784
  deepline plays run cto-search.play.ts --limit 5
24436
24785
  deepline runs export <run-id> --out output.csv
@@ -24446,9 +24795,18 @@ Examples:
24446
24795
  "--fixture-behavior <json>",
24447
24796
  "Internal/testing: fixture response behavior JSON object or @file path"
24448
24797
  ).option("--watch", "Compatibility alias; run waits by default").option("--wait", "Compatibility alias; run waits by default").option("--no-wait", "Start the run and return immediately").option(
24798
+ "--run-id-file <path>",
24799
+ "Atomically write the accepted run id to a new JSON file"
24800
+ ).option(
24449
24801
  "--logs",
24450
24802
  "When output is non-interactive, stream play logs to stderr while waiting"
24451
- ).option("--tail-timeout-ms <ms>", "Timeout while watching the run stream").option("--force", "Start a fresh run graph").option("--open", "Open the play page in a browser after the run starts").option(
24803
+ ).option("--tail-timeout-ms <ms>", "Timeout while watching the run stream").option("--force", "Start a fresh run graph").option(
24804
+ "--max-concurrent-external-calls <count>",
24805
+ `Concurrent provider-tool and ctx.fetch executions (${DEFAULT_MAX_CONCURRENT_EXTERNAL_CALLS} default, max ${MAX_CONFIGURABLE_CONCURRENT_EXTERNAL_CALLS})`
24806
+ ).option(
24807
+ "--max-concurrent-rows <count>",
24808
+ `Run-wide dataset map row resolver ceiling (max ${MAX_CONFIGURABLE_CONCURRENT_ROWS})`
24809
+ ).option("--open", "Open the play page in a browser after the run starts").option(
24452
24810
  "--debug-map-latency",
24453
24811
  "Internal diagnostics: emit one aggregate latency profile per dataset map"
24454
24812
  ).option(
@@ -24487,10 +24845,16 @@ Pass-through input flags:
24487
24845
  ...options.profile ? ["--profile", options.profile] : [],
24488
24846
  ...options.fixtureBehavior ? ["--fixture-behavior", options.fixtureBehavior] : [],
24489
24847
  ...options.wait === false ? ["--no-wait"] : [],
24848
+ ...options.runIdFile ? ["--run-id-file", options.runIdFile] : [],
24490
24849
  ...options.watch || options.wait ? ["--watch"] : [],
24491
24850
  ...options.logs ? ["--logs"] : [],
24492
24851
  ...options.tailTimeoutMs ? ["--tail-timeout-ms", options.tailTimeoutMs] : [],
24493
24852
  ...options.force ? ["--force"] : [],
24853
+ ...options.maxConcurrentExternalCalls ? [
24854
+ "--max-concurrent-external-calls",
24855
+ options.maxConcurrentExternalCalls
24856
+ ] : [],
24857
+ ...options.maxConcurrentRows ? ["--max-concurrent-rows", options.maxConcurrentRows] : [],
24494
24858
  ...options.open ? ["--open"] : [],
24495
24859
  ...options.debugMapLatency ? ["--debug-map-latency"] : [],
24496
24860
  ...options.debugFixtureProviderPacing ? ["--debug-fixture-provider-pacing"] : [],
@@ -30266,7 +30630,7 @@ function registerEnrichCommand(program) {
30266
30630
  let inPlaceTempDir = null;
30267
30631
  let inPlaceTempOutputPath = null;
30268
30632
  const inPlaceFinalOutputPath = options.inPlace ? resolve12(inputCsv) : null;
30269
- const inPlaceCommitOutputPath = options.inPlace ? (await lstat(inputCsv)).isSymbolicLink() ? await realpath2(inputCsv) : inPlaceFinalOutputPath : null;
30633
+ const inPlaceCommitOutputPath = options.inPlace ? (await lstat2(inputCsv)).isSymbolicLink() ? await realpath2(inputCsv) : inPlaceFinalOutputPath : null;
30270
30634
  const failureReportOutputPath = options.inPlace ? inPlaceFinalOutputPath : null;
30271
30635
  const enrichIssueFollowUpOutputPath = options.inPlace && inPlaceFinalOutputPath ? sidecarEnrichRowsExportPath(inPlaceFinalOutputPath) : null;
30272
30636
  let activeRunId2 = null;
@@ -30581,7 +30945,7 @@ import {
30581
30945
  import { homedir as homedir8, platform } from "os";
30582
30946
  import { basename as basename5, dirname as dirname11, join as join12, resolve as resolve13 } from "path";
30583
30947
  import { gzipSync } from "zlib";
30584
- import { randomUUID as randomUUID3 } from "crypto";
30948
+ import { randomUUID as randomUUID4 } from "crypto";
30585
30949
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
30586
30950
  var UUID_IN_TEXT_RE = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i;
30587
30951
  var MAX_SESSION_UPLOAD_BYTES = 35e5;
@@ -30980,7 +31344,7 @@ async function uploadPayload(path, payload) {
30980
31344
  return await http.post(path, payload);
30981
31345
  }
30982
31346
  async function uploadChunkedSessions(sessions, options) {
30983
- const uploadId = randomUUID3();
31347
+ const uploadId = randomUUID4();
30984
31348
  for (const session of sessions) {
30985
31349
  const bytes = Buffer.from(session.encodedContent, "base64");
30986
31350
  const chunks = [];