deepline 0.1.178 → 0.1.180

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 (26) hide show
  1. package/dist/bundling-sources/apps/play-runner-workers/src/coordinator-entry.ts +132 -7
  2. package/dist/bundling-sources/apps/play-runner-workers/src/dedup-do.ts +25 -8
  3. package/dist/bundling-sources/apps/play-runner-workers/src/durable-object-deploy-handoff.ts +8 -2
  4. package/dist/bundling-sources/apps/play-runner-workers/src/entry.ts +414 -196
  5. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/batching.ts +42 -2
  6. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/coordinator-progress.ts +289 -0
  7. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-receipts.ts +112 -1
  8. package/dist/bundling-sources/sdk/src/client.ts +237 -0
  9. package/dist/bundling-sources/sdk/src/http.ts +100 -9
  10. package/dist/bundling-sources/sdk/src/release.ts +2 -2
  11. package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +10 -2
  12. package/dist/bundling-sources/shared_libs/play-runtime/batch-runtime.ts +58 -2
  13. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +99 -25
  14. package/dist/bundling-sources/shared_libs/play-runtime/db-session.ts +7 -2
  15. package/dist/bundling-sources/shared_libs/play-runtime/persistence-latch.ts +167 -0
  16. package/dist/bundling-sources/shared_libs/play-runtime/receipt-salvage.ts +158 -0
  17. package/dist/bundling-sources/shared_libs/play-runtime/run-failure.ts +44 -0
  18. package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +74 -30
  19. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +6 -0
  20. package/dist/cli/index.js +338 -13
  21. package/dist/cli/index.mjs +338 -13
  22. package/dist/index.d.mts +44 -0
  23. package/dist/index.d.ts +44 -0
  24. package/dist/index.js +229 -9
  25. package/dist/index.mjs +229 -9
  26. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -422,10 +422,10 @@ var SDK_RELEASE = {
422
422
  // 0.1.154 removes the short-lived generated enrich StepOptions recompute
423
423
  // fields shipped in 0.1.153.
424
424
  // 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
425
- version: "0.1.178",
425
+ version: "0.1.180",
426
426
  apiContract: "2026-06-dataset-handle-results-hard-cutover",
427
427
  supportPolicy: {
428
- latest: "0.1.178",
428
+ latest: "0.1.180",
429
429
  minimumSupported: "0.1.53",
430
430
  deprecatedBelow: "0.1.53",
431
431
  commandMinimumSupported: [
@@ -586,6 +586,58 @@ var SYNTHETIC_RUN_HEADER = "x-deepline-synthetic-run";
586
586
  // src/http.ts
587
587
  var MAX_DIAGNOSTIC_HEADER_LENGTH = 120;
588
588
  var COWORK_NETWORK_HINT = "Claude Cowork appears to be running Deepline in a network-restricted sandbox. In Claude Desktop, open Settings > Capabilities, turn on Allow network egress, and set Domain allowlist to All domains for the Cowork session.";
589
+ var REQUEST_TIMEOUT_MARKER = /* @__PURE__ */ Symbol("deeplineRequestTimeout");
590
+ var REQUEST_ABORT_MARKER = /* @__PURE__ */ Symbol("deeplineRequestAbort");
591
+ function normalizeRequestAbortError(error, input) {
592
+ const normalized = error instanceof Error ? error : new Error(String(error));
593
+ const tagged = normalized;
594
+ if (input.timedOut) {
595
+ tagged[REQUEST_TIMEOUT_MARKER] = input.timeoutMs;
596
+ } else if (isAbortLikeError(normalized)) {
597
+ tagged[REQUEST_ABORT_MARKER] = true;
598
+ }
599
+ return normalized;
600
+ }
601
+ function isAbortLikeError(error) {
602
+ const name = error.name.toLowerCase();
603
+ const message = error.message.toLowerCase();
604
+ return name === "aborterror" || message.includes("operation was aborted") || message === "aborted";
605
+ }
606
+ function mapNetworkError(baseUrl, error, targetLabel) {
607
+ const tagged = error;
608
+ const timeoutMs = tagged?.[REQUEST_TIMEOUT_MARKER];
609
+ if (typeof timeoutMs === "number") {
610
+ return {
611
+ message: `Request to ${targetLabel} timed out after ${timeoutMs}ms while waiting for a response.`,
612
+ code: "NETWORK_TIMEOUT",
613
+ details: { timeoutMs, target: targetLabel }
614
+ };
615
+ }
616
+ if (tagged?.[REQUEST_ABORT_MARKER]) {
617
+ return {
618
+ message: `Request to ${targetLabel} was aborted before it completed.`,
619
+ code: "NETWORK_ABORTED"
620
+ };
621
+ }
622
+ return {
623
+ message: error?.message ? `Unable to connect to ${baseUrl}. ${error.message}` : `Unable to connect to ${baseUrl}. Is the computer able to access the url?`,
624
+ code: "NETWORK_ERROR"
625
+ };
626
+ }
627
+ function describeRequestTarget(path) {
628
+ const toolExecuteMatch = path.match(
629
+ /^\/api\/v2\/integrations\/([^/?#]+)\/execute(?:[/?#]|$)/
630
+ );
631
+ if (toolExecuteMatch) {
632
+ return providerLabelFromToolId(decodeURIComponent(toolExecuteMatch[1]));
633
+ }
634
+ return "Deepline";
635
+ }
636
+ function providerLabelFromToolId(toolId) {
637
+ const normalized = toolId.trim().toLowerCase();
638
+ const provider = normalized.split(/[_:./-]+/)[0];
639
+ return provider || normalized || "provider";
640
+ }
589
641
  var HttpClient = class {
590
642
  constructor(config) {
591
643
  this.config = config;
@@ -698,10 +750,12 @@ var HttpClient = class {
698
750
  }
699
751
  for (const candidateUrl of candidateUrls) {
700
752
  const controller = new AbortController();
701
- const timeoutId = setTimeout(
702
- () => controller.abort(),
703
- options?.timeout ?? this.config.timeout
704
- );
753
+ const timeoutMs = options?.timeout ?? this.config.timeout;
754
+ let requestTimedOut = false;
755
+ const timeoutId = setTimeout(() => {
756
+ requestTimedOut = true;
757
+ controller.abort();
758
+ }, timeoutMs);
705
759
  try {
706
760
  const response = await fetch(candidateUrl, {
707
761
  method,
@@ -774,7 +828,10 @@ var HttpClient = class {
774
828
  lastError = error;
775
829
  break;
776
830
  }
777
- lastError = error instanceof Error ? error : new Error(String(error));
831
+ lastError = normalizeRequestAbortError(error, {
832
+ timeoutMs,
833
+ timedOut: requestTimedOut
834
+ });
778
835
  }
779
836
  }
780
837
  if (attempt < maxRetries) continue;
@@ -782,8 +839,17 @@ var HttpClient = class {
782
839
  if (lastError instanceof DeeplineError) {
783
840
  throw lastError;
784
841
  }
785
- const errorMessage = lastError?.message ? `Unable to connect to ${baseUrl}. ${lastError.message}` : `Unable to connect to ${baseUrl}. Is the computer able to access the url?`;
786
- throw new DeeplineError(withCoworkNetworkHint(errorMessage));
842
+ const mappedNetworkError = mapNetworkError(
843
+ baseUrl,
844
+ lastError,
845
+ describeRequestTarget(path)
846
+ );
847
+ throw new DeeplineError(
848
+ withCoworkNetworkHint(mappedNetworkError.message),
849
+ void 0,
850
+ mappedNetworkError.code,
851
+ mappedNetworkError.details
852
+ );
787
853
  }
788
854
  /**
789
855
  * Send a GET request.
@@ -2054,6 +2120,57 @@ function decodeBase64Bytes(value) {
2054
2120
  }
2055
2121
  return bytes;
2056
2122
  }
2123
+ var STAGE_LEGACY_MULTIPART_MAX_BYTES = 4e6;
2124
+ var STAGE_DIRECT_UPLOAD_MAX_ATTEMPTS = 3;
2125
+ function stagedUploadIdentity(logicalPath, contentHash) {
2126
+ return `${contentHash}:${logicalPath}`;
2127
+ }
2128
+ function isStagedUploadMintUnsupported(error) {
2129
+ return error instanceof DeeplineError && error.statusCode === 404;
2130
+ }
2131
+ function formatMegabytes(bytes) {
2132
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
2133
+ }
2134
+ async function uploadStagedFileToPresignedUrl(input) {
2135
+ const arrayBuffer = input.body.buffer.slice(
2136
+ input.body.byteOffset,
2137
+ input.body.byteOffset + input.body.byteLength
2138
+ );
2139
+ let lastError;
2140
+ for (let attempt = 1; attempt <= STAGE_DIRECT_UPLOAD_MAX_ATTEMPTS; attempt += 1) {
2141
+ try {
2142
+ const response = await fetch(input.url, {
2143
+ method: "PUT",
2144
+ headers: input.headers,
2145
+ body: new Blob([arrayBuffer])
2146
+ });
2147
+ if (response.ok) {
2148
+ return;
2149
+ }
2150
+ const text = await response.text().catch(() => "");
2151
+ lastError = new DeeplineError(
2152
+ `Direct storage upload of ${input.logicalPath} failed: ${response.status} ${text.slice(0, 200)}`,
2153
+ response.status,
2154
+ "STAGED_FILE_UPLOAD_FAILED",
2155
+ { logicalPath: input.logicalPath }
2156
+ );
2157
+ if (response.status < 500 && response.status !== 429) {
2158
+ break;
2159
+ }
2160
+ } catch (error) {
2161
+ lastError = error;
2162
+ }
2163
+ if (attempt < STAGE_DIRECT_UPLOAD_MAX_ATTEMPTS) {
2164
+ await sleep2(250 * attempt);
2165
+ }
2166
+ }
2167
+ throw lastError instanceof Error ? lastError : new DeeplineError(
2168
+ `Direct storage upload of ${input.logicalPath} failed.`,
2169
+ void 0,
2170
+ "STAGED_FILE_UPLOAD_FAILED",
2171
+ { logicalPath: input.logicalPath }
2172
+ );
2173
+ }
2057
2174
  function readStringArray(value) {
2058
2175
  return Array.isArray(value) ? value.filter((line) => typeof line === "string") : [];
2059
2176
  }
@@ -2459,6 +2576,21 @@ var DeeplineClient = class {
2459
2576
  ...input.maxRows ? { max_rows: input.maxRows } : {}
2460
2577
  });
2461
2578
  }
2579
+ /**
2580
+ * Re-establish this workspace's tenant storage contract: role/DB connect
2581
+ * grants plus materialized table grants. Org-admin only. Use when a run fails
2582
+ * with WORKSPACE_STORAGE_NOT_READY.
2583
+ */
2584
+ async repairIngestionStorage(input) {
2585
+ return this.http.post(
2586
+ "/api/v2/ingestion/repair",
2587
+ {
2588
+ ...input?.provider ? { provider: input.provider } : {}
2589
+ },
2590
+ void 0,
2591
+ { forbiddenAsApiError: true }
2592
+ );
2593
+ }
2462
2594
  // ——————————————————————————————————————————————————————————
2463
2595
  // Plays — submission and lifecycle
2464
2596
  // ——————————————————————————————————————————————————————————
@@ -2836,6 +2968,94 @@ var DeeplineClient = class {
2836
2968
  * ```
2837
2969
  */
2838
2970
  async stagePlayFiles(files) {
2971
+ let uploads;
2972
+ try {
2973
+ uploads = await this.mintStagedPlayFileUploads(
2974
+ files.map((file) => ({
2975
+ logicalPath: file.logicalPath,
2976
+ contentHash: file.contentHash,
2977
+ contentType: file.contentType,
2978
+ bytes: file.bytes
2979
+ }))
2980
+ );
2981
+ } catch (error) {
2982
+ if (isStagedUploadMintUnsupported(error)) {
2983
+ return this.stagePlayFilesViaMultipart(files);
2984
+ }
2985
+ throw error;
2986
+ }
2987
+ const uploadByIdentity = /* @__PURE__ */ new Map();
2988
+ for (const upload of uploads) {
2989
+ uploadByIdentity.set(
2990
+ stagedUploadIdentity(upload.logicalPath, upload.contentHash),
2991
+ upload
2992
+ );
2993
+ }
2994
+ await Promise.all(
2995
+ files.map(async (file) => {
2996
+ const upload = uploadByIdentity.get(
2997
+ stagedUploadIdentity(file.logicalPath, file.contentHash)
2998
+ );
2999
+ if (!upload) {
3000
+ throw new DeeplineError(
3001
+ `The staging server did not return an upload target for ${file.logicalPath}.`,
3002
+ void 0,
3003
+ "STAGED_FILE_MINT_INCOMPLETE"
3004
+ );
3005
+ }
3006
+ if (upload.alreadyStaged || !upload.uploadUrl) {
3007
+ return;
3008
+ }
3009
+ await uploadStagedFileToPresignedUrl({
3010
+ url: upload.uploadUrl,
3011
+ headers: upload.uploadHeaders ?? {
3012
+ "content-type": file.contentType
3013
+ },
3014
+ body: decodeBase64Bytes(file.contentBase64),
3015
+ logicalPath: file.logicalPath
3016
+ });
3017
+ })
3018
+ );
3019
+ return files.map((file) => {
3020
+ const upload = uploadByIdentity.get(
3021
+ stagedUploadIdentity(file.logicalPath, file.contentHash)
3022
+ );
3023
+ if (!upload) {
3024
+ throw new DeeplineError(
3025
+ `The staging server did not return an upload target for ${file.logicalPath}.`,
3026
+ void 0,
3027
+ "STAGED_FILE_MINT_INCOMPLETE"
3028
+ );
3029
+ }
3030
+ return upload.ref;
3031
+ });
3032
+ }
3033
+ /**
3034
+ * Mint short-lived presigned upload targets for staged play files.
3035
+ *
3036
+ * Internal primitive used by {@link stagePlayFiles}. The server returns an
3037
+ * already-staged ref (no upload needed) for content-addressed files it
3038
+ * already holds, or a presigned PUT URL the caller uploads the body to.
3039
+ */
3040
+ async mintStagedPlayFileUploads(files) {
3041
+ const response = await this.http.post("/api/v2/plays/files/stage/mint", { files });
3042
+ return response.uploads ?? [];
3043
+ }
3044
+ async stagePlayFilesViaMultipart(files) {
3045
+ for (const file of files) {
3046
+ if (file.bytes > STAGE_LEGACY_MULTIPART_MAX_BYTES) {
3047
+ throw new DeeplineError(
3048
+ `Cannot stage ${file.logicalPath} (${formatMegabytes(file.bytes)}): the connected Deepline server does not support direct-to-storage uploads, and this file exceeds the ~4.5MB request-body limit the legacy upload path is subject to. Update the Deepline server (or target code.deepline.com) to stage files this large.`,
3049
+ 413,
3050
+ "STAGED_FILE_TOO_LARGE_FOR_LEGACY_UPLOAD",
3051
+ {
3052
+ logicalPath: file.logicalPath,
3053
+ bytes: file.bytes,
3054
+ maxBytes: STAGE_LEGACY_MULTIPART_MAX_BYTES
3055
+ }
3056
+ );
3057
+ }
3058
+ }
2839
3059
  const buildFormData = () => {
2840
3060
  const formData = new FormData();
2841
3061
  formData.set(
package/dist/index.mjs CHANGED
@@ -352,10 +352,10 @@ var SDK_RELEASE = {
352
352
  // 0.1.154 removes the short-lived generated enrich StepOptions recompute
353
353
  // fields shipped in 0.1.153.
354
354
  // 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
355
- version: "0.1.178",
355
+ version: "0.1.180",
356
356
  apiContract: "2026-06-dataset-handle-results-hard-cutover",
357
357
  supportPolicy: {
358
- latest: "0.1.178",
358
+ latest: "0.1.180",
359
359
  minimumSupported: "0.1.53",
360
360
  deprecatedBelow: "0.1.53",
361
361
  commandMinimumSupported: [
@@ -516,6 +516,58 @@ var SYNTHETIC_RUN_HEADER = "x-deepline-synthetic-run";
516
516
  // src/http.ts
517
517
  var MAX_DIAGNOSTIC_HEADER_LENGTH = 120;
518
518
  var COWORK_NETWORK_HINT = "Claude Cowork appears to be running Deepline in a network-restricted sandbox. In Claude Desktop, open Settings > Capabilities, turn on Allow network egress, and set Domain allowlist to All domains for the Cowork session.";
519
+ var REQUEST_TIMEOUT_MARKER = /* @__PURE__ */ Symbol("deeplineRequestTimeout");
520
+ var REQUEST_ABORT_MARKER = /* @__PURE__ */ Symbol("deeplineRequestAbort");
521
+ function normalizeRequestAbortError(error, input) {
522
+ const normalized = error instanceof Error ? error : new Error(String(error));
523
+ const tagged = normalized;
524
+ if (input.timedOut) {
525
+ tagged[REQUEST_TIMEOUT_MARKER] = input.timeoutMs;
526
+ } else if (isAbortLikeError(normalized)) {
527
+ tagged[REQUEST_ABORT_MARKER] = true;
528
+ }
529
+ return normalized;
530
+ }
531
+ function isAbortLikeError(error) {
532
+ const name = error.name.toLowerCase();
533
+ const message = error.message.toLowerCase();
534
+ return name === "aborterror" || message.includes("operation was aborted") || message === "aborted";
535
+ }
536
+ function mapNetworkError(baseUrl, error, targetLabel) {
537
+ const tagged = error;
538
+ const timeoutMs = tagged?.[REQUEST_TIMEOUT_MARKER];
539
+ if (typeof timeoutMs === "number") {
540
+ return {
541
+ message: `Request to ${targetLabel} timed out after ${timeoutMs}ms while waiting for a response.`,
542
+ code: "NETWORK_TIMEOUT",
543
+ details: { timeoutMs, target: targetLabel }
544
+ };
545
+ }
546
+ if (tagged?.[REQUEST_ABORT_MARKER]) {
547
+ return {
548
+ message: `Request to ${targetLabel} was aborted before it completed.`,
549
+ code: "NETWORK_ABORTED"
550
+ };
551
+ }
552
+ return {
553
+ message: error?.message ? `Unable to connect to ${baseUrl}. ${error.message}` : `Unable to connect to ${baseUrl}. Is the computer able to access the url?`,
554
+ code: "NETWORK_ERROR"
555
+ };
556
+ }
557
+ function describeRequestTarget(path) {
558
+ const toolExecuteMatch = path.match(
559
+ /^\/api\/v2\/integrations\/([^/?#]+)\/execute(?:[/?#]|$)/
560
+ );
561
+ if (toolExecuteMatch) {
562
+ return providerLabelFromToolId(decodeURIComponent(toolExecuteMatch[1]));
563
+ }
564
+ return "Deepline";
565
+ }
566
+ function providerLabelFromToolId(toolId) {
567
+ const normalized = toolId.trim().toLowerCase();
568
+ const provider = normalized.split(/[_:./-]+/)[0];
569
+ return provider || normalized || "provider";
570
+ }
519
571
  var HttpClient = class {
520
572
  constructor(config) {
521
573
  this.config = config;
@@ -628,10 +680,12 @@ var HttpClient = class {
628
680
  }
629
681
  for (const candidateUrl of candidateUrls) {
630
682
  const controller = new AbortController();
631
- const timeoutId = setTimeout(
632
- () => controller.abort(),
633
- options?.timeout ?? this.config.timeout
634
- );
683
+ const timeoutMs = options?.timeout ?? this.config.timeout;
684
+ let requestTimedOut = false;
685
+ const timeoutId = setTimeout(() => {
686
+ requestTimedOut = true;
687
+ controller.abort();
688
+ }, timeoutMs);
635
689
  try {
636
690
  const response = await fetch(candidateUrl, {
637
691
  method,
@@ -704,7 +758,10 @@ var HttpClient = class {
704
758
  lastError = error;
705
759
  break;
706
760
  }
707
- lastError = error instanceof Error ? error : new Error(String(error));
761
+ lastError = normalizeRequestAbortError(error, {
762
+ timeoutMs,
763
+ timedOut: requestTimedOut
764
+ });
708
765
  }
709
766
  }
710
767
  if (attempt < maxRetries) continue;
@@ -712,8 +769,17 @@ var HttpClient = class {
712
769
  if (lastError instanceof DeeplineError) {
713
770
  throw lastError;
714
771
  }
715
- const errorMessage = lastError?.message ? `Unable to connect to ${baseUrl}. ${lastError.message}` : `Unable to connect to ${baseUrl}. Is the computer able to access the url?`;
716
- throw new DeeplineError(withCoworkNetworkHint(errorMessage));
772
+ const mappedNetworkError = mapNetworkError(
773
+ baseUrl,
774
+ lastError,
775
+ describeRequestTarget(path)
776
+ );
777
+ throw new DeeplineError(
778
+ withCoworkNetworkHint(mappedNetworkError.message),
779
+ void 0,
780
+ mappedNetworkError.code,
781
+ mappedNetworkError.details
782
+ );
717
783
  }
718
784
  /**
719
785
  * Send a GET request.
@@ -1984,6 +2050,57 @@ function decodeBase64Bytes(value) {
1984
2050
  }
1985
2051
  return bytes;
1986
2052
  }
2053
+ var STAGE_LEGACY_MULTIPART_MAX_BYTES = 4e6;
2054
+ var STAGE_DIRECT_UPLOAD_MAX_ATTEMPTS = 3;
2055
+ function stagedUploadIdentity(logicalPath, contentHash) {
2056
+ return `${contentHash}:${logicalPath}`;
2057
+ }
2058
+ function isStagedUploadMintUnsupported(error) {
2059
+ return error instanceof DeeplineError && error.statusCode === 404;
2060
+ }
2061
+ function formatMegabytes(bytes) {
2062
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
2063
+ }
2064
+ async function uploadStagedFileToPresignedUrl(input) {
2065
+ const arrayBuffer = input.body.buffer.slice(
2066
+ input.body.byteOffset,
2067
+ input.body.byteOffset + input.body.byteLength
2068
+ );
2069
+ let lastError;
2070
+ for (let attempt = 1; attempt <= STAGE_DIRECT_UPLOAD_MAX_ATTEMPTS; attempt += 1) {
2071
+ try {
2072
+ const response = await fetch(input.url, {
2073
+ method: "PUT",
2074
+ headers: input.headers,
2075
+ body: new Blob([arrayBuffer])
2076
+ });
2077
+ if (response.ok) {
2078
+ return;
2079
+ }
2080
+ const text = await response.text().catch(() => "");
2081
+ lastError = new DeeplineError(
2082
+ `Direct storage upload of ${input.logicalPath} failed: ${response.status} ${text.slice(0, 200)}`,
2083
+ response.status,
2084
+ "STAGED_FILE_UPLOAD_FAILED",
2085
+ { logicalPath: input.logicalPath }
2086
+ );
2087
+ if (response.status < 500 && response.status !== 429) {
2088
+ break;
2089
+ }
2090
+ } catch (error) {
2091
+ lastError = error;
2092
+ }
2093
+ if (attempt < STAGE_DIRECT_UPLOAD_MAX_ATTEMPTS) {
2094
+ await sleep2(250 * attempt);
2095
+ }
2096
+ }
2097
+ throw lastError instanceof Error ? lastError : new DeeplineError(
2098
+ `Direct storage upload of ${input.logicalPath} failed.`,
2099
+ void 0,
2100
+ "STAGED_FILE_UPLOAD_FAILED",
2101
+ { logicalPath: input.logicalPath }
2102
+ );
2103
+ }
1987
2104
  function readStringArray(value) {
1988
2105
  return Array.isArray(value) ? value.filter((line) => typeof line === "string") : [];
1989
2106
  }
@@ -2389,6 +2506,21 @@ var DeeplineClient = class {
2389
2506
  ...input.maxRows ? { max_rows: input.maxRows } : {}
2390
2507
  });
2391
2508
  }
2509
+ /**
2510
+ * Re-establish this workspace's tenant storage contract: role/DB connect
2511
+ * grants plus materialized table grants. Org-admin only. Use when a run fails
2512
+ * with WORKSPACE_STORAGE_NOT_READY.
2513
+ */
2514
+ async repairIngestionStorage(input) {
2515
+ return this.http.post(
2516
+ "/api/v2/ingestion/repair",
2517
+ {
2518
+ ...input?.provider ? { provider: input.provider } : {}
2519
+ },
2520
+ void 0,
2521
+ { forbiddenAsApiError: true }
2522
+ );
2523
+ }
2392
2524
  // ——————————————————————————————————————————————————————————
2393
2525
  // Plays — submission and lifecycle
2394
2526
  // ——————————————————————————————————————————————————————————
@@ -2766,6 +2898,94 @@ var DeeplineClient = class {
2766
2898
  * ```
2767
2899
  */
2768
2900
  async stagePlayFiles(files) {
2901
+ let uploads;
2902
+ try {
2903
+ uploads = await this.mintStagedPlayFileUploads(
2904
+ files.map((file) => ({
2905
+ logicalPath: file.logicalPath,
2906
+ contentHash: file.contentHash,
2907
+ contentType: file.contentType,
2908
+ bytes: file.bytes
2909
+ }))
2910
+ );
2911
+ } catch (error) {
2912
+ if (isStagedUploadMintUnsupported(error)) {
2913
+ return this.stagePlayFilesViaMultipart(files);
2914
+ }
2915
+ throw error;
2916
+ }
2917
+ const uploadByIdentity = /* @__PURE__ */ new Map();
2918
+ for (const upload of uploads) {
2919
+ uploadByIdentity.set(
2920
+ stagedUploadIdentity(upload.logicalPath, upload.contentHash),
2921
+ upload
2922
+ );
2923
+ }
2924
+ await Promise.all(
2925
+ files.map(async (file) => {
2926
+ const upload = uploadByIdentity.get(
2927
+ stagedUploadIdentity(file.logicalPath, file.contentHash)
2928
+ );
2929
+ if (!upload) {
2930
+ throw new DeeplineError(
2931
+ `The staging server did not return an upload target for ${file.logicalPath}.`,
2932
+ void 0,
2933
+ "STAGED_FILE_MINT_INCOMPLETE"
2934
+ );
2935
+ }
2936
+ if (upload.alreadyStaged || !upload.uploadUrl) {
2937
+ return;
2938
+ }
2939
+ await uploadStagedFileToPresignedUrl({
2940
+ url: upload.uploadUrl,
2941
+ headers: upload.uploadHeaders ?? {
2942
+ "content-type": file.contentType
2943
+ },
2944
+ body: decodeBase64Bytes(file.contentBase64),
2945
+ logicalPath: file.logicalPath
2946
+ });
2947
+ })
2948
+ );
2949
+ return files.map((file) => {
2950
+ const upload = uploadByIdentity.get(
2951
+ stagedUploadIdentity(file.logicalPath, file.contentHash)
2952
+ );
2953
+ if (!upload) {
2954
+ throw new DeeplineError(
2955
+ `The staging server did not return an upload target for ${file.logicalPath}.`,
2956
+ void 0,
2957
+ "STAGED_FILE_MINT_INCOMPLETE"
2958
+ );
2959
+ }
2960
+ return upload.ref;
2961
+ });
2962
+ }
2963
+ /**
2964
+ * Mint short-lived presigned upload targets for staged play files.
2965
+ *
2966
+ * Internal primitive used by {@link stagePlayFiles}. The server returns an
2967
+ * already-staged ref (no upload needed) for content-addressed files it
2968
+ * already holds, or a presigned PUT URL the caller uploads the body to.
2969
+ */
2970
+ async mintStagedPlayFileUploads(files) {
2971
+ const response = await this.http.post("/api/v2/plays/files/stage/mint", { files });
2972
+ return response.uploads ?? [];
2973
+ }
2974
+ async stagePlayFilesViaMultipart(files) {
2975
+ for (const file of files) {
2976
+ if (file.bytes > STAGE_LEGACY_MULTIPART_MAX_BYTES) {
2977
+ throw new DeeplineError(
2978
+ `Cannot stage ${file.logicalPath} (${formatMegabytes(file.bytes)}): the connected Deepline server does not support direct-to-storage uploads, and this file exceeds the ~4.5MB request-body limit the legacy upload path is subject to. Update the Deepline server (or target code.deepline.com) to stage files this large.`,
2979
+ 413,
2980
+ "STAGED_FILE_TOO_LARGE_FOR_LEGACY_UPLOAD",
2981
+ {
2982
+ logicalPath: file.logicalPath,
2983
+ bytes: file.bytes,
2984
+ maxBytes: STAGE_LEGACY_MULTIPART_MAX_BYTES
2985
+ }
2986
+ );
2987
+ }
2988
+ }
2769
2989
  const buildFormData = () => {
2770
2990
  const formData = new FormData();
2771
2991
  formData.set(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepline",
3
- "version": "0.1.178",
3
+ "version": "0.1.180",
4
4
  "description": "Deepline SDK + CLI — B2B data enrichment powered by durable cloud execution",
5
5
  "license": "MIT",
6
6
  "repository": {