deepline 0.3.87 → 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.
- package/dist/bundling-sources/sdk/src/client.ts +532 -2
- package/dist/bundling-sources/sdk/src/config.ts +77 -0
- package/dist/bundling-sources/sdk/src/http.ts +76 -30
- package/dist/bundling-sources/sdk/src/index.ts +6 -0
- package/dist/bundling-sources/sdk/src/play.ts +15 -1
- package/dist/bundling-sources/sdk/src/plays/bundle-play-file.ts +1 -1
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/sdk/src/types.ts +83 -4
- package/dist/bundling-sources/shared_libs/monitors/deactivation-reasons.ts +16 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-batch-operation.ts +263 -0
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-identity-claims.ts +9 -1
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-state.ts +122 -0
- package/dist/bundling-sources/shared_libs/play-runtime/activity-observation.ts +6 -11
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +90 -12
- package/dist/bundling-sources/shared_libs/play-runtime/async-play-invocation.ts +78 -0
- package/dist/bundling-sources/shared_libs/play-runtime/checkpoint-boundaries.ts +70 -0
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +407 -53
- package/dist/bundling-sources/shared_libs/play-runtime/coordinator-headers.ts +1 -32
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +57 -0
- package/dist/bundling-sources/shared_libs/play-runtime/default-batch-strategies.ts +20 -1
- package/dist/bundling-sources/shared_libs/play-runtime/execution-capabilities.ts +6 -0
- package/dist/bundling-sources/shared_libs/play-runtime/execution-protocol.ts +7 -1
- package/dist/bundling-sources/shared_libs/play-runtime/lane-retirement-gate.ts +189 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-semantics.ts +28 -0
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/run-failure.ts +23 -0
- package/dist/bundling-sources/shared_libs/play-runtime/run-snapshot-stream.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +18 -4
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-start-admission.ts +68 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/runtime-sandbox-reconciliation.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +10 -8
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-capacity-policy.ts +20 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-contract.ts +6 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-scheduler-topology.ts +24 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +30 -1
- package/dist/bundling-sources/shared_libs/plays/artifact-types.ts +6 -3
- package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +152 -17
- package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +19 -4
- package/dist/bundling-sources/shared_libs/plays/contracts.ts +6 -14
- package/dist/bundling-sources/shared_libs/plays/core.ts +11 -6
- package/dist/bundling-sources/shared_libs/plays/docflow-binding-owner.ts +16 -11
- package/dist/bundling-sources/shared_libs/plays/resolve-static-pipeline.ts +1 -1
- package/dist/bundling-sources/shared_libs/plays/slug.ts +115 -0
- package/dist/bundling-sources/shared_libs/plays/static-pipeline.ts +44 -14
- package/dist/bundling-sources/shared_libs/product-notifications/catalog.ts +877 -0
- package/dist/bundling-sources/shared_libs/product-notifications/configuration-draft.ts +456 -0
- package/dist/bundling-sources/shared_libs/product-notifications/configuration.ts +560 -0
- package/dist/bundling-sources/shared_libs/product-notifications/content.ts +28 -0
- package/dist/bundling-sources/shared_libs/product-notifications/default-policy.ts +59 -0
- package/dist/bundling-sources/shared_libs/product-notifications/email.ts +246 -0
- package/dist/bundling-sources/shared_libs/product-notifications/evaluation.ts +280 -0
- package/dist/bundling-sources/shared_libs/product-notifications/events.ts +433 -0
- package/dist/bundling-sources/shared_libs/product-notifications/legacy-slack.ts +80 -0
- package/dist/bundling-sources/shared_libs/product-notifications/workspace.ts +38 -0
- package/dist/bundling-sources/shared_libs/security/internal-actor.ts +50 -0
- package/dist/cli/index.js +1065 -190
- package/dist/cli/index.mjs +1052 -177
- package/dist/{compiler-manifest-Dbov16F7.d.mts → compiler-manifest-DsLbQaew.d.mts} +132 -8
- package/dist/{compiler-manifest-Dbov16F7.d.ts → compiler-manifest-DsLbQaew.d.ts} +132 -8
- package/dist/index.d.mts +833 -11
- package/dist/index.d.ts +833 -11
- package/dist/index.js +467 -38
- package/dist/index.mjs +467 -38
- package/dist/install-integrity.json +20 -2
- package/dist/plays/bundle-play-file.d.mts +8 -3
- package/dist/plays/bundle-play-file.d.ts +8 -3
- package/dist/plays/bundle-play-file.mjs +100 -20
- package/package.json +1 -1
|
@@ -109,7 +109,11 @@ import {
|
|
|
109
109
|
type ToolResponseContract,
|
|
110
110
|
type ToolResponseView,
|
|
111
111
|
} from '../plays/tool-response-contract';
|
|
112
|
-
import {
|
|
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
|
|
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:
|
|
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 ??
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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(') &&
|
|
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
|
-
|
|
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
|
-
?
|
|
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
|
-
|
|
9669
|
-
|
|
9670
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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:
|
|
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:
|
|
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
|
|
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: {
|
|
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 =
|
|
13639
|
-
|
|
13640
|
-
|
|
13641
|
-
|
|
13642
|
-
|
|
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
|
|
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 &&
|