deepline 0.3.88 → 0.3.89

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 (121) hide show
  1. package/dist/bundling-sources/sdk/src/client.ts +534 -4
  2. package/dist/bundling-sources/sdk/src/config.ts +77 -0
  3. package/dist/bundling-sources/sdk/src/http.ts +76 -30
  4. package/dist/bundling-sources/sdk/src/index.ts +6 -0
  5. package/dist/bundling-sources/sdk/src/monitor-fleet-contract.ts +1 -1
  6. package/dist/bundling-sources/sdk/src/play.ts +15 -1
  7. package/dist/bundling-sources/sdk/src/plays/bundle-play-file.ts +2 -2
  8. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  9. package/dist/bundling-sources/sdk/src/types.ts +83 -4
  10. package/dist/bundling-sources/shared_libs/integration-contracts/AGENTS.md +9 -0
  11. package/dist/bundling-sources/shared_libs/integration-contracts/README.md +3 -0
  12. package/dist/bundling-sources/shared_libs/integration-contracts/index.ts +2 -0
  13. package/dist/bundling-sources/shared_libs/monitors/analytics-install-guides.ts +77 -0
  14. package/dist/bundling-sources/shared_libs/monitors/billing/charges.ts +125 -0
  15. package/dist/bundling-sources/shared_libs/monitors/billing/policy-builders.ts +686 -0
  16. package/dist/bundling-sources/shared_libs/monitors/billing/pricing.ts +54 -0
  17. package/dist/bundling-sources/shared_libs/monitors/billing/public-pricing.ts +72 -0
  18. package/dist/bundling-sources/shared_libs/monitors/billing/session.ts +45 -0
  19. package/dist/bundling-sources/shared_libs/monitors/billing/state.ts +454 -0
  20. package/dist/bundling-sources/shared_libs/monitors/billing/tamradar.ts +260 -0
  21. package/dist/bundling-sources/shared_libs/monitors/billing/types.ts +215 -0
  22. package/dist/bundling-sources/shared_libs/monitors/callback-security.ts +333 -0
  23. package/dist/bundling-sources/shared_libs/monitors/cursor-page.ts +42 -0
  24. package/dist/bundling-sources/shared_libs/monitors/deactivation-reasons.ts +16 -0
  25. package/dist/bundling-sources/shared_libs/monitors/framework-managed-payload-fields.ts +71 -0
  26. package/dist/bundling-sources/shared_libs/monitors/generated/provider-monitor-callback-admission-targets.ts +16 -0
  27. package/dist/bundling-sources/shared_libs/monitors/http-error-response.ts +267 -0
  28. package/dist/bundling-sources/shared_libs/monitors/inbound-publish-key.ts +39 -0
  29. package/dist/bundling-sources/shared_libs/monitors/inbound-queue.ts +82 -0
  30. package/dist/bundling-sources/shared_libs/monitors/lifecycle-notifications.ts +473 -0
  31. package/dist/bundling-sources/shared_libs/monitors/org-monitor-limits.ts +1 -1
  32. package/dist/bundling-sources/shared_libs/monitors/package.json +29 -0
  33. package/dist/bundling-sources/shared_libs/monitors/provider-callback-verification.ts +1125 -0
  34. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-batch-operation.ts +263 -0
  35. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-capability.ts +964 -0
  36. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-consumer-health.ts +56 -0
  37. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-errors.ts +131 -0
  38. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-identity-claims.ts +535 -0
  39. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-legacy-aliases.ts +16 -0
  40. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-priority-execution.ts +56 -0
  41. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-public-aliases.ts +28 -0
  42. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-runtime-read-eligibility.ts +38 -0
  43. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-state.ts +122 -0
  44. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-status.ts +10 -0
  45. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-upstream-identity.ts +112 -0
  46. package/dist/bundling-sources/shared_libs/monitors/provider-webhook-monitor-runtime.ts +300 -0
  47. package/dist/bundling-sources/shared_libs/monitors/quickstart-prompts.ts +22 -0
  48. package/dist/bundling-sources/shared_libs/monitors/snitcher-install-validation.ts +93 -0
  49. package/dist/bundling-sources/shared_libs/monitors/snitcher-ip2company.ts +332 -0
  50. package/dist/bundling-sources/shared_libs/monitors/snitcher-radar.ts +221 -0
  51. package/dist/bundling-sources/shared_libs/monitors/starter-templates.ts +105 -0
  52. package/dist/bundling-sources/shared_libs/monitors/tracking-provider-guidance.ts +73 -0
  53. package/dist/bundling-sources/shared_libs/monitors/tracking-provider-integrations.ts +81 -0
  54. package/dist/bundling-sources/shared_libs/monitors/tsconfig.json +10 -0
  55. package/dist/bundling-sources/shared_libs/play-runtime/activity-observation.ts +6 -11
  56. package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +90 -12
  57. package/dist/bundling-sources/shared_libs/play-runtime/async-play-invocation.ts +78 -0
  58. package/dist/bundling-sources/shared_libs/play-runtime/checkpoint-boundaries.ts +70 -0
  59. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +407 -53
  60. package/dist/bundling-sources/shared_libs/play-runtime/coordinator-headers.ts +1 -32
  61. package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +57 -0
  62. package/dist/bundling-sources/shared_libs/play-runtime/default-batch-strategies.ts +20 -1
  63. package/dist/bundling-sources/shared_libs/play-runtime/execution-capabilities.ts +6 -0
  64. package/dist/bundling-sources/shared_libs/play-runtime/execution-protocol.ts +7 -1
  65. package/dist/bundling-sources/shared_libs/play-runtime/lane-retirement-gate.ts +189 -0
  66. package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-semantics.ts +28 -0
  67. package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +2 -0
  68. package/dist/bundling-sources/shared_libs/play-runtime/run-failure.ts +23 -0
  69. package/dist/bundling-sources/shared_libs/play-runtime/run-snapshot-stream.ts +1 -1
  70. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +18 -4
  71. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-start-admission.ts +68 -0
  72. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +2 -0
  73. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/runtime-sandbox-reconciliation.ts +1 -0
  74. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +10 -8
  75. package/dist/bundling-sources/shared_libs/play-runtime/runtime-capacity-policy.ts +20 -0
  76. package/dist/bundling-sources/shared_libs/play-runtime/runtime-contract.ts +6 -0
  77. package/dist/bundling-sources/shared_libs/play-runtime/runtime-env.ts +6 -0
  78. package/dist/bundling-sources/shared_libs/play-runtime/runtime-scheduler-topology.ts +24 -0
  79. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +30 -1
  80. package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +1 -1
  81. package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +1 -1
  82. package/dist/bundling-sources/shared_libs/plays/artifact-types.ts +6 -3
  83. package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +454 -17
  84. package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +19 -4
  85. package/dist/bundling-sources/shared_libs/plays/contracts.ts +6 -14
  86. package/dist/bundling-sources/shared_libs/plays/core.ts +11 -6
  87. package/dist/bundling-sources/shared_libs/plays/docflow-binding-owner.ts +16 -11
  88. package/dist/bundling-sources/shared_libs/plays/email-status.ts +1 -1
  89. package/dist/bundling-sources/shared_libs/plays/resolve-static-pipeline.ts +1 -1
  90. package/dist/bundling-sources/shared_libs/plays/slug.ts +115 -0
  91. package/dist/bundling-sources/shared_libs/plays/static-pipeline.ts +44 -14
  92. package/dist/bundling-sources/shared_libs/plays/tool-category-descriptions.ts +2 -2
  93. package/dist/bundling-sources/shared_libs/product-notifications/catalog.ts +877 -0
  94. package/dist/bundling-sources/shared_libs/product-notifications/configuration-draft.ts +456 -0
  95. package/dist/bundling-sources/shared_libs/product-notifications/configuration.ts +560 -0
  96. package/dist/bundling-sources/shared_libs/product-notifications/content.ts +28 -0
  97. package/dist/bundling-sources/shared_libs/product-notifications/default-policy.ts +59 -0
  98. package/dist/bundling-sources/shared_libs/product-notifications/email.ts +246 -0
  99. package/dist/bundling-sources/shared_libs/product-notifications/evaluation.ts +280 -0
  100. package/dist/bundling-sources/shared_libs/product-notifications/events.ts +433 -0
  101. package/dist/bundling-sources/shared_libs/product-notifications/legacy-slack.ts +80 -0
  102. package/dist/bundling-sources/shared_libs/product-notifications/workspace.ts +38 -0
  103. package/dist/bundling-sources/shared_libs/runtime-env.ts +6 -0
  104. package/dist/bundling-sources/shared_libs/security/header-safe-env.ts +28 -0
  105. package/dist/bundling-sources/shared_libs/security/internal-actor.ts +50 -0
  106. package/dist/bundling-sources/shared_libs/security/secret-redaction.ts +267 -0
  107. package/dist/cli/index.js +1070 -193
  108. package/dist/cli/index.mjs +1057 -180
  109. package/dist/{compiler-manifest-Dbov16F7.d.mts → compiler-manifest-DsLbQaew.d.mts} +132 -8
  110. package/dist/{compiler-manifest-Dbov16F7.d.ts → compiler-manifest-DsLbQaew.d.ts} +132 -8
  111. package/dist/index.d.mts +834 -12
  112. package/dist/index.d.ts +834 -12
  113. package/dist/index.js +469 -40
  114. package/dist/index.mjs +469 -40
  115. package/dist/install-integrity.json +65 -4
  116. package/dist/plays/bundle-play-file.d.mts +8 -3
  117. package/dist/plays/bundle-play-file.d.ts +8 -3
  118. package/dist/plays/bundle-play-file.mjs +100 -20
  119. package/package.json +3 -1
  120. /package/dist/bundling-sources/shared_libs/{integrations/bettercontact-execution-policy.ts → integration-contracts/bettercontact/execution-policy.ts} +0 -0
  121. /package/dist/bundling-sources/shared_libs/{integrations/theirstack-execution-policy.ts → integration-contracts/theirstack/execution-policy.ts} +0 -0
@@ -109,7 +109,11 @@ import {
109
109
  type ToolResponseContract,
110
110
  type ToolResponseView,
111
111
  } from '../plays/tool-response-contract';
112
- import { ProviderExhaustedError } from './run-failure';
112
+ import {
113
+ isProviderExhaustedFailure,
114
+ ProviderExhaustedError,
115
+ } from './run-failure';
116
+ import { PROVIDER_EXHAUSTED_MAX_WAIT_MS } from './governor/app-runtime-rate-state-backend';
113
117
  import {
114
118
  buildPlayContractCompatibility,
115
119
  normalizePlayContractCompatibility,
@@ -353,7 +357,11 @@ import type {
353
357
  PlayRowReadOrder,
354
358
  PlayRowMeta,
355
359
  PlaySheetCellProducer,
360
+ PlayAsyncCallOptions,
356
361
  } from './ctx-types';
362
+ import { assertKnownResolvedBoundary } from './checkpoint-boundaries';
363
+ import type { AsyncPlayRunRef } from '../plays/authoring-contract';
364
+ import { admitAsyncPlayLineage } from './async-play-invocation';
357
365
  import {
358
366
  MAX_CELL_READ_REFS,
359
367
  MAX_ROW_READ_CELLS,
@@ -1982,6 +1990,19 @@ function normalizeStepDescription(
1982
1990
  return trimmed ? trimmed : undefined;
1983
1991
  }
1984
1992
 
1993
+ function resolvePlayReferenceName(playRef: unknown): string {
1994
+ if (typeof playRef === 'string') return playRef;
1995
+ if (
1996
+ playRef === null ||
1997
+ (typeof playRef !== 'object' && typeof playRef !== 'function')
1998
+ ) {
1999
+ return '';
2000
+ }
2001
+ const reference = playRef as { playName?: unknown; name?: unknown };
2002
+ if (typeof reference.playName === 'string') return reference.playName;
2003
+ return typeof reference.name === 'string' ? reference.name : '';
2004
+ }
2005
+
1985
2006
  function emptyCheckpoint(): PlayCheckpoint {
1986
2007
  return {
1987
2008
  completedBatches: {},
@@ -2103,6 +2124,12 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
2103
2124
  >();
2104
2125
  private readonly ctxFetchHttpFailureDiagnosticIdentities = new Set<string>();
2105
2126
  private checkpoint: PlayCheckpoint;
2127
+ /**
2128
+ * A durable boundary is a control-flow handoff, not a customer-catchable
2129
+ * business failure. Keep it on the context as the source of truth so a
2130
+ * broad catch cannot turn the invocation into a normal completed result.
2131
+ */
2132
+ private pendingDurableSuspension: PlayExecutionSuspension | null = null;
2106
2133
  private readonly durableCallCacheEpochMs: number;
2107
2134
  /**
2108
2135
  * Durable tool receipts are the replay/cache authority for the execution
@@ -2215,6 +2242,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
2215
2242
  timeoutMs?: number;
2216
2243
  receiptWaitMs?: number;
2217
2244
  }): Promise<TOutput> => {
2245
+ this.assertNoPendingDurableSuspension();
2218
2246
  if (!request || typeof request !== 'object' || Array.isArray(request)) {
2219
2247
  throw new Error(
2220
2248
  'ctx.tools.execute requires a request object: ctx.tools.execute({ id, tool, input, description }).',
@@ -2303,6 +2331,28 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
2303
2331
  }
2304
2332
  }
2305
2333
 
2334
+ /**
2335
+ * Called at ctx effect boundaries and by the runner before it can settle a
2336
+ * normal result. This makes suspension sticky even when authored code uses a
2337
+ * broad catch around `await ctx.sleep(...)`.
2338
+ */
2339
+ assertNoPendingDurableSuspension(): void {
2340
+ if (this.pendingDurableSuspension) {
2341
+ throw new PlayExecutionSuspendedError(this.pendingDurableSuspension);
2342
+ }
2343
+ }
2344
+
2345
+ /**
2346
+ * The single seam that hands this invocation back to the scheduler. The
2347
+ * first suspension wins: a later boundary reached by code that caught the
2348
+ * first one cannot replace the handoff the scheduler already owes a resume.
2349
+ */
2350
+ private suspendDurably(suspension: PlayExecutionSuspension): never {
2351
+ this.pendingDurableSuspension ??= suspension;
2352
+ this.#options.onBatchComplete?.(this.checkpoint);
2353
+ throw new PlayExecutionSuspendedError(this.pendingDurableSuspension);
2354
+ }
2355
+
2306
2356
  async tool<TOutput = unknown>(
2307
2357
  key: string,
2308
2358
  toolId: string,
@@ -2475,11 +2525,12 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
2475
2525
  options.playId ||
2476
2526
  options.workflowId ||
2477
2527
  'anonymous-play';
2478
- const rootRunId = options.runId ?? options.workflowId ?? 'anonymous-run';
2528
+ const currentRunId = options.runId ?? options.workflowId ?? 'anonymous-run';
2529
+ const rootRunId = options.rootRunId ?? currentRunId;
2479
2530
  this.executionScope =
2480
2531
  options.executionScope ??
2481
2532
  createRootRunExecutionScope({
2482
- runId: rootRunId,
2533
+ runId: currentRunId,
2483
2534
  playId: rootPlayId,
2484
2535
  attempt:
2485
2536
  typeof options.runAttempt === 'number' &&
@@ -2489,7 +2540,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
2489
2540
  receiptNamespace: options.runtimeReceiptScope ?? rootPlayId,
2490
2541
  authority: {
2491
2542
  orgId: options.orgId ?? 'org',
2492
- workflowId: options.workflowId ?? rootRunId,
2543
+ workflowId: options.workflowId ?? currentRunId,
2493
2544
  integrationMode: options.integrationMode ?? null,
2494
2545
  capabilities: DEFAULT_RUNTIME_EXECUTION_CAPABILITIES,
2495
2546
  },
@@ -2505,7 +2556,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
2505
2556
  adapter: 'cjs_node20',
2506
2557
  scope: {
2507
2558
  orgId: options.orgId ?? 'org',
2508
- rootRunId: options.runId ?? options.workflowId ?? 'run',
2559
+ rootRunId,
2509
2560
  },
2510
2561
  rateState: options.rateState ?? new InMemoryRateStateBackend(),
2511
2562
  budgetState: options.budgetState,
@@ -2710,6 +2761,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
2710
2761
  private assertInlineChildContract(
2711
2762
  reason: 'dataset_child' | 'suspending_child',
2712
2763
  ): void {
2764
+ this.assertNoPendingDurableSuspension();
2713
2765
  const composition = this.activeInlineComposition;
2714
2766
  if (!composition) return;
2715
2767
  throw new Error(ctxRunPlayInlineOnlyMessage(composition.playName, reason));
@@ -4429,6 +4481,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
4429
4481
  }) => Promise<T>;
4430
4482
  },
4431
4483
  ): Promise<T> {
4484
+ this.assertNoPendingDurableSuspension();
4432
4485
  let releaseExternalCallSlot: (() => void) | null = null;
4433
4486
  const execute = async (leaseId: string | null): Promise<T> =>
4434
4487
  await opts.execute({
@@ -4471,7 +4524,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
4471
4524
  cacheEpochMs: this.durableCallCacheEpochMs,
4472
4525
  });
4473
4526
  try {
4474
- return await executeWithDurableRuntimeReceipt<T>({
4527
+ const output = await executeWithDurableRuntimeReceipt<T>({
4475
4528
  operation,
4476
4529
  id,
4477
4530
  runId: this.currentReceiptOwnerRunId,
@@ -4499,6 +4552,11 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
4499
4552
  log: (message) => this.runtimeLog(message),
4500
4553
  execute: ({ leaseId }) => execute(leaseId),
4501
4554
  });
4555
+ // Every receipt publication and cache return crosses this one success
4556
+ // seam. A customer catch cannot turn a pending durable handoff into a
4557
+ // reusable completed value.
4558
+ this.assertNoPendingDurableSuspension();
4559
+ return output;
4502
4560
  } finally {
4503
4561
  const release = releaseExternalCallSlot as (() => void) | null;
4504
4562
  release?.();
@@ -4705,6 +4763,62 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
4705
4763
  });
4706
4764
  }
4707
4765
 
4766
+ /**
4767
+ * A forced call bypasses the immutable Tool Result Receipt, so its physical
4768
+ * gateway invocation must bypass that receipt's replay identity too. Keep
4769
+ * the transport key deterministic for one logical call: a retry of that
4770
+ * call still converges, while a later explicit force gets a fresh identity.
4771
+ */
4772
+ private toolTransportReceiptKey(input: {
4773
+ toolId: string;
4774
+ resultReceiptKey: string;
4775
+ providerOperationKey: string;
4776
+ force?: boolean;
4777
+ }): string {
4778
+ if (input.force !== true) return input.resultReceiptKey;
4779
+ return `${buildDurableToolReceiptPrefix({
4780
+ orgId: this.#options.orgId,
4781
+ toolId: input.toolId,
4782
+ })}${stableDigest(`forced-provider-invocation:${input.providerOperationKey}`)}`;
4783
+ }
4784
+
4785
+ /**
4786
+ * Ordinary operations do not carry provider-idempotency metadata, but the
4787
+ * execution relay still needs a semantic signal that an explicit force is
4788
+ * a new provider operation. Unsafe-write operations already carry their
4789
+ * forced provider key directly, so only ordinary calls need this attested
4790
+ * canonical-operation salt.
4791
+ */
4792
+ private providerForceRefreshIdentitiesForToolCall(input: {
4793
+ operationKey: string;
4794
+ force?: boolean;
4795
+ requiresExecutionFence?: boolean;
4796
+ logicalCallId?: string;
4797
+ }): string[] {
4798
+ if (input.force !== true || input.requiresExecutionFence === true) {
4799
+ return [];
4800
+ }
4801
+ return [
4802
+ this.providerIdempotencyKeyForToolCall({
4803
+ operationKey: input.operationKey,
4804
+ force: true,
4805
+ logicalCallId: input.logicalCallId,
4806
+ }),
4807
+ ];
4808
+ }
4809
+
4810
+ private providerForceRefreshOptionsForToolCall(input: {
4811
+ operationKey: string;
4812
+ force?: boolean;
4813
+ requiresExecutionFence?: boolean;
4814
+ logicalCallId?: string;
4815
+ }): Pick<ToolExecutionApiOptions, 'providerOperationForceRefreshIdentities'> {
4816
+ const identities = this.providerForceRefreshIdentitiesForToolCall(input);
4817
+ return identities.length > 0
4818
+ ? { providerOperationForceRefreshIdentities: identities }
4819
+ : {};
4820
+ }
4821
+
4708
4822
  /**
4709
4823
  * Bounded per-cell producer trace (ADR 0018).
4710
4824
  *
@@ -6125,6 +6239,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
6125
6239
  input: TInput,
6126
6240
  options?: { description?: string },
6127
6241
  ): Promise<TOutput> {
6242
+ this.assertNoPendingDurableSuspension();
6128
6243
  if (!isRuntimeStepProgram(program)) {
6129
6244
  throw new Error('ctx.runSteps(program, input) requires steps().');
6130
6245
  }
@@ -6149,6 +6264,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
6149
6264
  input: MapFieldDefinition<T, TColumns>,
6150
6265
  options?: RuntimeDatasetOptions<T>,
6151
6266
  ): Promise<PlayDataset<Record<string, unknown>>> {
6267
+ this.assertNoPendingDurableSuspension();
6152
6268
  if (rowContext.getStore()) {
6153
6269
  throw new Error(
6154
6270
  'Nested ctx.dataset() is not supported. Flatten your columns into one dataset, or keep custom per-row logic inside a single column.',
@@ -8356,8 +8472,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
8356
8472
  await incrementalPersistence?.checkpoint(checkpointUpdates);
8357
8473
  await incrementalPersistence?.flush();
8358
8474
  this.pendingRowEventBoundaries = [];
8359
- this.#options.onBatchComplete?.(this.checkpoint);
8360
- throw new PlayExecutionSuspendedError({
8475
+ this.suspendDurably({
8361
8476
  kind: 'integration_event_batch',
8362
8477
  boundaries: uniqueBoundaries,
8363
8478
  });
@@ -8452,7 +8567,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
8452
8567
  if (
8453
8568
  (!continueOnProviderUnavailable &&
8454
8569
  step.continueOnProviderUnavailable !== true) ||
8455
- (!(error instanceof ProviderExhaustedError) &&
8570
+ (!isProviderExhaustedFailure(error) &&
8456
8571
  !isProviderUnavailable(error))
8457
8572
  ) {
8458
8573
  throw error;
@@ -8537,6 +8652,10 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
8537
8652
  let value: unknown;
8538
8653
  try {
8539
8654
  value = await runStep();
8655
+ // A step-program resolver has the same publication boundary as a
8656
+ // normal mapped field. Its customer callback may catch `ctx.sleep()`;
8657
+ // do not turn that parked invocation into a waterfall output/cell.
8658
+ this.assertNoPendingDurableSuspension();
8540
8659
  } catch (error) {
8541
8660
  // A provider admission, transient upstream failure, or exhausted
8542
8661
  // managed provider account is a visible unavailable leg, not a row
@@ -8545,7 +8664,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
8545
8664
  // author hand-writing error handling.
8546
8665
  if (
8547
8666
  !continueOnProviderUnavailable ||
8548
- (!(error instanceof ProviderExhaustedError) &&
8667
+ (!isProviderExhaustedFailure(error) &&
8549
8668
  !isProviderUnavailable(error))
8550
8669
  ) {
8551
8670
  throw error;
@@ -8755,7 +8874,9 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
8755
8874
 
8756
8875
  const source = Function.prototype.toString.call(resolver);
8757
8876
  return (
8758
- !source.includes('.tools.execute(') && !source.includes('.runPlay(')
8877
+ !source.includes('.tools.execute(') &&
8878
+ !source.includes('.runPlay(') &&
8879
+ !source.includes('.runPlayAsync(')
8759
8880
  );
8760
8881
  });
8761
8882
  }
@@ -9237,7 +9358,12 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
9237
9358
  return resolver;
9238
9359
  }
9239
9360
 
9240
- return await resolver(row, this, fields, index, previousCell);
9361
+ const value = await resolver(row, this, fields, index, previousCell);
9362
+ // This is the common user-code return seam for every mapped field path
9363
+ // (incremental, pure, and step-program adapters). Check before callers
9364
+ // serialize a cell, emit a completion event, or advance a map frame.
9365
+ this.assertNoPendingDurableSuspension();
9366
+ return value;
9241
9367
  }
9242
9368
 
9243
9369
  private toOutputRow<T>(item: T): Record<string, unknown> {
@@ -9592,14 +9718,23 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
9592
9718
  }
9593
9719
  const useDirectResultReceipts =
9594
9720
  cacheableToolResult && !bypassDirectResultCache;
9721
+ const providerOperationKey = this.providerIdempotencyKeyForToolCall({
9722
+ operationKey: providerIdempotencyKeyBase,
9723
+ force: cacheableToolResult && toolCachePolicy.force,
9724
+ logicalCallId,
9725
+ });
9595
9726
  const physicalDirectKey = useDirectResultReceipts
9596
- ? directCacheKey
9727
+ ? this.toolTransportReceiptKey({
9728
+ toolId,
9729
+ resultReceiptKey: directCacheKey,
9730
+ providerOperationKey,
9731
+ force: toolCachePolicy.force,
9732
+ })
9597
9733
  : `${buildDurableToolReceiptPrefix({
9598
9734
  orgId: this.#options.orgId ?? 'unknown-org',
9599
9735
  toolId,
9600
9736
  })}${stableDigest(`${this.currentRunId}:${logicalCallId}:always-fresh`)}`;
9601
9737
  const resultReceiptKey = directResultReceiptKey;
9602
- const providerOperationKey = providerIdempotencyKeyBase;
9603
9738
  const toolRetryPolicy = await this.#options
9604
9739
  .getToolRetryPolicy?.(toolId, input)
9605
9740
  .catch(() => null);
@@ -9665,16 +9800,20 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
9665
9800
  ? resultReceiptKey
9666
9801
  : physicalDirectKey,
9667
9802
  providerIdempotencyKey:
9668
- this.providerIdempotencyKeyForToolCall({
9669
- operationKey:
9670
- useDirectResultReceipts && operationKey
9671
- ? providerIdempotencyKeyBase
9672
- : physicalDirectKey,
9673
- force: cacheableToolResult && toolCachePolicy.force,
9674
- logicalCallId,
9675
- }),
9803
+ useDirectResultReceipts && operationKey
9804
+ ? providerOperationKey
9805
+ : physicalDirectKey,
9676
9806
  }
9677
9807
  : {}),
9808
+ ...(useDirectResultReceipts && operationKey
9809
+ ? this.providerForceRefreshOptionsForToolCall({
9810
+ operationKey: providerIdempotencyKeyBase,
9811
+ force: toolCachePolicy.force,
9812
+ requiresExecutionFence:
9813
+ toolRetryPolicy?.requiresExecutionFence,
9814
+ logicalCallId,
9815
+ })
9816
+ : {}),
9678
9817
  playNodeScope: buildPlayNodeScope({
9679
9818
  toolId,
9680
9819
  callKey: normalizedKey,
@@ -10019,6 +10158,8 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
10019
10158
  workflowId: this.#options.workflowId,
10020
10159
  orgId: this.#options.orgId,
10021
10160
  executorToken: this.#options.executorToken,
10161
+ slackHitlDefaultTimeoutMs:
10162
+ this.currentRuntimeSemantics.slackHitl.defaultTimeoutMs,
10022
10163
  };
10023
10164
  const basePreparedBoundary = await handler.prepare({
10024
10165
  payload: input,
@@ -10039,8 +10180,10 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
10039
10180
  }
10040
10181
  : basePreparedBoundary;
10041
10182
 
10042
- const existing =
10043
- this.checkpoint.resolvedBoundaries?.[preparedBoundary.boundaryId];
10183
+ const existing = assertKnownResolvedBoundary(
10184
+ preparedBoundary.boundaryId,
10185
+ this.checkpoint.resolvedBoundaries?.[preparedBoundary.boundaryId],
10186
+ );
10044
10187
  if (existing?.kind === 'integration_event' && 'output' in existing) {
10045
10188
  const output = recordOrNull(existing.output);
10046
10189
  if (output?.timed_out === true) {
@@ -10056,6 +10199,18 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
10056
10199
  ...(existing.messageRef ? { messageRef: existing.messageRef } : {}),
10057
10200
  },
10058
10201
  });
10202
+ if (handler.suspendAfterTimeoutMs !== undefined) {
10203
+ this.runtimeLog(
10204
+ `Integration event ${preparedBoundary.boundaryId}: timeout finalized externally; keeping Play parked`,
10205
+ );
10206
+ this.suspendDurably({
10207
+ kind: 'integration_event',
10208
+ boundaryId: preparedBoundary.boundaryId,
10209
+ eventKey: preparedBoundary.eventKey,
10210
+ timeoutMs: handler.suspendAfterTimeoutMs,
10211
+ ...(existing.messageRef ? { messageRef: existing.messageRef } : {}),
10212
+ });
10213
+ }
10059
10214
  }
10060
10215
  this.runtimeLog(
10061
10216
  `Integration event ${preparedBoundary.boundaryId}: recovered response from checkpoint`,
@@ -10105,9 +10260,8 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
10105
10260
  ...(boundary.messageRef ? { messageRef: boundary.messageRef } : {}),
10106
10261
  },
10107
10262
  };
10108
- this.#options.onBatchComplete?.(this.checkpoint);
10109
-
10110
10263
  if (rowScope) {
10264
+ this.#options.onBatchComplete?.(this.checkpoint);
10111
10265
  throw new PlayRowExecutionSuspendedError({
10112
10266
  boundaryId: boundary.boundaryId,
10113
10267
  eventKey: boundary.eventKey,
@@ -10116,7 +10270,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
10116
10270
  });
10117
10271
  }
10118
10272
 
10119
- throw new PlayExecutionSuspendedError({
10273
+ this.suspendDurably({
10120
10274
  kind: 'integration_event',
10121
10275
  boundaryId: boundary.boundaryId,
10122
10276
  eventKey: boundary.eventKey,
@@ -10131,6 +10285,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
10131
10285
  input: Record<string, unknown>,
10132
10286
  options?: PlayCallOptions,
10133
10287
  ): Promise<TOutput> {
10288
+ this.assertNoPendingDurableSuspension();
10134
10289
  if (
10135
10290
  arguments.length === 3 &&
10136
10291
  typeof playRef === 'object' &&
@@ -10140,19 +10295,16 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
10140
10295
  'ctx.runPlay(...) signature is ctx.runPlay(key, playRef, input, { description }). Add a stable call key as the first argument.',
10141
10296
  );
10142
10297
  }
10143
- const resolvedName =
10144
- typeof playRef === 'string'
10145
- ? playRef
10146
- : playRef && typeof playRef.playName === 'string'
10147
- ? playRef.playName
10148
- : playRef && typeof playRef.name === 'string'
10149
- ? playRef.name
10150
- : '';
10298
+ const resolvedName = resolvePlayReferenceName(playRef);
10151
10299
 
10152
10300
  if (!resolvedName.trim()) {
10153
10301
  throw new Error('ctx.runPlay(...) requires a resolvable play name.');
10154
10302
  }
10155
- validatePlayAuthoringField('ctx.runPlay.playRef', playRef);
10303
+ // Imported Play dependencies are callable proxy functions at runtime. The
10304
+ // authoring contract describes their durable identity, so validate the
10305
+ // normalized name rather than requiring the ephemeral handle itself to be
10306
+ // JSON-shaped.
10307
+ validatePlayAuthoringField('ctx.runPlay.playRef', resolvedName);
10156
10308
  validatePlayAuthoringField('ctx.runPlay.input', input);
10157
10309
  assertNoSecretTaint(input, 'ctx.runPlay input');
10158
10310
  validatePlayAuthoringField('ctx.runPlay.key', key);
@@ -10264,6 +10416,10 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
10264
10416
  },
10265
10417
  () => this.executeResolvedPlay(resolvedPlay, this, input),
10266
10418
  );
10419
+ // Inline composition shares this Play Invocation. A child may catch a
10420
+ // parent suspension, but it cannot publish child success while that
10421
+ // handoff is still pending.
10422
+ this.assertNoPendingDurableSuspension();
10267
10423
  this.inlineChildAggregates.ok += 1;
10268
10424
  if (rowStore) {
10269
10425
  this.emitScopedFieldMetaUpdate({
@@ -10313,6 +10469,81 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
10313
10469
  }
10314
10470
  }
10315
10471
 
10472
+ async runPlayAsync(
10473
+ key: string,
10474
+ playRef: string | { playName?: string; name?: string },
10475
+ input: Record<string, unknown>,
10476
+ options: PlayAsyncCallOptions,
10477
+ ): Promise<AsyncPlayRunRef> {
10478
+ const resolvedName = resolvePlayReferenceName(playRef);
10479
+ if (!resolvedName.trim()) {
10480
+ throw new Error(
10481
+ 'ctx.runPlayAsync(...) requires a resolvable Play name or definition handle.',
10482
+ );
10483
+ }
10484
+ validatePlayAuthoringField('ctx.runPlayAsync.playRef', resolvedName);
10485
+ validatePlayAuthoringField('ctx.runPlayAsync.input', input);
10486
+ assertNoSecretTaint(input, 'ctx.runPlayAsync input');
10487
+ validatePlayAuthoringField('ctx.runPlayAsync.key', key);
10488
+ validatePlayAuthoringField(
10489
+ 'ctx.runPlayAsync.options.description',
10490
+ options?.description,
10491
+ );
10492
+ const normalizedKey = this.normalizeContextKey(key, 'runPlayAsync');
10493
+ if (!this.#options.launchPlayAsync) {
10494
+ throw new Error(
10495
+ 'ctx.runPlayAsync(...) is unavailable because no async Play admission Adapter was configured.',
10496
+ );
10497
+ }
10498
+ const ancestryPlayIds =
10499
+ this.currentExecutionGovernor.snapshot().ancestryPlayIds;
10500
+ admitAsyncPlayLineage({
10501
+ ancestryPlayIds,
10502
+ childPlayName: resolvedName,
10503
+ });
10504
+
10505
+ const rowScope = rowContext.getStore();
10506
+ const invocationKey = sha256Hex(
10507
+ stableStringify({
10508
+ parentRunId: this.currentExecutionScope.logical.runId,
10509
+ callerScope: this.currentExecutionScope.receipt.namespace,
10510
+ key: normalizedKey,
10511
+ row: rowScope
10512
+ ? {
10513
+ tableNamespace: rowScope.tableNamespace ?? null,
10514
+ rowKey: rowScope.rowKey ?? null,
10515
+ rowId: rowScope.rowKey ? null : rowScope.rowId,
10516
+ fieldName: rowScope.fieldName ?? null,
10517
+ }
10518
+ : null,
10519
+ }),
10520
+ );
10521
+ const launched = await this.#options.launchPlayAsync({
10522
+ key: normalizedKey,
10523
+ invocationKey,
10524
+ playRef: resolvedName,
10525
+ input,
10526
+ description: options.description,
10527
+ schedulerSchema: this.#options.runtimeSchedulerSchema ?? null,
10528
+ ancestryPlayIds,
10529
+ });
10530
+ const step = {
10531
+ type: 'play_launch' as const,
10532
+ playId: resolvedName,
10533
+ runId: launched.runId,
10534
+ description: normalizeStepDescription(options?.description),
10535
+ };
10536
+ if (this.activeDatasetStep) {
10537
+ this.activeDatasetStep.substeps.push(step);
10538
+ } else {
10539
+ this.steps.push(step);
10540
+ }
10541
+ this.log(
10542
+ `ctx.runPlayAsync(${normalizedKey}): admitted ${resolvedName} as ${launched.runId}`,
10543
+ );
10544
+ return launched;
10545
+ }
10546
+
10316
10547
  /**
10317
10548
  * Replay-stable receipt namespace for primitives executed by one inline
10318
10549
  * `ctx.runPlay` invocation. Format
@@ -10577,7 +10808,10 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
10577
10808
  `sleep-${this.sleepBoundaryIndex}-${delayMs}`,
10578
10809
  );
10579
10810
  this.sleepBoundaryIndex += 1;
10580
- const existing = this.checkpoint.resolvedBoundaries?.[boundaryId];
10811
+ const existing = assertKnownResolvedBoundary(
10812
+ boundaryId,
10813
+ this.checkpoint.resolvedBoundaries?.[boundaryId],
10814
+ );
10581
10815
  if (existing?.kind === 'sleep' && existing.completedAt !== undefined) {
10582
10816
  return;
10583
10817
  }
@@ -10588,8 +10822,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
10588
10822
  delayMs,
10589
10823
  },
10590
10824
  };
10591
- this.#options.onBatchComplete?.(this.checkpoint);
10592
- throw new PlayExecutionSuspendedError({
10825
+ this.suspendDurably({
10593
10826
  kind: 'sleep',
10594
10827
  boundaryId,
10595
10828
  delayMs,
@@ -10759,7 +10992,10 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
10759
10992
  )}`,
10760
10993
  );
10761
10994
 
10762
- const existing = this.checkpoint.resolvedBoundaries?.[boundaryId];
10995
+ const existing = assertKnownResolvedBoundary(
10996
+ boundaryId,
10997
+ this.checkpoint.resolvedBoundaries?.[boundaryId],
10998
+ );
10763
10999
  // A transient request deliberately has no durable recovery path, so
10764
11000
  // its response stays in the running Play rather than a checkpoint.
10765
11001
  if (
@@ -10951,6 +11187,10 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
10951
11187
  options?.transient !== true &&
10952
11188
  !this.durableDirectToolResultsBackedByReceipts
10953
11189
  ) {
11190
+ // Publishing a fetch boundary makes the result replay-visible.
11191
+ // A caught durable suspension is still the invocation's outcome,
11192
+ // so never checkpoint over a pending handoff.
11193
+ this.assertNoPendingDurableSuspension();
10954
11194
  this.checkpoint.resolvedBoundaries = {
10955
11195
  ...(this.checkpoint.resolvedBoundaries ?? {}),
10956
11196
  [boundaryId]: {
@@ -10998,7 +11238,10 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
10998
11238
  `step-${scope}:${normalizedKey}${boundarySuffix}`,
10999
11239
  );
11000
11240
  const executeStep = async (): Promise<T> => {
11001
- const existing = this.checkpoint.resolvedBoundaries?.[boundaryId];
11241
+ const existing = assertKnownResolvedBoundary(
11242
+ boundaryId,
11243
+ this.checkpoint.resolvedBoundaries?.[boundaryId],
11244
+ );
11002
11245
  if (existing?.kind === 'step' && 'output' in existing) {
11003
11246
  this.runtimeLog(
11004
11247
  `ctx.step(${normalizedKey}): recovered result from checkpoint`,
@@ -11008,6 +11251,10 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
11008
11251
 
11009
11252
  const output = await run();
11010
11253
  assertJsonSerializableStepOutput(normalizedKey, output);
11254
+ // A caught durable suspension is still the invocation's outcome. Never
11255
+ // publish an enclosing step fallback that would make a later resume skip
11256
+ // the suspended work.
11257
+ this.assertNoPendingDurableSuspension();
11011
11258
  this.checkpoint.resolvedBoundaries = {
11012
11259
  ...(this.checkpoint.resolvedBoundaries ?? {}),
11013
11260
  [boundaryId]: {
@@ -11566,19 +11813,38 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
11566
11813
  }
11567
11814
  let releaseToolSlot: () => void = () => undefined;
11568
11815
  try {
11816
+ const providerOperationKey = this.providerIdempotencyKeyForToolCall(
11817
+ {
11818
+ operationKey:
11819
+ request.providerIdempotencyKeyBase ?? request.receiptKey!,
11820
+ force: request.force === true,
11821
+ logicalCallId: request.logicalCallId,
11822
+ },
11823
+ );
11569
11824
  const result = await this.executeMappedToolCallThroughModule({
11570
11825
  request,
11571
11826
  dispatchToolId: toolId,
11572
11827
  dispatchPayload: request.input,
11573
11828
  resultReceiptKey: request.receiptKey!,
11574
- providerOperationKey:
11575
- request.providerIdempotencyKeyBase ?? request.receiptKey!,
11829
+ providerOperationKey,
11576
11830
  resultReceipts: receipts,
11577
11831
  apiOptions: {
11578
11832
  playNodeScope: playNodeScopeForToolCallRequest(request),
11579
- durableCallReceiptKey: request.receiptKey,
11833
+ durableCallReceiptKey: this.toolTransportReceiptKey({
11834
+ toolId,
11835
+ resultReceiptKey: request.receiptKey!,
11836
+ providerOperationKey,
11837
+ force: request.force,
11838
+ }),
11580
11839
  completedResultCache: true,
11581
11840
  executionAuthScopeDigest: request.executionAuthScopeDigest,
11841
+ ...this.providerForceRefreshOptionsForToolCall({
11842
+ operationKey:
11843
+ request.providerIdempotencyKeyBase ?? request.receiptKey!,
11844
+ force: request.force,
11845
+ requiresExecutionFence: request.requiresExecutionFence,
11846
+ logicalCallId: request.logicalCallId,
11847
+ }),
11582
11848
  timeoutMs:
11583
11849
  typeof request.timeoutMs === 'number' && request.timeoutMs > 0
11584
11850
  ? request.timeoutMs
@@ -11705,6 +11971,12 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
11705
11971
  batch.memberRequests.length;
11706
11972
  throw new RuntimePersistenceCircuitOpenError(this.persistenceLatch);
11707
11973
  }
11974
+ const batchProviderActionVersion =
11975
+ (
11976
+ await this.#options.getToolActionCacheVersion?.(
11977
+ batch.batchOperation,
11978
+ )
11979
+ )?.trim() ?? '';
11708
11980
  const batchRequestByCallId = new Map(
11709
11981
  batch.memberRequests.map((request) => [request.callId, request]),
11710
11982
  );
@@ -11714,6 +11986,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
11714
11986
  members: ToolCallRequest[];
11715
11987
  compiled: CompiledRequestBatch<ToolCallRequest>;
11716
11988
  providerOperationKey: string;
11989
+ forceRefreshIdentities: string[];
11717
11990
  receiptLeaseExpiresAt: string | null;
11718
11991
  timeoutMs: number | undefined;
11719
11992
  }
@@ -11752,7 +12025,14 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
11752
12025
  call.tool.id,
11753
12026
  call.payload,
11754
12027
  {
11755
- durableCallReceiptKey: compatibility.resultReceiptKey,
12028
+ durableCallReceiptKey: this.toolTransportReceiptKey({
12029
+ toolId: call.tool.id,
12030
+ resultReceiptKey: compatibility.resultReceiptKey,
12031
+ providerOperationKey: detail.providerOperationKey,
12032
+ force: detail.members.some(
12033
+ (request) => request.force === true,
12034
+ ),
12035
+ }),
11756
12036
  completedResultCache: true,
11757
12037
  playNodeScope: playNodeScopeForToolCallRequest(
11758
12038
  detail.members[0]!,
@@ -11760,6 +12040,12 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
11760
12040
  ),
11761
12041
  executionAuthScopeDigest:
11762
12042
  detail.members[0]?.executionAuthScopeDigest ?? null,
12043
+ ...(detail.forceRefreshIdentities.length > 0
12044
+ ? {
12045
+ providerOperationForceRefreshIdentities:
12046
+ detail.forceRefreshIdentities,
12047
+ }
12048
+ : {}),
11763
12049
  receiptLeaseExpiresAt: detail.receiptLeaseExpiresAt,
11764
12050
  timeoutMs: detail.timeoutMs,
11765
12051
  },
@@ -11850,11 +12136,30 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
11850
12136
  'Native batch owned members changed provider operation during compilation.',
11851
12137
  );
11852
12138
  }
11853
- const providerOperationKey = aggregateReceiptKey;
12139
+ const forceRefreshIdentities = owned.flatMap((request) =>
12140
+ this.providerForceRefreshIdentitiesForToolCall({
12141
+ operationKey:
12142
+ this.providerOperationKeyForQueuedToolCall(request),
12143
+ force: request.force,
12144
+ requiresExecutionFence: request.requiresExecutionFence,
12145
+ logicalCallId: request.logicalCallId,
12146
+ }),
12147
+ );
12148
+ const providerOperationKey =
12149
+ buildDurableToolPayloadProviderIdempotencyKey({
12150
+ orgId: this.#options.orgId,
12151
+ toolId: batch.batchOperation,
12152
+ executionAuthScopeDigest:
12153
+ owned[0]?.executionAuthScopeDigest ?? null,
12154
+ providerActionVersion: batchProviderActionVersion,
12155
+ payload: compiled.batchPayload,
12156
+ forceRefreshIdentities,
12157
+ });
11854
12158
  dispatchDetails.set(aggregateReceiptKey, {
11855
12159
  members: owned,
11856
12160
  compiled,
11857
12161
  providerOperationKey,
12162
+ forceRefreshIdentities,
11858
12163
  receiptLeaseExpiresAt: owned.reduce<string | null>(
11859
12164
  (earliest, request) => {
11860
12165
  const expiresAt = request.receiptLeaseExpiresAt ?? null;
@@ -11877,7 +12182,10 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
11877
12182
  });
11878
12183
  return {
11879
12184
  ...members[0]!.call,
11880
- tool: { id: batch.batchOperation, providerActionVersion: '' },
12185
+ tool: {
12186
+ id: batch.batchOperation,
12187
+ providerActionVersion: batchProviderActionVersion,
12188
+ },
11881
12189
  payload: compiled.batchPayload,
11882
12190
  compatibility: {
11883
12191
  resultReceiptKey: aggregateReceiptKey,
@@ -11964,6 +12272,17 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
11964
12272
  for (const request of entry.request.memberRequests) {
11965
12273
  await reject(request, entry.error);
11966
12274
  }
12275
+ // A provider-unavailable physical batch is a logical outcome for
12276
+ // each member, not a map-fatal scheduler failure. Each rejected
12277
+ // row resolver now receives the typed error above, where a
12278
+ // waterfall may persist the failed leg and advance to its next
12279
+ // provider. Keep infrastructure and contract failures loud.
12280
+ if (
12281
+ isProviderExhaustedFailure(entry.error) ||
12282
+ isProviderUnavailable(entry.error)
12283
+ ) {
12284
+ continue;
12285
+ }
11967
12286
  throw entry.error;
11968
12287
  }
11969
12288
  if (!entry.result) {
@@ -12618,6 +12937,13 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
12618
12937
  force: owner.force === true,
12619
12938
  logicalCallId: owner.logicalCallId,
12620
12939
  }),
12940
+ ...this.providerForceRefreshOptionsForToolCall({
12941
+ operationKey:
12942
+ this.providerOperationKeyForQueuedToolCall(owner),
12943
+ force: owner.force,
12944
+ requiresExecutionFence: owner.requiresExecutionFence,
12945
+ logicalCallId: owner.logicalCallId,
12946
+ }),
12621
12947
  timeoutMs: resolveRuntimeTimeoutMsForClaimedOwners([
12622
12948
  owner,
12623
12949
  ]),
@@ -13232,6 +13558,16 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
13232
13558
  force: request.force === true,
13233
13559
  logicalCallId: request.logicalCallId,
13234
13560
  }),
13561
+ ...this.providerForceRefreshOptionsForToolCall({
13562
+ operationKey:
13563
+ this.providerOperationKeyForQueuedToolCall(
13564
+ request,
13565
+ ),
13566
+ force: request.force,
13567
+ requiresExecutionFence:
13568
+ request.requiresExecutionFence,
13569
+ logicalCallId: request.logicalCallId,
13570
+ }),
13235
13571
  receiptLeaseExpiresAt:
13236
13572
  request.receiptLeaseExpiresAt,
13237
13573
  heartbeatReceipt: () =>
@@ -13635,13 +13971,17 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
13635
13971
  `Tool ${toolId} requires a provider execution fence but no provider operation identity was supplied.`,
13636
13972
  );
13637
13973
  }
13638
- const canonicalOperationActionVersion = providerExecutionFenceKey
13639
- ? (
13640
- await this.#options.getToolActionCacheVersion?.(toolId)
13641
- )?.trim() || null
13642
- : null;
13974
+ const canonicalOperationActionVersion =
13975
+ providerExecutionFenceKey ||
13976
+ options?.providerOperationForceRefreshIdentities?.length
13977
+ ? (
13978
+ await this.#options.getToolActionCacheVersion?.(toolId)
13979
+ )?.trim() || null
13980
+ : null;
13643
13981
  const canonicalOperation =
13644
- providerExecutionFenceKey && canonicalOperationActionVersion
13982
+ (providerExecutionFenceKey ||
13983
+ options?.providerOperationForceRefreshIdentities?.length) &&
13984
+ canonicalOperationActionVersion
13645
13985
  ? {
13646
13986
  version: 1,
13647
13987
  tool_id: toolId,
@@ -14242,6 +14582,20 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
14242
14582
  toolId,
14243
14583
  failure.backpressureDelayMs,
14244
14584
  );
14585
+ // A Retry-After beyond the durable pacer's maximum wait has
14586
+ // no useful in-process retry. It is already an exhausted
14587
+ // provider outcome: surface the same typed error the next
14588
+ // provider admission would produce, without sleeping before a
14589
+ // waterfall can choose an independent fallback.
14590
+ if (
14591
+ failure.backpressureDelayMs > PROVIDER_EXHAUSTED_MAX_WAIT_MS
14592
+ ) {
14593
+ throw new ProviderExhaustedError({
14594
+ provider,
14595
+ retryAtMs: Date.now() + failure.backpressureDelayMs,
14596
+ cause: failure.error,
14597
+ });
14598
+ }
14245
14599
  }
14246
14600
  if (
14247
14601
  failure.shouldRetry &&