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
@@ -2172,6 +2172,14 @@ export class DeeplineClient {
2172
2172
  : {}),
2173
2173
  ...(request.force ? { force: true } : {}),
2174
2174
  ...(forceToolRefresh ? { forceToolRefresh: true } : {}),
2175
+ ...(typeof request.maxConcurrentExternalCalls === 'number'
2176
+ ? {
2177
+ maxConcurrentExternalCalls: request.maxConcurrentExternalCalls,
2178
+ }
2179
+ : {}),
2180
+ ...(typeof request.maxConcurrentRows === 'number'
2181
+ ? { maxConcurrentRows: request.maxConcurrentRows }
2182
+ : {}),
2175
2183
  ...(typeof request.waitForCompletionMs === 'number'
2176
2184
  ? { waitForCompletionMs: request.waitForCompletionMs }
2177
2185
  : {}),
@@ -2247,6 +2255,12 @@ export class DeeplineClient {
2247
2255
  : {}),
2248
2256
  ...(request.force ? { force: true } : {}),
2249
2257
  ...(forceToolRefresh ? { forceToolRefresh: true } : {}),
2258
+ ...(typeof request.maxConcurrentExternalCalls === 'number'
2259
+ ? { maxConcurrentExternalCalls: request.maxConcurrentExternalCalls }
2260
+ : {}),
2261
+ ...(typeof request.maxConcurrentRows === 'number'
2262
+ ? { maxConcurrentRows: request.maxConcurrentRows }
2263
+ : {}),
2250
2264
  ...(typeof request.waitForCompletionMs === 'number'
2251
2265
  ? { waitForCompletionMs: request.waitForCompletionMs }
2252
2266
  : {}),
@@ -652,10 +652,28 @@ export class HttpClient {
652
652
  if (error instanceof AuthError || error instanceof DeeplineError) {
653
653
  throw error;
654
654
  }
655
- lastError = error instanceof Error ? error : new Error(String(error));
655
+ const normalized =
656
+ error instanceof Error ? error : new Error(String(error));
657
+ if (isAbortLikeError(normalized) && options?.signal?.aborted) {
658
+ throw new DeeplineError(
659
+ `Stream from ${this.config.baseUrl} was aborted by the caller.`,
660
+ undefined,
661
+ 'ABORTED',
662
+ );
663
+ }
664
+ lastError = normalized;
656
665
  }
657
666
  }
658
667
 
668
+ if (lastError && isAbortLikeError(lastError)) {
669
+ throw new DeeplineError(
670
+ withCoworkNetworkHint(
671
+ `Unable to stream from ${this.config.baseUrl}. The remote stream was interrupted.`,
672
+ ),
673
+ undefined,
674
+ 'PLAY_STREAM_NETWORK_ABORTED',
675
+ );
676
+ }
659
677
  throw new DeeplineError(
660
678
  withCoworkNetworkHint(
661
679
  lastError?.message
@@ -160,7 +160,7 @@ export const SDK_RELEASE = {
160
160
  // 0.2.0 makes Dataset Handles uniformly async-only after 0.1.320 briefly
161
161
  // exposed storage-dependent synchronous access. This deliberate minor
162
162
  // release keeps lazy paging semantics independent of row residency.
163
- version: '0.2.55',
163
+ version: '0.2.56',
164
164
  contracts: {
165
165
  api: {
166
166
  name: 'sdk-http-api',
@@ -30,6 +30,12 @@ export function streamReconnectDelayMs(attempt: number): number {
30
30
  }
31
31
 
32
32
  export function isTransientPlayStreamError(error: unknown): boolean {
33
+ if (
34
+ error instanceof DeeplineError &&
35
+ error.code === 'PLAY_STREAM_NETWORK_ABORTED'
36
+ ) {
37
+ return true;
38
+ }
33
39
  if (error instanceof DeeplineError && typeof error.statusCode === 'number') {
34
40
  // Server-shaped errors with a definite status code are NOT transient by
35
41
  // pattern — only network-level failures are. 5xx counts as transient
@@ -1488,6 +1488,13 @@ export interface StartPlayRunRequest {
1488
1488
  force?: boolean;
1489
1489
  /** Explicit cache-bypass flag for durable dataset and tool-call reuse. */
1490
1490
  forceToolRefresh?: boolean;
1491
+ /**
1492
+ * Per-run ceiling for concurrently resident provider-tool executions and
1493
+ * direct ctx.fetch calls. The server validates the supported range.
1494
+ */
1495
+ maxConcurrentExternalCalls?: number;
1496
+ /** Run-wide default and ceiling for live dataset-map row resolvers. */
1497
+ maxConcurrentRows?: number;
1491
1498
  /** Optionally let the start request wait briefly and return a terminal result. */
1492
1499
  waitForCompletionMs?: number;
1493
1500
  /**
@@ -1,4 +1,5 @@
1
1
  import { createWriteStream } from 'node:fs';
2
+ import { randomUUID } from 'node:crypto';
2
3
  import { stat } from 'node:fs/promises';
3
4
  import { Readable } from 'node:stream';
4
5
  import { pipeline } from 'node:stream/promises';
@@ -39,13 +40,18 @@ import type {
39
40
  PlayRunnerRateStateReleaseInput,
40
41
  } from '@shared_libs/play-runtime/protocol';
41
42
  import {
43
+ PLAY_RUNTIME_TRANSPORT_ATTEMPT_HEADER,
42
44
  PLAY_RUNTIME_CONTRACT,
43
45
  PLAY_RUNTIME_CONTRACT_HEADER,
44
46
  } from '@shared_libs/play-runtime/runtime-contract';
45
47
  import { PLAY_RUNTIME_API_COMPAT_PATH } from '@shared_libs/play-runtime/runtime-api-paths';
46
- import { PLAY_RUNTIME_TEST_FAULT_HEADER } from '@shared_libs/play-runtime/test-runtime-seams';
48
+ import {
49
+ PLAY_RUNTIME_TEST_FAULT_HEADER,
50
+ recognizedRuntimeTestFaultCount,
51
+ } from '@shared_libs/play-runtime/test-runtime-seams';
47
52
  import { vercelProtectionBypassHeaders } from '@shared_libs/play-runtime/vercel-protection';
48
53
  import type { RuntimeReceiptAction } from '@shared_libs/play-runtime/runtime-actions';
54
+ import { RUNTIME_CAPACITY_POLICY } from '@shared_libs/play-runtime/runtime-capacity-policy';
49
55
 
50
56
  export type StoredPlayArtifactPayload = {
51
57
  sourceCode: string;
@@ -244,7 +250,8 @@ function applyRetryJitter(delayMs: number): number {
244
250
  const half = delayMs / 2;
245
251
  return Math.round(half + Math.random() * half);
246
252
  }
247
- const APP_RUNTIME_API_DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
253
+ const APP_RUNTIME_API_DEFAULT_REQUEST_TIMEOUT_MS =
254
+ RUNTIME_CAPACITY_POLICY.receiptGateway.requestTimeoutMs;
248
255
  const APP_RUNTIME_RECEIPT_RETRY_TELEMETRY_TAG =
249
256
  '[perf][worker.receipt_api.transport]';
250
257
  const RUN_STATUS_LEDGER_SNAPSHOT_CACHE_LIMIT = 1_000;
@@ -254,10 +261,103 @@ const runStatusLedgerSnapshots = new Map<string, PlayRunLedgerSnapshot>();
254
261
  // ingestion skips overlap within that same attempt.
255
262
  const runLogChannelSentCounts = new Map<string, number>();
256
263
  const runStatusUpdateChains = new Map<string, Promise<void>>();
264
+ const receiptClaimResponseTimeoutFaults = new Map<string, number>();
265
+ const receiptClaimQueryHoldFaults = new Set<string>();
266
+ const RECEIPT_CLAIM_RESPONSE_TIMEOUT_FAULT_LIMIT = 1_000;
257
267
  const APP_RUNTIME_LOG_EVENT_MAX_JSON_BYTES = 128 * 1024;
258
268
  const APP_RUNTIME_EVENT_BATCH_MAX_JSON_BYTES = 512 * 1024;
259
269
  const appRuntimeTextEncoder = new TextEncoder();
260
270
 
271
+ function shouldSuppressBulkClaimResponse(input: {
272
+ body: RuntimeApiRequest;
273
+ runtimeTestFaultHeader?: string | null;
274
+ }): boolean {
275
+ if (
276
+ input.body.action !== 'claim_runtime_step_receipts' ||
277
+ input.body.keys.length < 128
278
+ ) {
279
+ return false;
280
+ }
281
+ const requested = recognizedRuntimeTestFaultCount(
282
+ input.runtimeTestFaultHeader,
283
+ 'receipt_claim_response_timeout',
284
+ );
285
+ if (requested <= 0) return false;
286
+ const key = `${input.body.runId}:${input.body.runAttempt ?? 0}`;
287
+ const consumed = receiptClaimResponseTimeoutFaults.get(key) ?? 0;
288
+ if (consumed >= requested) return false;
289
+ receiptClaimResponseTimeoutFaults.set(key, consumed + 1);
290
+ while (
291
+ receiptClaimResponseTimeoutFaults.size >
292
+ RECEIPT_CLAIM_RESPONSE_TIMEOUT_FAULT_LIMIT
293
+ ) {
294
+ const oldest = receiptClaimResponseTimeoutFaults.keys().next().value;
295
+ if (typeof oldest !== 'string') break;
296
+ receiptClaimResponseTimeoutFaults.delete(oldest);
297
+ }
298
+ return true;
299
+ }
300
+
301
+ function runtimeTestFaultHeaderForRequest(input: {
302
+ body: RuntimeApiRequest;
303
+ runtimeTestFaultHeader?: string | null;
304
+ }): string | null {
305
+ const header = input.runtimeTestFaultHeader?.trim();
306
+ if (!header) return null;
307
+ if (
308
+ input.body.action !== 'claim_runtime_step_receipts' ||
309
+ input.body.keys.length < 128 ||
310
+ recognizedRuntimeTestFaultCount(
311
+ header,
312
+ 'receipt_claim_query_hold_once_ms',
313
+ ) <= 0
314
+ ) {
315
+ return header;
316
+ }
317
+
318
+ const key = `${input.body.runId}:${input.body.runAttempt ?? 0}`;
319
+ if (!receiptClaimQueryHoldFaults.has(key)) {
320
+ receiptClaimQueryHoldFaults.add(key);
321
+ while (
322
+ receiptClaimQueryHoldFaults.size >
323
+ RECEIPT_CLAIM_RESPONSE_TIMEOUT_FAULT_LIMIT
324
+ ) {
325
+ const oldest = receiptClaimQueryHoldFaults.values().next().value;
326
+ if (typeof oldest !== 'string') break;
327
+ receiptClaimQueryHoldFaults.delete(oldest);
328
+ }
329
+ return header;
330
+ }
331
+
332
+ const filtered = header
333
+ .split(',')
334
+ .map((part) => part.trim())
335
+ .filter(
336
+ (part) =>
337
+ part &&
338
+ part.split(':', 1)[0]?.trim() !== 'receipt_claim_query_hold_once_ms',
339
+ )
340
+ .join(',');
341
+ return filtered || null;
342
+ }
343
+
344
+ async function suppressBulkClaimResponseUntilTimeout(
345
+ signal: AbortSignal,
346
+ ): Promise<never> {
347
+ if (signal.aborted) {
348
+ throw signal.reason ?? new Error('Runtime receipt request was aborted.');
349
+ }
350
+ return await new Promise<never>((_resolve, reject) => {
351
+ const onAbort = () => {
352
+ signal.removeEventListener('abort', onAbort);
353
+ reject(
354
+ signal.reason ?? new Error('Runtime receipt request was aborted.'),
355
+ );
356
+ };
357
+ signal.addEventListener('abort', onAbort, { once: true });
358
+ });
359
+ }
360
+
261
361
  function splitRunEventForAppRuntime(
262
362
  event: PlayRunLedgerEvent,
263
363
  ): PlayRunLedgerEvent[] {
@@ -633,6 +733,7 @@ type AppRuntimeRetryFailureKind =
633
733
 
634
734
  type AppRuntimeRetryFailure = {
635
735
  attempt: number;
736
+ transportAttemptId: string;
636
737
  failureKind: AppRuntimeRetryFailureKind;
637
738
  attemptElapsedMs: number;
638
739
  retryDelayMs: number;
@@ -674,6 +775,7 @@ function appRuntimeTransportFailureKind(
674
775
  function recordAppRuntimeRetryFailure(input: {
675
776
  telemetry: AppRuntimeRetryTelemetry;
676
777
  attempt: number;
778
+ transportAttemptId: string;
677
779
  failureKind: AppRuntimeRetryFailureKind;
678
780
  attemptStartedAt: number;
679
781
  retryDelayMs: number;
@@ -685,6 +787,7 @@ function recordAppRuntimeRetryFailure(input: {
685
787
  if (input.telemetry.failures.length < 8) {
686
788
  input.telemetry.failures.push({
687
789
  attempt: input.attempt,
790
+ transportAttemptId: input.transportAttemptId,
688
791
  failureKind: input.failureKind,
689
792
  attemptElapsedMs: Date.now() - input.attemptStartedAt,
690
793
  retryDelayMs: input.retryDelayMs,
@@ -734,6 +837,7 @@ function emitAppRuntimeRetrySummary(input: {
734
837
  async function retryAppRuntimeBodyTimeoutOrThrow(input: {
735
838
  action: RuntimeApiRequest['action'];
736
839
  attempt: number;
840
+ transportAttemptId: string;
737
841
  maxAttempts: number;
738
842
  error: unknown;
739
843
  telemetry: AppRuntimeRetryTelemetry;
@@ -746,6 +850,7 @@ async function retryAppRuntimeBodyTimeoutOrThrow(input: {
746
850
  recordAppRuntimeRetryFailure({
747
851
  telemetry: input.telemetry,
748
852
  attempt: input.attempt,
853
+ transportAttemptId: input.transportAttemptId,
749
854
  failureKind: 'response_body_error',
750
855
  attemptStartedAt: input.attemptStartedAt,
751
856
  retryDelayMs: 0,
@@ -775,6 +880,7 @@ async function retryAppRuntimeBodyTimeoutOrThrow(input: {
775
880
  recordAppRuntimeRetryFailure({
776
881
  telemetry: input.telemetry,
777
882
  attempt: input.attempt,
883
+ transportAttemptId: input.transportAttemptId,
778
884
  failureKind: 'response_body_timeout',
779
885
  attemptStartedAt: input.attemptStartedAt,
780
886
  retryDelayMs,
@@ -786,6 +892,7 @@ async function retryAppRuntimeBodyTimeoutOrThrow(input: {
786
892
  recordAppRuntimeRetryFailure({
787
893
  telemetry: input.telemetry,
788
894
  attempt: input.attempt,
895
+ transportAttemptId: input.transportAttemptId,
789
896
  failureKind: 'response_body_timeout',
790
897
  attemptStartedAt: input.attemptStartedAt,
791
898
  retryDelayMs: 0,
@@ -802,6 +909,7 @@ async function retryAppRuntimeBodyTimeoutOrThrow(input: {
802
909
  throw new AppRuntimeApiTransportError({
803
910
  action: input.action,
804
911
  attempts: input.attempt,
912
+ transportAttemptId: input.transportAttemptId,
805
913
  cause: input.error,
806
914
  boundaryLabel: input.boundaryLabel,
807
915
  });
@@ -818,22 +926,28 @@ function runtimeApiBoundaryLabel(
818
926
  export class AppRuntimeApiTransportError extends Error {
819
927
  readonly action: RuntimeApiRequest['action'];
820
928
  readonly attempts: number;
929
+ readonly transportAttemptId: string | null;
821
930
 
822
931
  constructor(input: {
823
932
  action: RuntimeApiRequest['action'];
824
933
  attempts: number;
934
+ transportAttemptId?: string | null;
825
935
  cause: unknown;
826
936
  boundaryLabel?: string;
827
937
  }) {
828
938
  const causeMessage =
829
939
  input.cause instanceof Error ? input.cause.message : String(input.cause);
830
940
  super(
831
- `${input.boundaryLabel ?? 'App runtime API'} transport exhausted action=${input.action} attempts=${input.attempts}: ${causeMessage}`,
941
+ `${input.boundaryLabel ?? 'App runtime API'} transport exhausted action=${input.action} attempts=${input.attempts}: ${causeMessage}` +
942
+ (input.transportAttemptId
943
+ ? ` (transport_attempt=${input.transportAttemptId})`
944
+ : ''),
832
945
  { cause: input.cause },
833
946
  );
834
947
  this.name = 'AppRuntimeApiTransportError';
835
948
  this.action = input.action;
836
949
  this.attempts = input.attempts;
950
+ this.transportAttemptId = input.transportAttemptId ?? null;
837
951
  }
838
952
  }
839
953
 
@@ -874,6 +988,7 @@ export class AppRuntimeApiResponseError extends Error {
874
988
 
875
989
  const APP_RUNTIME_CAPACITY_ERROR_CODES = new Set([
876
990
  'receipt_db_admission_backpressure',
991
+ 'runtime_receipt_claim_timeout',
877
992
  'scheduler_capacity',
878
993
  'runtime_postgres_admission_backpressure',
879
994
  'RUNTIME_SCHEDULER_SATURATED',
@@ -970,7 +1085,15 @@ async function postAppRuntimeApi<TResponse>(
970
1085
 
971
1086
  for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
972
1087
  const attemptStartedAt = Date.now();
1088
+ const transportAttemptId = randomUUID();
1089
+ const requestSignal = context.signal
1090
+ ? AbortSignal.any([context.signal, AbortSignal.timeout(requestTimeoutMs)])
1091
+ : AbortSignal.timeout(requestTimeoutMs);
973
1092
  let response: Response;
1093
+ const runtimeTestFaultHeader = runtimeTestFaultHeaderForRequest({
1094
+ body,
1095
+ runtimeTestFaultHeader: context.runtimeTestFaultHeader,
1096
+ });
974
1097
  try {
975
1098
  response = await runtimeFetch(resolveAppRuntimeApiUrl(context), {
976
1099
  method: 'POST',
@@ -978,21 +1101,16 @@ async function postAppRuntimeApi<TResponse>(
978
1101
  'content-type': 'application/json',
979
1102
  authorization: `Bearer ${token}`,
980
1103
  [PLAY_RUNTIME_CONTRACT_HEADER]: String(PLAY_RUNTIME_CONTRACT),
1104
+ [PLAY_RUNTIME_TRANSPORT_ATTEMPT_HEADER]: transportAttemptId,
981
1105
  ...vercelHeaders,
982
- ...(context.runtimeTestFaultHeader?.trim()
1106
+ ...(runtimeTestFaultHeader
983
1107
  ? {
984
- [PLAY_RUNTIME_TEST_FAULT_HEADER]:
985
- context.runtimeTestFaultHeader.trim(),
1108
+ [PLAY_RUNTIME_TEST_FAULT_HEADER]: runtimeTestFaultHeader,
986
1109
  }
987
1110
  : {}),
988
1111
  },
989
1112
  body: JSON.stringify(body),
990
- signal: context.signal
991
- ? AbortSignal.any([
992
- context.signal,
993
- AbortSignal.timeout(requestTimeoutMs),
994
- ])
995
- : AbortSignal.timeout(requestTimeoutMs),
1113
+ signal: requestSignal,
996
1114
  });
997
1115
  } catch (error) {
998
1116
  if (context.signal?.aborted) {
@@ -1008,6 +1126,7 @@ async function postAppRuntimeApi<TResponse>(
1008
1126
  recordAppRuntimeRetryFailure({
1009
1127
  telemetry: retryTelemetry,
1010
1128
  attempt,
1129
+ transportAttemptId,
1011
1130
  failureKind: appRuntimeTransportFailureKind(error),
1012
1131
  attemptStartedAt,
1013
1132
  retryDelayMs,
@@ -1018,6 +1137,7 @@ async function postAppRuntimeApi<TResponse>(
1018
1137
  recordAppRuntimeRetryFailure({
1019
1138
  telemetry: retryTelemetry,
1020
1139
  attempt,
1140
+ transportAttemptId,
1021
1141
  failureKind: appRuntimeTransportFailureKind(error),
1022
1142
  attemptStartedAt,
1023
1143
  retryDelayMs: 0,
@@ -1033,6 +1153,7 @@ async function postAppRuntimeApi<TResponse>(
1033
1153
  throw new AppRuntimeApiTransportError({
1034
1154
  action: body.action,
1035
1155
  attempts: attempt,
1156
+ transportAttemptId,
1036
1157
  cause: error,
1037
1158
  boundaryLabel,
1038
1159
  });
@@ -1040,6 +1161,14 @@ async function postAppRuntimeApi<TResponse>(
1040
1161
  if (response.ok) {
1041
1162
  try {
1042
1163
  const parsed = (await response.json()) as TResponse;
1164
+ if (
1165
+ shouldSuppressBulkClaimResponse({
1166
+ body,
1167
+ runtimeTestFaultHeader: context.runtimeTestFaultHeader,
1168
+ })
1169
+ ) {
1170
+ await suppressBulkClaimResponseUntilTimeout(requestSignal);
1171
+ }
1043
1172
  emitAppRuntimeRetrySummary({
1044
1173
  telemetry: retryTelemetry,
1045
1174
  action: body.action,
@@ -1053,6 +1182,7 @@ async function postAppRuntimeApi<TResponse>(
1053
1182
  await retryAppRuntimeBodyTimeoutOrThrow({
1054
1183
  action: body.action,
1055
1184
  attempt,
1185
+ transportAttemptId,
1056
1186
  maxAttempts,
1057
1187
  error,
1058
1188
  telemetry: retryTelemetry,
@@ -1074,6 +1204,7 @@ async function postAppRuntimeApi<TResponse>(
1074
1204
  recordAppRuntimeRetryFailure({
1075
1205
  telemetry: retryTelemetry,
1076
1206
  attempt,
1207
+ transportAttemptId,
1077
1208
  failureKind: 'response_body_timeout',
1078
1209
  attemptStartedAt,
1079
1210
  retryDelayMs: 0,
@@ -1095,6 +1226,7 @@ async function postAppRuntimeApi<TResponse>(
1095
1226
  await retryAppRuntimeBodyTimeoutOrThrow({
1096
1227
  action: body.action,
1097
1228
  attempt,
1229
+ transportAttemptId,
1098
1230
  maxAttempts,
1099
1231
  error,
1100
1232
  telemetry: retryTelemetry,
@@ -1135,6 +1267,7 @@ async function postAppRuntimeApi<TResponse>(
1135
1267
  recordAppRuntimeRetryFailure({
1136
1268
  telemetry: retryTelemetry,
1137
1269
  attempt,
1270
+ transportAttemptId,
1138
1271
  failureKind: 'retryable_http',
1139
1272
  attemptStartedAt,
1140
1273
  retryDelayMs,
@@ -1146,6 +1279,7 @@ async function postAppRuntimeApi<TResponse>(
1146
1279
  recordAppRuntimeRetryFailure({
1147
1280
  telemetry: retryTelemetry,
1148
1281
  attempt,
1282
+ transportAttemptId,
1149
1283
  failureKind: isRetryableAppRuntimeResponse({
1150
1284
  action: body.action,
1151
1285
  status: response.status,
@@ -52,8 +52,7 @@ function planExecutionChunks<TRequest>(
52
52
  for (const request of requests) {
53
53
  if (
54
54
  current.length > 0 &&
55
- (current.length >= maxUnits ||
56
- (weightCap != null && weight >= weightCap))
55
+ (current.length >= maxUnits || (weightCap != null && weight >= weightCap))
57
56
  ) {
58
57
  chunks.push(current);
59
58
  current = [];
@@ -91,6 +90,8 @@ export async function executeChunkedRequests<TRequest, TResult>(input: {
91
90
  onChunkComplete?: (
92
91
  results: Array<ChunkExecutionResult<TRequest, TResult>>,
93
92
  ) => void | Promise<void>;
93
+ /** Keep settled entries for the returned aggregate. Defaults to true. */
94
+ retainResults?: boolean;
94
95
  }): Promise<Array<ChunkExecutionResult<TRequest, TResult>>> {
95
96
  const results: Array<ChunkExecutionResult<TRequest, TResult>> = [];
96
97
 
@@ -105,7 +106,9 @@ export async function executeChunkedRequests<TRequest, TResult>(input: {
105
106
  const notify = async (
106
107
  entry: ChunkExecutionResult<TRequest, TResult>,
107
108
  ): Promise<void> => {
108
- results.push(entry);
109
+ if (input.retainResults !== false) {
110
+ results.push(entry);
111
+ }
109
112
  notifyChain = notifyChain.then(
110
113
  async () => await input.onChunkComplete?.([entry]),
111
114
  );