deepline 0.3.73 → 0.3.75
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 +279 -21
- package/dist/bundling-sources/sdk/src/config.ts +21 -3
- package/dist/bundling-sources/sdk/src/index.ts +1 -0
- package/dist/bundling-sources/sdk/src/monitor-fleet-contract.ts +1 -1
- package/dist/bundling-sources/sdk/src/play.ts +3 -6
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/sdk/src/types.ts +3 -2
- package/dist/bundling-sources/shared_libs/monitors/deactivation-reasons.ts +186 -0
- package/dist/bundling-sources/shared_libs/monitors/monitor-fleet-limits.ts +46 -3
- package/dist/bundling-sources/shared_libs/play-runtime/async-operation.ts +213 -55
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +243 -356
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +18 -0
- package/dist/bundling-sources/shared_libs/play-runtime/durable-call-cache.ts +13 -32
- package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +5 -1
- package/dist/bundling-sources/shared_libs/play-runtime/play-artifact-runtime-contract.ts +62 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-semantics.ts +122 -0
- package/dist/bundling-sources/shared_libs/play-runtime/resource-governor.ts +7 -2
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +11 -2
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-incident-drills.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/suspension.ts +16 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-call/native-batch.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-call/testkit.ts +5 -4
- package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +12 -2
- package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +1 -1
- package/dist/bundling-sources/shared_libs/plays/deployment-status.ts +74 -0
- package/dist/cli/index.js +919 -168
- package/dist/cli/index.mjs +919 -168
- package/dist/{compiler-manifest-CMNgA_JQ.d.mts → compiler-manifest-Dbov16F7.d.mts} +1 -1
- package/dist/{compiler-manifest-CMNgA_JQ.d.ts → compiler-manifest-Dbov16F7.d.ts} +1 -1
- package/dist/index.d.mts +177 -24
- package/dist/index.d.ts +177 -24
- package/dist/index.js +110 -13
- package/dist/index.mjs +110 -13
- package/dist/install-integrity.json +6 -2
- package/dist/plays/bundle-play-file.d.mts +2 -2
- package/dist/plays/bundle-play-file.d.ts +2 -2
- package/package.json +1 -1
|
@@ -100,7 +100,6 @@ import {
|
|
|
100
100
|
import { CTX_FETCH_EGRESS_TOOL_ID } from './builtin-pacing';
|
|
101
101
|
import {
|
|
102
102
|
legacyRawFromToolResponseRawV2,
|
|
103
|
-
normalizeToolResponseContract,
|
|
104
103
|
providerMetaFromToolResponseRawV2,
|
|
105
104
|
RAW_V2_TOOL_RESPONSE_CONTRACT,
|
|
106
105
|
type ToolResponseContract,
|
|
@@ -115,7 +114,6 @@ import {
|
|
|
115
114
|
isProviderUnavailable,
|
|
116
115
|
serializeToolExecutionFailure,
|
|
117
116
|
ToolExecutionError,
|
|
118
|
-
TOOL_EXECUTION_ERROR_SCHEMA_VERSION,
|
|
119
117
|
TOOL_EXECUTION_ERROR_SCHEMA_HEADER,
|
|
120
118
|
type ToolExecutionErrorSchemaVersion,
|
|
121
119
|
type ToolExecutionFailureV1,
|
|
@@ -177,7 +175,7 @@ import {
|
|
|
177
175
|
buildDurableToolCallCacheKey,
|
|
178
176
|
buildLegacyRevisionedDurableToolCallCacheKey,
|
|
179
177
|
buildDurableToolPayloadProviderIdempotencyKey,
|
|
180
|
-
|
|
178
|
+
buildProviderOperationIdempotencyKey,
|
|
181
179
|
buildDurableToolReceiptPrefix,
|
|
182
180
|
resolveDurableCallCachePolicy,
|
|
183
181
|
} from './durable-call-cache';
|
|
@@ -195,6 +193,10 @@ import {
|
|
|
195
193
|
type PlayAuthoringRunScope,
|
|
196
194
|
type PlayAuthoringRuntimeContext,
|
|
197
195
|
} from '../plays/authoring-contract';
|
|
196
|
+
import {
|
|
197
|
+
resolvePlayArtifactRuntimeContract,
|
|
198
|
+
type PlayArtifactRuntimeContract,
|
|
199
|
+
} from './play-artifact-runtime-contract';
|
|
198
200
|
import {
|
|
199
201
|
LOG_LEVELS,
|
|
200
202
|
formatCtxFetchHttpFailureDiagnostic,
|
|
@@ -217,11 +219,7 @@ import {
|
|
|
217
219
|
type DurableReceiptExecutionStore,
|
|
218
220
|
} from './durable-receipt-execution';
|
|
219
221
|
import {
|
|
220
|
-
asyncOperationDurableStartInput,
|
|
221
222
|
asyncOperationShouldWait,
|
|
222
|
-
classifyAsyncOperationResult,
|
|
223
|
-
executeAsyncOperationLifecycle,
|
|
224
|
-
resolveAsyncOperationPolling,
|
|
225
223
|
type AsyncOperationContractWire,
|
|
226
224
|
} from './async-operation';
|
|
227
225
|
import {
|
|
@@ -407,14 +405,8 @@ type InlineCompositionStore = {
|
|
|
407
405
|
governor: PlayExecutionGovernor;
|
|
408
406
|
playName: string;
|
|
409
407
|
staticPipeline: ContextOptions['staticPipeline'];
|
|
410
|
-
/** Immutable
|
|
411
|
-
|
|
412
|
-
/** Immutable authoring semantics pinned by the child play artifact. */
|
|
413
|
-
authoringContractEdition: PlayAuthoringContractEdition;
|
|
414
|
-
/** Execute response contract pinned by the child artifact. */
|
|
415
|
-
toolResponseContract: ToolResponseContract;
|
|
416
|
-
/** Explicit response-transform revision used for durable receipt identity. */
|
|
417
|
-
toolResponseReceiptRevision?: string;
|
|
408
|
+
/** Immutable compatibility projection of the child Play artifact. */
|
|
409
|
+
runtimeContract: PlayArtifactRuntimeContract;
|
|
418
410
|
};
|
|
419
411
|
const inlineCompositionContext =
|
|
420
412
|
new AsyncLocalStorage<InlineCompositionStore>();
|
|
@@ -1089,7 +1081,10 @@ export function resolveToolRuntimeTimeoutMs(
|
|
|
1089
1081
|
authoringContractEdition: PlayAuthoringContractEdition = PLAY_AUTHORING_CONTRACT_EDITION,
|
|
1090
1082
|
): number | undefined {
|
|
1091
1083
|
if (requestedTimeoutMs !== undefined) {
|
|
1092
|
-
if (
|
|
1084
|
+
if (
|
|
1085
|
+
resolvePlayArtifactRuntimeContract({ authoringContractEdition }).semantics
|
|
1086
|
+
.validation === 'strict'
|
|
1087
|
+
) {
|
|
1093
1088
|
validatePlayAuthoringField(
|
|
1094
1089
|
'ctx.tools.execute.timeoutMs',
|
|
1095
1090
|
requestedTimeoutMs,
|
|
@@ -1249,27 +1244,6 @@ function publicToolResponseEnvelope(value: unknown): {
|
|
|
1249
1244
|
};
|
|
1250
1245
|
}
|
|
1251
1246
|
|
|
1252
|
-
function materializedAsyncToolResponse(
|
|
1253
|
-
response: ParsedToolExecuteResponse['toolResponse'] | undefined,
|
|
1254
|
-
result: unknown,
|
|
1255
|
-
): ParsedToolExecuteResponse['toolResponse'] {
|
|
1256
|
-
const hasRawV2 =
|
|
1257
|
-
response !== undefined &&
|
|
1258
|
-
Object.prototype.hasOwnProperty.call(response, 'rawV2');
|
|
1259
|
-
const materializedRawV2 =
|
|
1260
|
-
response?.view === 'data'
|
|
1261
|
-
? {
|
|
1262
|
-
...(recordOrNull(response.rawV2) ?? {}),
|
|
1263
|
-
data: result,
|
|
1264
|
-
}
|
|
1265
|
-
: result;
|
|
1266
|
-
return {
|
|
1267
|
-
...(response ?? {}),
|
|
1268
|
-
raw: result,
|
|
1269
|
-
...(hasRawV2 ? { rawV2: materializedRawV2 } : {}),
|
|
1270
|
-
};
|
|
1271
|
-
}
|
|
1272
|
-
|
|
1273
1247
|
/**
|
|
1274
1248
|
* A batched provider request returns one envelope for several logical source
|
|
1275
1249
|
* tool calls. Each source call persists only its own item-shaped canonical
|
|
@@ -1399,13 +1373,11 @@ type ToolExecutionApiOptions = {
|
|
|
1399
1373
|
/** The receipt key is a completed-result cache for this read-only call. */
|
|
1400
1374
|
completedResultCache?: boolean;
|
|
1401
1375
|
executionAuthScopeDigest?: string | null;
|
|
1402
|
-
/**
|
|
1403
|
-
* Historical receipt identity used for the API's provider/billing
|
|
1404
|
-
* idempotency receipt. This can differ from the worker-owned durable cache
|
|
1405
|
-
* receipt while a response contract migrates.
|
|
1406
|
-
*/
|
|
1407
|
-
providerIdempotencyReceiptKey?: string | null;
|
|
1408
1376
|
providerIdempotencyKey?: string | null;
|
|
1377
|
+
/** Exact forced-member identities that participate in a native batch key. */
|
|
1378
|
+
providerOperationForceRefreshIdentities?: readonly string[];
|
|
1379
|
+
/** Presentation-only cache identity paired with a separate execution fence. */
|
|
1380
|
+
providerIdempotencyReceiptKey?: string | null;
|
|
1409
1381
|
receiptLeaseExpiresAt?: string | null;
|
|
1410
1382
|
beforeProviderCall?: () => Promise<void> | void;
|
|
1411
1383
|
heartbeatReceipt?: () => Promise<void> | void;
|
|
@@ -1430,6 +1402,13 @@ type ToolExecutionApiOptions = {
|
|
|
1430
1402
|
};
|
|
1431
1403
|
/** Internal recursion guard for lifecycle poll/finish calls. */
|
|
1432
1404
|
skipAsyncOperationResolution?: boolean;
|
|
1405
|
+
/**
|
|
1406
|
+
* A status/result request belonging to a live async lifecycle. It must not
|
|
1407
|
+
* create a result receipt or provider idempotency identity from a parent
|
|
1408
|
+
* cache entry: the job handle is process-local, while the outer tool-call
|
|
1409
|
+
* cache stores only its terminal materialized output.
|
|
1410
|
+
*/
|
|
1411
|
+
uncachedAsyncLifecycleCall?: boolean;
|
|
1433
1412
|
};
|
|
1434
1413
|
const IN_MEMORY_STEP_RESULT_PREVIEW_LIMIT = 25;
|
|
1435
1414
|
/**
|
|
@@ -2110,11 +2089,8 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
2110
2089
|
string,
|
|
2111
2090
|
{ resolve: (value: unknown) => void; reject: (reason: unknown) => void }
|
|
2112
2091
|
>();
|
|
2113
|
-
private readonly asyncOperationCapacity = new Map<
|
|
2114
|
-
string,
|
|
2115
|
-
{ active: number; waiters: Array<() => void> }
|
|
2116
|
-
>();
|
|
2117
2092
|
#options: ContextOptions;
|
|
2093
|
+
private readonly rootRuntimeContract: PlayArtifactRuntimeContract;
|
|
2118
2094
|
private readonly executionScope: RunExecutionScope;
|
|
2119
2095
|
private logBuffer: string[] = [];
|
|
2120
2096
|
private readonly logRateLimitState = new Map<
|
|
@@ -2160,6 +2136,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
2160
2136
|
boundaryId: string;
|
|
2161
2137
|
eventKey: string;
|
|
2162
2138
|
timeoutMs: number;
|
|
2139
|
+
messageRef?: { channel: string; ts: string };
|
|
2163
2140
|
}> = [];
|
|
2164
2141
|
private processedRowCount = 0;
|
|
2165
2142
|
private sleepBoundaryIndex = 0;
|
|
@@ -2253,7 +2230,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
2253
2230
|
assertNoSecretTaint(request.input, 'ctx.tools.execute input');
|
|
2254
2231
|
if (
|
|
2255
2232
|
request.timeoutMs !== undefined &&
|
|
2256
|
-
this.
|
|
2233
|
+
this.currentRuntimeSemantics.validation === 'strict'
|
|
2257
2234
|
) {
|
|
2258
2235
|
validatePlayAuthoringField(
|
|
2259
2236
|
'ctx.tools.execute.timeoutMs',
|
|
@@ -2262,7 +2239,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
2262
2239
|
}
|
|
2263
2240
|
if (
|
|
2264
2241
|
request.receiptWaitMs !== undefined &&
|
|
2265
|
-
this.
|
|
2242
|
+
this.currentRuntimeSemantics.validation === 'strict'
|
|
2266
2243
|
) {
|
|
2267
2244
|
validatePlayAuthoringField(
|
|
2268
2245
|
'ctx.tools.execute.receiptWaitMs',
|
|
@@ -2406,7 +2383,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
2406
2383
|
}
|
|
2407
2384
|
const handle = createSecretHandle(name.trim());
|
|
2408
2385
|
const value =
|
|
2409
|
-
this.
|
|
2386
|
+
this.currentRuntimeSemantics.secrets.value === 'plaintext_promise'
|
|
2410
2387
|
? createPlaintextSecretPromise(handle.name, () =>
|
|
2411
2388
|
this.resolveSecretHandle(handle),
|
|
2412
2389
|
)
|
|
@@ -2430,6 +2407,13 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
2430
2407
|
};
|
|
2431
2408
|
|
|
2432
2409
|
constructor(options: ContextOptions) {
|
|
2410
|
+
this.rootRuntimeContract =
|
|
2411
|
+
options.artifactRuntimeContract ??
|
|
2412
|
+
resolvePlayArtifactRuntimeContract({
|
|
2413
|
+
authoringContractEdition: options.authoringContractEdition,
|
|
2414
|
+
toolResponseContract: options.toolResponseContract,
|
|
2415
|
+
toolResponseReceiptRevision: options.toolResponseReceiptRevision,
|
|
2416
|
+
});
|
|
2433
2417
|
const fixtureBehavior = validateFixtureBehavior(options.fixtureBehavior);
|
|
2434
2418
|
if (fixtureBehavior.ok === false) {
|
|
2435
2419
|
throw new Error(fixtureBehavior.error);
|
|
@@ -2749,20 +2733,15 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
2749
2733
|
|
|
2750
2734
|
/**
|
|
2751
2735
|
* Inline children share this context's queues, but not its artifact contract.
|
|
2752
|
-
* Each child
|
|
2753
|
-
*
|
|
2736
|
+
* Each child uses its own admitted semantics and response projection even
|
|
2737
|
+
* when the parent was published under another artifact contract.
|
|
2754
2738
|
*/
|
|
2755
2739
|
private get currentToolErrorSchemaVersion(): ToolExecutionErrorSchemaVersion {
|
|
2756
|
-
|
|
2757
|
-
// strings. Every runtime tool call and receipt rehydration uses v1.
|
|
2758
|
-
return TOOL_EXECUTION_ERROR_SCHEMA_VERSION;
|
|
2740
|
+
return this.currentRuntimeContract.toolErrorSchemaVersion;
|
|
2759
2741
|
}
|
|
2760
2742
|
|
|
2761
2743
|
private get currentToolResponseContract(): ToolResponseContract {
|
|
2762
|
-
return
|
|
2763
|
-
this.activeInlineComposition?.toolResponseContract ??
|
|
2764
|
-
this.#options.toolResponseContract,
|
|
2765
|
-
);
|
|
2744
|
+
return this.currentRuntimeContract.toolResponseContract;
|
|
2766
2745
|
}
|
|
2767
2746
|
|
|
2768
2747
|
/**
|
|
@@ -2775,6 +2754,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
2775
2754
|
private get hasPinnedToolResponseContract(): boolean {
|
|
2776
2755
|
return (
|
|
2777
2756
|
this.activeInlineComposition !== null ||
|
|
2757
|
+
this.#options.artifactRuntimeContract !== undefined ||
|
|
2778
2758
|
this.#options.toolResponseContract !== undefined
|
|
2779
2759
|
);
|
|
2780
2760
|
}
|
|
@@ -2784,20 +2764,23 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
2784
2764
|
* changes the completed-result cache identity.
|
|
2785
2765
|
*/
|
|
2786
2766
|
private get currentToolResponseReceiptRevision(): string | undefined {
|
|
2787
|
-
|
|
2788
|
-
return this.activeInlineComposition.toolResponseReceiptRevision;
|
|
2789
|
-
}
|
|
2790
|
-
return this.#options.toolResponseReceiptRevision;
|
|
2767
|
+
return this.currentRuntimeContract.toolResponseReceiptRevision;
|
|
2791
2768
|
}
|
|
2792
2769
|
|
|
2793
2770
|
private get currentAuthoringContractEdition(): PlayAuthoringContractEdition {
|
|
2771
|
+
return this.currentRuntimeContract.semantics.authoringContractEdition;
|
|
2772
|
+
}
|
|
2773
|
+
|
|
2774
|
+
private get currentRuntimeContract(): PlayArtifactRuntimeContract {
|
|
2794
2775
|
return (
|
|
2795
|
-
this.activeInlineComposition?.
|
|
2796
|
-
this.#options.authoringContractEdition ??
|
|
2797
|
-
PLAY_AUTHORING_CONTRACT_EDITION
|
|
2776
|
+
this.activeInlineComposition?.runtimeContract ?? this.rootRuntimeContract
|
|
2798
2777
|
);
|
|
2799
2778
|
}
|
|
2800
2779
|
|
|
2780
|
+
private get currentRuntimeSemantics() {
|
|
2781
|
+
return this.currentRuntimeContract.semantics;
|
|
2782
|
+
}
|
|
2783
|
+
|
|
2801
2784
|
private recordPlayCallStep(input: {
|
|
2802
2785
|
playId: string;
|
|
2803
2786
|
execution?: 'inline';
|
|
@@ -9326,7 +9309,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9326
9309
|
}
|
|
9327
9310
|
if (
|
|
9328
9311
|
options?.timeoutMs !== undefined &&
|
|
9329
|
-
this.
|
|
9312
|
+
this.currentRuntimeSemantics.validation === 'strict'
|
|
9330
9313
|
) {
|
|
9331
9314
|
validatePlayAuthoringField(
|
|
9332
9315
|
'ctx.tools.execute.timeoutMs',
|
|
@@ -9335,7 +9318,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9335
9318
|
}
|
|
9336
9319
|
if (
|
|
9337
9320
|
options?.receiptWaitMs !== undefined &&
|
|
9338
|
-
this.
|
|
9321
|
+
this.currentRuntimeSemantics.validation === 'strict'
|
|
9339
9322
|
) {
|
|
9340
9323
|
validatePlayAuthoringField(
|
|
9341
9324
|
'ctx.tools.execute.receiptWaitMs',
|
|
@@ -9382,9 +9365,18 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9382
9365
|
executionAuthScopeDigest,
|
|
9383
9366
|
staleAfterSeconds: toolCachePolicy.staleAfterSeconds,
|
|
9384
9367
|
});
|
|
9385
|
-
|
|
9386
|
-
|
|
9387
|
-
|
|
9368
|
+
const providerActionVersion =
|
|
9369
|
+
(await this.#options.getToolActionCacheVersion?.(toolId))?.trim() ?? '';
|
|
9370
|
+
// A provider operation is keyed by its requested semantic payload. The
|
|
9371
|
+
// durable receipt below remains a completed-result cache only.
|
|
9372
|
+
let providerIdempotencyKeyBase =
|
|
9373
|
+
buildDurableToolPayloadProviderIdempotencyKey({
|
|
9374
|
+
orgId: this.#options.orgId,
|
|
9375
|
+
toolId,
|
|
9376
|
+
executionAuthScopeDigest,
|
|
9377
|
+
providerActionVersion,
|
|
9378
|
+
payload: input,
|
|
9379
|
+
});
|
|
9388
9380
|
let directResultReceiptKey = durableCacheKey;
|
|
9389
9381
|
// A raw-v2 artifact must never read an older receipt that cannot render
|
|
9390
9382
|
// raw-v2. If both the primary and its revisioned recovery receipt are
|
|
@@ -9664,17 +9656,16 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9664
9656
|
...(toolRetryPolicy?.requiresExecutionFence === true
|
|
9665
9657
|
? {
|
|
9666
9658
|
providerIdempotencyReceiptKey:
|
|
9667
|
-
cacheableToolResult &&
|
|
9668
|
-
?
|
|
9659
|
+
cacheableToolResult && resultReceiptKey
|
|
9660
|
+
? resultReceiptKey
|
|
9669
9661
|
: physicalDirectKey,
|
|
9670
9662
|
providerIdempotencyKey:
|
|
9671
9663
|
this.providerIdempotencyKeyForToolCall({
|
|
9672
|
-
|
|
9664
|
+
operationKey:
|
|
9673
9665
|
useDirectResultReceipts && operationKey
|
|
9674
9666
|
? providerIdempotencyKeyBase
|
|
9675
9667
|
: physicalDirectKey,
|
|
9676
9668
|
force: cacheableToolResult && toolCachePolicy.force,
|
|
9677
|
-
leaseId: directReceiptLeaseId,
|
|
9678
9669
|
logicalCallId,
|
|
9679
9670
|
}),
|
|
9680
9671
|
}
|
|
@@ -9756,9 +9747,6 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9756
9747
|
dispatcher,
|
|
9757
9748
|
})
|
|
9758
9749
|
: null;
|
|
9759
|
-
const providerActionVersion =
|
|
9760
|
-
(await this.#options.getToolActionCacheVersion?.(toolId))?.trim() ??
|
|
9761
|
-
'';
|
|
9762
9750
|
const call: Omit<ToolCallInput, 'compatibility'> = {
|
|
9763
9751
|
scope: {
|
|
9764
9752
|
organizationId: this.#options.orgId ?? 'unknown-org',
|
|
@@ -9987,7 +9975,14 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9987
9975
|
executionAuthScopeDigest,
|
|
9988
9976
|
staleAfterSeconds: toolCachePolicy.staleAfterSeconds,
|
|
9989
9977
|
});
|
|
9990
|
-
providerIdempotencyKeyBase =
|
|
9978
|
+
providerIdempotencyKeyBase =
|
|
9979
|
+
buildDurableToolPayloadProviderIdempotencyKey({
|
|
9980
|
+
orgId: this.#options.orgId,
|
|
9981
|
+
toolId,
|
|
9982
|
+
executionAuthScopeDigest,
|
|
9983
|
+
providerActionVersion,
|
|
9984
|
+
payload: input,
|
|
9985
|
+
});
|
|
9991
9986
|
directResultReceiptKey = durableCacheKey;
|
|
9992
9987
|
bypassDirectResultCache = false;
|
|
9993
9988
|
checkpointCacheKeys.splice(
|
|
@@ -10042,6 +10037,21 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
10042
10037
|
const existing =
|
|
10043
10038
|
this.checkpoint.resolvedBoundaries?.[preparedBoundary.boundaryId];
|
|
10044
10039
|
if (existing?.kind === 'integration_event' && 'output' in existing) {
|
|
10040
|
+
const output = recordOrNull(existing.output);
|
|
10041
|
+
if (output?.timed_out === true) {
|
|
10042
|
+
await handler.onTimeout?.({
|
|
10043
|
+
payload: input,
|
|
10044
|
+
context: eventContext,
|
|
10045
|
+
boundary: {
|
|
10046
|
+
boundaryId: preparedBoundary.boundaryId,
|
|
10047
|
+
eventKey: preparedBoundary.eventKey,
|
|
10048
|
+
timeoutMs: preparedBoundary.timeoutMs,
|
|
10049
|
+
provider: existing.provider ?? handler.provider,
|
|
10050
|
+
toolId: existing.toolId ?? handler.toolId,
|
|
10051
|
+
...(existing.messageRef ? { messageRef: existing.messageRef } : {}),
|
|
10052
|
+
},
|
|
10053
|
+
});
|
|
10054
|
+
}
|
|
10045
10055
|
this.runtimeLog(
|
|
10046
10056
|
`Integration event ${preparedBoundary.boundaryId}: recovered response from checkpoint`,
|
|
10047
10057
|
);
|
|
@@ -10097,6 +10107,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
10097
10107
|
boundaryId: boundary.boundaryId,
|
|
10098
10108
|
eventKey: boundary.eventKey,
|
|
10099
10109
|
timeoutMs: boundary.timeoutMs,
|
|
10110
|
+
...(boundary.messageRef ? { messageRef: boundary.messageRef } : {}),
|
|
10100
10111
|
});
|
|
10101
10112
|
}
|
|
10102
10113
|
|
|
@@ -10105,6 +10116,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
10105
10116
|
boundaryId: boundary.boundaryId,
|
|
10106
10117
|
eventKey: boundary.eventKey,
|
|
10107
10118
|
timeoutMs: boundary.timeoutMs,
|
|
10119
|
+
...(boundary.messageRef ? { messageRef: boundary.messageRef } : {}),
|
|
10108
10120
|
});
|
|
10109
10121
|
}
|
|
10110
10122
|
|
|
@@ -10164,8 +10176,6 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
10164
10176
|
const childCompatibility = normalizePlayContractCompatibility(
|
|
10165
10177
|
childCompatibilitySnapshot,
|
|
10166
10178
|
);
|
|
10167
|
-
const childToolErrorSchemaVersion =
|
|
10168
|
-
childCompatibility.toolErrorSchemaVersion;
|
|
10169
10179
|
const childToolResponseContract = childCompatibility.toolResponseContract;
|
|
10170
10180
|
const childToolResponseReceiptRevision =
|
|
10171
10181
|
childCompatibility.toolResponseReceiptRevision;
|
|
@@ -10240,11 +10250,12 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
10240
10250
|
governor: inlineChildGovernor,
|
|
10241
10251
|
playName: resolvedName,
|
|
10242
10252
|
staticPipeline: resolvedPlay.staticPipeline ?? null,
|
|
10243
|
-
|
|
10244
|
-
|
|
10245
|
-
|
|
10246
|
-
|
|
10247
|
-
|
|
10253
|
+
runtimeContract: resolvePlayArtifactRuntimeContract({
|
|
10254
|
+
authoringContractEdition:
|
|
10255
|
+
childCompatibility.authoringContractEdition,
|
|
10256
|
+
toolResponseContract: childToolResponseContract,
|
|
10257
|
+
toolResponseReceiptRevision: childToolResponseReceiptRevision,
|
|
10258
|
+
}),
|
|
10248
10259
|
},
|
|
10249
10260
|
() => this.executeResolvedPlay(resolvedPlay, this, input),
|
|
10250
10261
|
);
|
|
@@ -10553,7 +10564,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
10553
10564
|
async sleep(ms: number): Promise<void> {
|
|
10554
10565
|
this.assertInlineChildContract('suspending_child');
|
|
10555
10566
|
const delayMs =
|
|
10556
|
-
this.
|
|
10567
|
+
this.currentRuntimeSemantics.validation === 'strict'
|
|
10557
10568
|
? (validatePlayAuthoringField('ctx.sleep.ms', ms), ms)
|
|
10558
10569
|
: Math.max(0, Math.round(ms));
|
|
10559
10570
|
if (this.#options.durableBoundaries) {
|
|
@@ -10679,14 +10690,15 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
10679
10690
|
...secretHeaderMarkers,
|
|
10680
10691
|
},
|
|
10681
10692
|
row: rowFetchScope,
|
|
10682
|
-
//
|
|
10683
|
-
// non-cacheable errors
|
|
10684
|
-
//
|
|
10685
|
-
//
|
|
10686
|
-
...(this.
|
|
10693
|
+
// A pinned fetch policy that turns non-2xx responses into typed,
|
|
10694
|
+
// non-cacheable errors needs its own receipt epoch. Preserve the
|
|
10695
|
+
// historical epoch selected by the artifact rather than deriving a
|
|
10696
|
+
// new cache key from every future authoring edition.
|
|
10697
|
+
...(this.currentRuntimeSemantics.fetch.receiptIdentityEdition !==
|
|
10698
|
+
null
|
|
10687
10699
|
? {
|
|
10688
10700
|
authoringContractEdition:
|
|
10689
|
-
this.
|
|
10701
|
+
this.currentRuntimeSemantics.fetch.receiptIdentityEdition,
|
|
10690
10702
|
}
|
|
10691
10703
|
: {}),
|
|
10692
10704
|
}),
|
|
@@ -10702,7 +10714,11 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
10702
10714
|
httpStatus: output.status,
|
|
10703
10715
|
});
|
|
10704
10716
|
}
|
|
10705
|
-
if (
|
|
10717
|
+
if (
|
|
10718
|
+
!output.ok &&
|
|
10719
|
+
this.currentRuntimeSemantics.fetch.httpFailure ===
|
|
10720
|
+
'throw_typed_error'
|
|
10721
|
+
) {
|
|
10706
10722
|
throw new CtxFetchHttpError(output);
|
|
10707
10723
|
}
|
|
10708
10724
|
return output;
|
|
@@ -10710,7 +10726,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
10710
10726
|
shouldPersistFailure: (error) =>
|
|
10711
10727
|
!(
|
|
10712
10728
|
error instanceof CtxFetchHttpError &&
|
|
10713
|
-
this.
|
|
10729
|
+
!this.currentRuntimeSemantics.fetch.persistHttpFailure
|
|
10714
10730
|
),
|
|
10715
10731
|
execute: async ({ retainExternalCallSlot }) => {
|
|
10716
10732
|
const secretHeaders = await this.resolveSecretAuth(secretAuth);
|
|
@@ -10914,11 +10930,15 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
10914
10930
|
});
|
|
10915
10931
|
}
|
|
10916
10932
|
|
|
10917
|
-
//
|
|
10918
|
-
// a failed durable operation.
|
|
10919
|
-
// completion so the
|
|
10920
|
-
//
|
|
10921
|
-
if (
|
|
10933
|
+
// The artifact-pinned fetch policy decides whether a non-2xx
|
|
10934
|
+
// response is a returned record or a failed durable operation.
|
|
10935
|
+
// Throw before checkpoint/receipt completion so the throwing
|
|
10936
|
+
// policy never caches its failure.
|
|
10937
|
+
if (
|
|
10938
|
+
!output.ok &&
|
|
10939
|
+
this.currentRuntimeSemantics.fetch.httpFailure ===
|
|
10940
|
+
'throw_typed_error'
|
|
10941
|
+
) {
|
|
10922
10942
|
throw new CtxFetchHttpError(output);
|
|
10923
10943
|
}
|
|
10924
10944
|
|
|
@@ -11310,15 +11330,15 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
11310
11330
|
async () =>
|
|
11311
11331
|
await recordsFor(
|
|
11312
11332
|
resultReceiptKeys,
|
|
11313
|
-
await this.getRuntimeStepReceipts(
|
|
11314
|
-
|
|
11333
|
+
await this.getRuntimeStepReceipts([
|
|
11334
|
+
...new Set(
|
|
11315
11335
|
resultReceiptKeys.flatMap((receiptKey) => [
|
|
11316
11336
|
receiptKey,
|
|
11317
11337
|
...(input.requestForReceipt.get(receiptKey)
|
|
11318
11338
|
?.legacyReceiptKeys ?? []),
|
|
11319
11339
|
]),
|
|
11320
|
-
)
|
|
11321
|
-
),
|
|
11340
|
+
),
|
|
11341
|
+
]),
|
|
11322
11342
|
),
|
|
11323
11343
|
),
|
|
11324
11344
|
complete: async ({ receipts }) => {
|
|
@@ -12575,7 +12595,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
12575
12595
|
dispatchPayload: owner.input,
|
|
12576
12596
|
resultReceiptKey: receiptKey,
|
|
12577
12597
|
providerOperationKey:
|
|
12578
|
-
|
|
12598
|
+
this.providerOperationKeyForQueuedToolCall(owner),
|
|
12579
12599
|
apiOptions: {
|
|
12580
12600
|
beforeProviderCall: () =>
|
|
12581
12601
|
this.assertRuntimeToolReceiptOwnership([owner]),
|
|
@@ -12583,18 +12603,14 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
12583
12603
|
playNodeScope: playNodeScopeForToolCallRequest(owner),
|
|
12584
12604
|
executionAuthScopeDigest:
|
|
12585
12605
|
owner.executionAuthScopeDigest,
|
|
12586
|
-
providerIdempotencyReceiptKey:
|
|
12587
|
-
owner.providerIdempotencyKeyBase ?? owner.cacheKey,
|
|
12588
12606
|
receiptLeaseExpiresAt: owner.receiptLeaseExpiresAt,
|
|
12589
12607
|
heartbeatReceipt: () =>
|
|
12590
12608
|
this.renewRuntimeToolReceiptOwnership([owner]),
|
|
12591
12609
|
providerIdempotencyKey:
|
|
12592
12610
|
this.providerIdempotencyKeyForToolCall({
|
|
12593
|
-
|
|
12594
|
-
owner
|
|
12595
|
-
owner.cacheKey,
|
|
12611
|
+
operationKey:
|
|
12612
|
+
this.providerOperationKeyForQueuedToolCall(owner),
|
|
12596
12613
|
force: owner.force === true,
|
|
12597
|
-
leaseId: owner.receiptLeaseId,
|
|
12598
12614
|
logicalCallId: owner.logicalCallId,
|
|
12599
12615
|
}),
|
|
12600
12616
|
timeoutMs: resolveRuntimeTimeoutMsForClaimedOwners([
|
|
@@ -12928,7 +12944,17 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
12928
12944
|
toolId: batch.batchOperation,
|
|
12929
12945
|
}),
|
|
12930
12946
|
});
|
|
12931
|
-
const
|
|
12947
|
+
const aggregateForceRefreshIdentities = batch.memberRequests
|
|
12948
|
+
.filter((request) => request.force === true)
|
|
12949
|
+
.map((request) =>
|
|
12950
|
+
this.providerIdempotencyKeyForToolCall({
|
|
12951
|
+
operationKey:
|
|
12952
|
+
this.providerOperationKeyForQueuedToolCall(request),
|
|
12953
|
+
force: true,
|
|
12954
|
+
logicalCallId: request.logicalCallId,
|
|
12955
|
+
}),
|
|
12956
|
+
);
|
|
12957
|
+
const aggregateProviderOperationKey =
|
|
12932
12958
|
buildDurableToolPayloadProviderIdempotencyKey({
|
|
12933
12959
|
orgId: this.#options.orgId,
|
|
12934
12960
|
toolId: batch.batchOperation,
|
|
@@ -12942,18 +12968,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
12942
12968
|
)
|
|
12943
12969
|
)?.trim() ?? 'unversioned',
|
|
12944
12970
|
payload: batch.batchPayload,
|
|
12945
|
-
forceRefreshIdentities:
|
|
12946
|
-
.filter((request) => request.force === true)
|
|
12947
|
-
.map((request) =>
|
|
12948
|
-
this.providerIdempotencyKeyForToolCall({
|
|
12949
|
-
cacheKey:
|
|
12950
|
-
request.providerIdempotencyKeyBase ??
|
|
12951
|
-
request.cacheKey,
|
|
12952
|
-
force: true,
|
|
12953
|
-
leaseId: request.receiptLeaseId,
|
|
12954
|
-
logicalCallId: request.logicalCallId,
|
|
12955
|
-
}),
|
|
12956
|
-
),
|
|
12971
|
+
forceRefreshIdentities: aggregateForceRefreshIdentities,
|
|
12957
12972
|
});
|
|
12958
12973
|
let releaseToolSlot: () => void = () => undefined;
|
|
12959
12974
|
try {
|
|
@@ -12963,8 +12978,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
12963
12978
|
dispatchToolId: batch.batchOperation,
|
|
12964
12979
|
dispatchPayload: batch.batchPayload,
|
|
12965
12980
|
resultReceiptKey: aggregateReceiptKey,
|
|
12966
|
-
providerOperationKey:
|
|
12967
|
-
aggregateProviderIdempotencyReceiptKey,
|
|
12981
|
+
providerOperationKey: aggregateProviderOperationKey,
|
|
12968
12982
|
apiOptions: {
|
|
12969
12983
|
durableCallReceiptKey: aggregateReceiptKey,
|
|
12970
12984
|
// A native batch is one physical provider request for
|
|
@@ -12977,10 +12991,9 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
12977
12991
|
executionAuthScopeDigest:
|
|
12978
12992
|
batch.memberRequests[0]?.executionAuthScopeDigest ??
|
|
12979
12993
|
null,
|
|
12980
|
-
|
|
12981
|
-
|
|
12982
|
-
|
|
12983
|
-
aggregateProviderIdempotencyReceiptKey,
|
|
12994
|
+
providerIdempotencyKey: aggregateProviderOperationKey,
|
|
12995
|
+
providerOperationForceRefreshIdentities:
|
|
12996
|
+
aggregateForceRefreshIdentities,
|
|
12984
12997
|
receiptLeaseExpiresAt: batch.memberRequests.reduce<
|
|
12985
12998
|
string | null
|
|
12986
12999
|
>((earliest, request) => {
|
|
@@ -13183,15 +13196,13 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
13183
13196
|
request,
|
|
13184
13197
|
dispatchToolId: toolId,
|
|
13185
13198
|
dispatchPayload: request.input,
|
|
13186
|
-
// Always-fresh map calls intentionally have no
|
|
13187
|
-
//
|
|
13188
|
-
//
|
|
13199
|
+
// Always-fresh map calls intentionally have no
|
|
13200
|
+
// result cache. Their provider-operation identity is
|
|
13201
|
+
// a unique forced semantic request, never a cache key.
|
|
13189
13202
|
resultReceiptKey:
|
|
13190
13203
|
request.receiptKey ?? request.cacheKey,
|
|
13191
13204
|
providerOperationKey:
|
|
13192
|
-
request
|
|
13193
|
-
request.receiptKey ??
|
|
13194
|
-
request.cacheKey,
|
|
13205
|
+
this.providerOperationKeyForQueuedToolCall(request),
|
|
13195
13206
|
apiOptions: {
|
|
13196
13207
|
beforeProviderCall: () =>
|
|
13197
13208
|
pendingReceiptHeartbeat
|
|
@@ -13207,16 +13218,13 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
13207
13218
|
completedResultCache: true,
|
|
13208
13219
|
executionAuthScopeDigest:
|
|
13209
13220
|
request.executionAuthScopeDigest,
|
|
13210
|
-
providerIdempotencyReceiptKey:
|
|
13211
|
-
request.providerIdempotencyKeyBase ??
|
|
13212
|
-
request.receiptKey,
|
|
13213
13221
|
providerIdempotencyKey:
|
|
13214
13222
|
this.providerIdempotencyKeyForToolCall({
|
|
13215
|
-
|
|
13216
|
-
|
|
13217
|
-
|
|
13223
|
+
operationKey:
|
|
13224
|
+
this.providerOperationKeyForQueuedToolCall(
|
|
13225
|
+
request,
|
|
13226
|
+
),
|
|
13218
13227
|
force: request.force === true,
|
|
13219
|
-
leaseId: request.receiptLeaseId,
|
|
13220
13228
|
logicalCallId: request.logicalCallId,
|
|
13221
13229
|
}),
|
|
13222
13230
|
receiptLeaseExpiresAt:
|
|
@@ -13226,7 +13234,17 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
13226
13234
|
request,
|
|
13227
13235
|
]),
|
|
13228
13236
|
}
|
|
13229
|
-
: {
|
|
13237
|
+
: {
|
|
13238
|
+
providerIdempotencyKey:
|
|
13239
|
+
this.providerIdempotencyKeyForToolCall({
|
|
13240
|
+
operationKey:
|
|
13241
|
+
this.providerOperationKeyForQueuedToolCall(
|
|
13242
|
+
request,
|
|
13243
|
+
),
|
|
13244
|
+
force: true,
|
|
13245
|
+
logicalCallId: request.logicalCallId,
|
|
13246
|
+
}),
|
|
13247
|
+
}),
|
|
13230
13248
|
timeoutMs: resolveRuntimeTimeoutMsForClaimedOwners([
|
|
13231
13249
|
request,
|
|
13232
13250
|
]),
|
|
@@ -13369,173 +13387,36 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
13369
13387
|
}
|
|
13370
13388
|
|
|
13371
13389
|
private providerIdempotencyKeyForToolCall(input: {
|
|
13372
|
-
|
|
13390
|
+
operationKey: string;
|
|
13373
13391
|
force?: boolean;
|
|
13374
|
-
leaseId?: string | null;
|
|
13375
13392
|
logicalCallId?: string;
|
|
13376
13393
|
}): string {
|
|
13377
13394
|
let fallbackAttemptId: string | null = null;
|
|
13378
|
-
if (input.force === true
|
|
13395
|
+
if (input.force === true) {
|
|
13379
13396
|
if (!input.logicalCallId) {
|
|
13380
13397
|
throw new Error(
|
|
13381
|
-
'Forced tool execution
|
|
13398
|
+
'Forced tool execution requires a stable logical call identity.',
|
|
13382
13399
|
);
|
|
13383
13400
|
}
|
|
13384
13401
|
fallbackAttemptId = `${this.currentRunId}:${input.logicalCallId}`;
|
|
13385
13402
|
}
|
|
13386
|
-
return
|
|
13387
|
-
|
|
13403
|
+
return buildProviderOperationIdempotencyKey({
|
|
13404
|
+
operationKey: input.operationKey,
|
|
13388
13405
|
force: input.force,
|
|
13389
|
-
receiptLeaseId: input.leaseId,
|
|
13390
13406
|
fallbackAttemptId,
|
|
13391
13407
|
});
|
|
13392
13408
|
}
|
|
13393
13409
|
|
|
13394
|
-
private
|
|
13395
|
-
|
|
13396
|
-
|
|
13397
|
-
|
|
13398
|
-
|
|
13399
|
-
}): Promise<() => void> {
|
|
13400
|
-
const scopeSuffix =
|
|
13401
|
-
input.contract.capacity.scope === 'organization_action'
|
|
13402
|
-
? input.toolId
|
|
13403
|
-
: input.contract.capacity.scope === 'organization_connection'
|
|
13404
|
-
? (input.authScopeDigest ?? 'default-connection')
|
|
13405
|
-
: input.provider;
|
|
13406
|
-
const key = `${this.#options.orgId ?? 'unknown-org'}:${input.provider}:${input.contract.capacity.scope}:${scopeSuffix}`;
|
|
13407
|
-
let limit = 1;
|
|
13408
|
-
if (input.contract.capacity.limit.kind === 'fixed') {
|
|
13409
|
-
limit = input.contract.capacity.limit.maxConcurrentJobs;
|
|
13410
|
-
} else {
|
|
13411
|
-
const hints =
|
|
13412
|
-
(await this.#options.getToolQueueHints?.(input.toolId)) ?? [];
|
|
13413
|
-
const declaredLimits = hints
|
|
13414
|
-
.map((hint) => hint.maxConcurrency)
|
|
13415
|
-
.filter(
|
|
13416
|
-
(value): value is number =>
|
|
13417
|
-
typeof value === 'number' && Number.isFinite(value) && value > 0,
|
|
13418
|
-
);
|
|
13419
|
-
limit = declaredLimits.length > 0 ? Math.min(...declaredLimits) : 1;
|
|
13420
|
-
}
|
|
13421
|
-
const state = this.asyncOperationCapacity.get(key) ?? {
|
|
13422
|
-
active: 0,
|
|
13423
|
-
waiters: [],
|
|
13424
|
-
};
|
|
13425
|
-
this.asyncOperationCapacity.set(key, state);
|
|
13426
|
-
if (state.active >= limit) {
|
|
13427
|
-
if (input.contract.capacity.onConflict === 'fail') {
|
|
13428
|
-
throw new Error(
|
|
13429
|
-
`Async provider capacity is full for ${input.provider}; the contract allows ${limit} active job(s).`,
|
|
13430
|
-
);
|
|
13431
|
-
}
|
|
13432
|
-
await new Promise<void>((resolve) => state.waiters.push(resolve));
|
|
13433
|
-
}
|
|
13434
|
-
state.active += 1;
|
|
13435
|
-
let released = false;
|
|
13436
|
-
return () => {
|
|
13437
|
-
if (released) return;
|
|
13438
|
-
released = true;
|
|
13439
|
-
state.active = Math.max(0, state.active - 1);
|
|
13440
|
-
state.waiters.shift()?.();
|
|
13441
|
-
if (state.active === 0 && state.waiters.length === 0) {
|
|
13442
|
-
this.asyncOperationCapacity.delete(key);
|
|
13443
|
-
}
|
|
13444
|
-
};
|
|
13445
|
-
}
|
|
13446
|
-
|
|
13447
|
-
private asyncOperationActionReceiptKey(input: {
|
|
13448
|
-
action: string;
|
|
13449
|
-
parentReceiptKey: string | null;
|
|
13450
|
-
jobId: string;
|
|
13451
|
-
phase: string;
|
|
13452
|
-
}): string | null {
|
|
13453
|
-
if (!input.parentReceiptKey) return null;
|
|
13454
|
-
return `${buildDurableToolReceiptPrefix({
|
|
13455
|
-
orgId: this.#options.orgId,
|
|
13456
|
-
toolId: input.action,
|
|
13457
|
-
})}${stableDigest(
|
|
13458
|
-
stableStringify({
|
|
13459
|
-
parentReceiptKey: input.parentReceiptKey,
|
|
13460
|
-
jobId: input.jobId,
|
|
13461
|
-
phase: input.phase,
|
|
13462
|
-
}),
|
|
13463
|
-
)}`;
|
|
13464
|
-
}
|
|
13465
|
-
|
|
13466
|
-
private async resolveAsyncOperation(input: {
|
|
13467
|
-
toolId: string;
|
|
13468
|
-
startInput: Record<string, unknown>;
|
|
13469
|
-
startResponse: ParsedToolExecuteResponse;
|
|
13470
|
-
contract: AsyncOperationContractWire;
|
|
13471
|
-
receiptKey: string | null;
|
|
13472
|
-
}): Promise<ParsedToolExecuteResponse> {
|
|
13473
|
-
const responseValue = (response: ParsedToolExecuteResponse): unknown => {
|
|
13474
|
-
if (response.toolResponse && 'raw' in response.toolResponse) {
|
|
13475
|
-
return response.toolResponse.raw;
|
|
13476
|
-
}
|
|
13477
|
-
const result = recordOrNull(response.result);
|
|
13478
|
-
return result && Object.prototype.hasOwnProperty.call(result, 'data')
|
|
13479
|
-
? result.data
|
|
13480
|
-
: response.result;
|
|
13481
|
-
};
|
|
13482
|
-
const startResult = responseValue(input.startResponse);
|
|
13483
|
-
const lifecycle = await executeAsyncOperationLifecycle({
|
|
13484
|
-
contract: input.contract,
|
|
13485
|
-
startInput: input.startInput,
|
|
13486
|
-
startResult,
|
|
13487
|
-
jobId: input.startResponse.jobId,
|
|
13488
|
-
sleep: async (delayMs) =>
|
|
13489
|
-
await this.sleepWithCheckpointHeartbeat(delayMs),
|
|
13490
|
-
responseValue,
|
|
13491
|
-
executeAction: async (request) => {
|
|
13492
|
-
const receiptKey = this.asyncOperationActionReceiptKey({
|
|
13493
|
-
action: request.action,
|
|
13494
|
-
parentReceiptKey: input.receiptKey,
|
|
13495
|
-
jobId: request.jobId,
|
|
13496
|
-
phase: `${request.phase}:${request.pollAttempt}:${request.page ?? ''}`,
|
|
13497
|
-
});
|
|
13498
|
-
return await this.callToolExecutionAPI(request.action, request.input, {
|
|
13499
|
-
skipAsyncOperationResolution: true,
|
|
13500
|
-
durableCallReceiptKey: receiptKey,
|
|
13501
|
-
providerIdempotencyReceiptKey: receiptKey,
|
|
13502
|
-
providerIdempotencyKey: receiptKey,
|
|
13503
|
-
});
|
|
13504
|
-
},
|
|
13505
|
-
});
|
|
13506
|
-
if (lifecycle.kind === 'timed_out') {
|
|
13507
|
-
const polling = resolveAsyncOperationPolling({
|
|
13508
|
-
contract: input.contract,
|
|
13509
|
-
startInput: input.startInput,
|
|
13510
|
-
});
|
|
13511
|
-
throw new Error(
|
|
13512
|
-
`Async operation ${input.toolId} (${lifecycle.jobId}) did not reach a terminal state within ${polling.timeoutMs}ms.`,
|
|
13513
|
-
);
|
|
13514
|
-
}
|
|
13515
|
-
if (
|
|
13516
|
-
lifecycle.classification.outcome === 'failed' ||
|
|
13517
|
-
lifecycle.classification.outcome === 'cancelled'
|
|
13518
|
-
) {
|
|
13410
|
+
private providerOperationKeyForQueuedToolCall(
|
|
13411
|
+
request: ToolCallRequest,
|
|
13412
|
+
): string {
|
|
13413
|
+
const operationKey = request.providerIdempotencyKeyBase?.trim();
|
|
13414
|
+
if (!operationKey) {
|
|
13519
13415
|
throw new Error(
|
|
13520
|
-
|
|
13521
|
-
`Async operation ${input.toolId} (${lifecycle.jobId}) ended ${lifecycle.classification.outcome}.`,
|
|
13416
|
+
`Queued tool call ${request.callId} is missing its semantic provider operation identity.`,
|
|
13522
13417
|
);
|
|
13523
13418
|
}
|
|
13524
|
-
|
|
13525
|
-
const selectedResponse = lifecycle.finishResponse ?? lifecycle.pollResponse;
|
|
13526
|
-
return {
|
|
13527
|
-
...selectedResponse,
|
|
13528
|
-
status:
|
|
13529
|
-
lifecycle.classification.outcome === 'no_result' ||
|
|
13530
|
-
materialized.outcome === 'no_result'
|
|
13531
|
-
? 'no_result'
|
|
13532
|
-
: 'completed',
|
|
13533
|
-
result: { data: materialized.result },
|
|
13534
|
-
toolResponse: materializedAsyncToolResponse(
|
|
13535
|
-
selectedResponse.toolResponse,
|
|
13536
|
-
materialized.result,
|
|
13537
|
-
),
|
|
13538
|
-
};
|
|
13419
|
+
return operationKey;
|
|
13539
13420
|
}
|
|
13540
13421
|
|
|
13541
13422
|
private async callToolExecutionAPI(
|
|
@@ -13551,7 +13432,29 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
13551
13432
|
const requestsDurableInvocationFence =
|
|
13552
13433
|
this.#options.requestDurableInvocationFence === true ||
|
|
13553
13434
|
this.#options.durableInvocationFence === true;
|
|
13554
|
-
const
|
|
13435
|
+
const asyncContract = options?.skipAsyncOperationResolution
|
|
13436
|
+
? null
|
|
13437
|
+
: ((await this.#options.getToolAsyncOperation?.(toolId)) ?? null);
|
|
13438
|
+
const shouldWaitForAsyncOperation = Boolean(
|
|
13439
|
+
asyncContract && asyncOperationShouldWait(asyncContract, input),
|
|
13440
|
+
);
|
|
13441
|
+
const executionAuthScopeDigest = options?.executionAuthScopeDigest
|
|
13442
|
+
? options.executionAuthScopeDigest.trim() || null
|
|
13443
|
+
: ((await this.resolveToolAuthScopeDigest(toolId))?.trim() ?? null);
|
|
13444
|
+
const provider =
|
|
13445
|
+
(await this.#options.getToolProvider?.(toolId))?.trim() ||
|
|
13446
|
+
toolId.split(/[._]/)[0]?.trim() ||
|
|
13447
|
+
'provider';
|
|
13448
|
+
// A receipt is a terminal-output cache, never an async provider-job
|
|
13449
|
+
// record. Launch/poll/finish transport must therefore remain receiptless:
|
|
13450
|
+
// if this process stops, a later cache miss starts a new provider request
|
|
13451
|
+
// rather than attaching itself to this one.
|
|
13452
|
+
const uncachedAsyncLifecycleCall =
|
|
13453
|
+
options?.uncachedAsyncLifecycleCall === true ||
|
|
13454
|
+
shouldWaitForAsyncOperation;
|
|
13455
|
+
const usesDurableInvocationFence =
|
|
13456
|
+
requestsDurableInvocationFence && !uncachedAsyncLifecycleCall;
|
|
13457
|
+
const executeSuffix = usesDurableInvocationFence
|
|
13555
13458
|
? 'execute-fenced-v1'
|
|
13556
13459
|
: 'execute';
|
|
13557
13460
|
// Keep receipts, runner heartbeats, terminals, sheets, and runtime control
|
|
@@ -13566,17 +13469,6 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
13566
13469
|
options?.timeoutMs,
|
|
13567
13470
|
this.currentAuthoringContractEdition,
|
|
13568
13471
|
);
|
|
13569
|
-
const provider = toolId.split(/[._]/)[0]?.trim() || 'provider';
|
|
13570
|
-
const asyncContract = options?.skipAsyncOperationResolution
|
|
13571
|
-
? null
|
|
13572
|
-
: ((await this.#options.getToolAsyncOperation?.(toolId)) ?? null);
|
|
13573
|
-
const shouldWaitForAsyncOperation = Boolean(
|
|
13574
|
-
asyncContract && asyncOperationShouldWait(asyncContract, input),
|
|
13575
|
-
);
|
|
13576
|
-
const effectiveInput =
|
|
13577
|
-
asyncContract && shouldWaitForAsyncOperation
|
|
13578
|
-
? asyncOperationDurableStartInput(asyncContract, input)
|
|
13579
|
-
: input;
|
|
13580
13472
|
const activityId = `provider:${toolId}`;
|
|
13581
13473
|
let retryActivityEmitted = false;
|
|
13582
13474
|
let toolCallSucceeded = false;
|
|
@@ -13585,16 +13477,6 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
13585
13477
|
// execution. Per-provider rate admission is intentionally deferred until
|
|
13586
13478
|
// each physical fetch is ready to leave this process.
|
|
13587
13479
|
const admissionStartedAt = Date.now();
|
|
13588
|
-
const asyncCapacityRelease =
|
|
13589
|
-
asyncContract && shouldWaitForAsyncOperation
|
|
13590
|
-
? await this.acquireAsyncOperationCapacity({
|
|
13591
|
-
toolId,
|
|
13592
|
-
provider,
|
|
13593
|
-
contract: asyncContract,
|
|
13594
|
-
authScopeDigest:
|
|
13595
|
-
(await this.resolveToolAuthScopeDigest(toolId))?.trim() ?? null,
|
|
13596
|
-
})
|
|
13597
|
-
: null;
|
|
13598
13480
|
let toolSlot: RuntimeResourceLease;
|
|
13599
13481
|
try {
|
|
13600
13482
|
toolSlot = await this.resourceGovernor.acquireTool({
|
|
@@ -13603,7 +13485,6 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
13603
13485
|
toolId,
|
|
13604
13486
|
});
|
|
13605
13487
|
} catch (error) {
|
|
13606
|
-
asyncCapacityRelease?.();
|
|
13607
13488
|
if (!(error instanceof ProviderExhaustedError)) throw error;
|
|
13608
13489
|
const retryAtMs = Date.parse(error.retryAt);
|
|
13609
13490
|
throw createToolHttpError(
|
|
@@ -13659,9 +13540,10 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
13659
13540
|
// Snapshot the caller-controlled payload exactly once. The replay
|
|
13660
13541
|
// decision and every physical attempt must refer to the same bytes,
|
|
13661
13542
|
// even if the original object has getters or is later mutated.
|
|
13662
|
-
const toolInputSnapshot = JSON.parse(
|
|
13663
|
-
|
|
13664
|
-
|
|
13543
|
+
const toolInputSnapshot = JSON.parse(JSON.stringify(input)) as Record<
|
|
13544
|
+
string,
|
|
13545
|
+
unknown
|
|
13546
|
+
>;
|
|
13665
13547
|
const retryPolicy = await this.#options
|
|
13666
13548
|
.getToolRetryPolicy?.(toolId, toolInputSnapshot)
|
|
13667
13549
|
.catch(() => null);
|
|
@@ -13681,19 +13563,17 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
13681
13563
|
const ambiguousTransportReplayAllowed =
|
|
13682
13564
|
readOnlyQuery || retrySafeTransientHttp;
|
|
13683
13565
|
const invocationOrgId = this.#options.orgId?.trim() ?? '';
|
|
13684
|
-
const durableCallReceiptKey =
|
|
13685
|
-
|
|
13686
|
-
(
|
|
13687
|
-
|
|
13688
|
-
|
|
13689
|
-
|
|
13690
|
-
|
|
13691
|
-
|
|
13692
|
-
|
|
13693
|
-
|
|
13694
|
-
|
|
13695
|
-
? options.executionAuthScopeDigest.trim() || null
|
|
13696
|
-
: ((await this.resolveToolAuthScopeDigest(toolId))?.trim() ?? null);
|
|
13566
|
+
const durableCallReceiptKey = uncachedAsyncLifecycleCall
|
|
13567
|
+
? null
|
|
13568
|
+
: options?.durableCallReceiptKey?.trim() ||
|
|
13569
|
+
(usesDurableInvocationFence && invocationOrgId
|
|
13570
|
+
? `${buildDurableToolReceiptPrefix({
|
|
13571
|
+
orgId: invocationOrgId,
|
|
13572
|
+
toolId,
|
|
13573
|
+
})}${stableDigest(
|
|
13574
|
+
`transport:${this.currentRunId}:${toolId}:${crypto.randomUUID()}`,
|
|
13575
|
+
)}`
|
|
13576
|
+
: null);
|
|
13697
13577
|
const executionRequestNonce = crypto.randomUUID();
|
|
13698
13578
|
// This identifies one logical tool call to billing. Physical retry
|
|
13699
13579
|
// count remains in request metadata, so a transport retry cannot
|
|
@@ -13713,6 +13593,29 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
13713
13593
|
`Tool ${toolId} requires a provider execution fence but no provider operation identity was supplied.`,
|
|
13714
13594
|
);
|
|
13715
13595
|
}
|
|
13596
|
+
const canonicalOperationActionVersion = providerExecutionFenceKey
|
|
13597
|
+
? (
|
|
13598
|
+
await this.#options.getToolActionCacheVersion?.(toolId)
|
|
13599
|
+
)?.trim() || null
|
|
13600
|
+
: null;
|
|
13601
|
+
const canonicalOperation =
|
|
13602
|
+
providerExecutionFenceKey && canonicalOperationActionVersion
|
|
13603
|
+
? {
|
|
13604
|
+
version: 1,
|
|
13605
|
+
tool_id: toolId,
|
|
13606
|
+
action_version: canonicalOperationActionVersion,
|
|
13607
|
+
play_scope: this.currentGovernance.currentPlayId || 'play',
|
|
13608
|
+
output_intent: options?.completedResultCache
|
|
13609
|
+
? 'dataset'
|
|
13610
|
+
: 'value',
|
|
13611
|
+
...(options?.providerOperationForceRefreshIdentities?.length
|
|
13612
|
+
? {
|
|
13613
|
+
force_refresh_identities:
|
|
13614
|
+
options.providerOperationForceRefreshIdentities,
|
|
13615
|
+
}
|
|
13616
|
+
: {}),
|
|
13617
|
+
}
|
|
13618
|
+
: null;
|
|
13716
13619
|
// Every runtime provider call carries at least a tool-scoped
|
|
13717
13620
|
// attribution. Callers that know the execution location (row-scoped
|
|
13718
13621
|
// map cells, batched owners) supply the fuller scope.
|
|
@@ -13724,7 +13627,10 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
13724
13627
|
metadata: {
|
|
13725
13628
|
parent_run_id: this.#options.runId,
|
|
13726
13629
|
invocation_attempt: invocationAttempt,
|
|
13727
|
-
...(
|
|
13630
|
+
...(shouldWaitForAsyncOperation
|
|
13631
|
+
? { async_operation_wait: true }
|
|
13632
|
+
: {}),
|
|
13633
|
+
...(usesDurableInvocationFence
|
|
13728
13634
|
? { invocation_fence_version: 1 }
|
|
13729
13635
|
: {}),
|
|
13730
13636
|
...(durableCallReceiptKey
|
|
@@ -13749,6 +13655,9 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
13749
13655
|
: {}),
|
|
13750
13656
|
}
|
|
13751
13657
|
: {}),
|
|
13658
|
+
...(canonicalOperation
|
|
13659
|
+
? { canonical_operation: canonicalOperation }
|
|
13660
|
+
: {}),
|
|
13752
13661
|
...(options?.customerDbDataset
|
|
13753
13662
|
? {
|
|
13754
13663
|
query_result_dataset: {
|
|
@@ -14377,29 +14286,8 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
14377
14286
|
? 'array'
|
|
14378
14287
|
: typeof parsed.result,
|
|
14379
14288
|
});
|
|
14380
|
-
const parsedValue =
|
|
14381
|
-
parsed.toolResponse && 'raw' in parsed.toolResponse
|
|
14382
|
-
? parsed.toolResponse.raw
|
|
14383
|
-
: (recordOrNull(parsed.result)?.data ?? parsed.result);
|
|
14384
|
-
const asyncStartPending =
|
|
14385
|
-
asyncContract && shouldWaitForAsyncOperation
|
|
14386
|
-
? classifyAsyncOperationResult(asyncContract, parsedValue)
|
|
14387
|
-
.outcome === 'running'
|
|
14388
|
-
: false;
|
|
14389
|
-
const resolved =
|
|
14390
|
-
asyncContract &&
|
|
14391
|
-
shouldWaitForAsyncOperation &&
|
|
14392
|
-
(parsed.status === 'running' || asyncStartPending)
|
|
14393
|
-
? await this.resolveAsyncOperation({
|
|
14394
|
-
toolId,
|
|
14395
|
-
startInput: input,
|
|
14396
|
-
startResponse: parsed,
|
|
14397
|
-
contract: asyncContract,
|
|
14398
|
-
receiptKey: durableCallReceiptKey,
|
|
14399
|
-
})
|
|
14400
|
-
: parsed;
|
|
14401
14289
|
toolCallSucceeded = true;
|
|
14402
|
-
return
|
|
14290
|
+
return parsed;
|
|
14403
14291
|
}
|
|
14404
14292
|
},
|
|
14405
14293
|
);
|
|
@@ -14426,7 +14314,6 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
14426
14314
|
if (!toolSlotTransferred) {
|
|
14427
14315
|
toolSlot.release();
|
|
14428
14316
|
}
|
|
14429
|
-
asyncCapacityRelease?.();
|
|
14430
14317
|
}
|
|
14431
14318
|
}
|
|
14432
14319
|
|