deepline 0.3.391 → 0.3.393
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 +156 -24
- package/dist/bundling-sources/sdk/src/compat.ts +9 -0
- package/dist/bundling-sources/sdk/src/http.ts +5 -1
- package/dist/bundling-sources/sdk/src/index.ts +5 -0
- package/dist/bundling-sources/sdk/src/plays/csv-data-input.ts +3 -0
- package/dist/bundling-sources/sdk/src/plays/run-request.ts +182 -6
- package/dist/bundling-sources/sdk/src/release.ts +4 -1
- package/dist/bundling-sources/sdk/src/types.ts +15 -0
- package/dist/bundling-sources/shared_libs/integration-contracts/request-warnings.ts +64 -0
- package/dist/bundling-sources/shared_libs/play-data-plane/runtime-sheet-storage-contract.ts +315 -0
- package/dist/bundling-sources/shared_libs/play-runtime/async-operation.ts +41 -8
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +418 -44
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +36 -3
- package/dist/bundling-sources/shared_libs/play-runtime/durable-invocation-fence.ts +36 -0
- package/dist/bundling-sources/shared_libs/play-runtime/map-execution-frame.ts +11 -3
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/projection/gateway-adapter.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-semantics.ts +19 -1
- package/dist/bundling-sources/shared_libs/play-runtime/run-identity.ts +26 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +158 -10
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +235 -2
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +92 -58
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-pool.ts +7 -1
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-progress.ts +30 -7
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +1161 -316
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-event-search.ts +28 -15
- package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +24 -3
- package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +31 -0
- package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +37 -4
- package/dist/bundling-sources/shared_libs/plays/dataset.ts +172 -79
- package/dist/bundling-sources/shared_libs/plays/extension-bridge.ts +57 -0
- package/dist/bundling-sources/shared_libs/plays/run-summary-types.ts +1 -0
- package/dist/bundling-sources/shared_libs/plays/static-pipeline.ts +173 -1
- package/dist/cli/{auth-6QET7NFP.mjs → auth-5PXHA232.mjs} +4 -4
- package/dist/cli/auth-P3MVDTXM.js +39 -0
- package/dist/cli/{billing-KJAGTCFR.js → billing-JFB2XLSO.js} +101 -74
- package/dist/cli/{billing-HAQ2VDQX.mjs → billing-Z2N7ZAGM.mjs} +32 -5
- package/dist/cli/{chunk-556BMGEB.js → chunk-2HAD745V.js} +242 -183
- package/dist/cli/{chunk-HFTOOZGN.mjs → chunk-2LGC4EFT.mjs} +3 -3
- package/dist/cli/{chunk-QCWIORKR.mjs → chunk-4PT2VA3A.mjs} +2 -2
- package/dist/cli/{chunk-KA7X75HC.js → chunk-4XX6443T.js} +176 -87
- package/dist/cli/{chunk-YBZY3EZF.mjs → chunk-5KWKZIQR.mjs} +4 -4
- package/dist/cli/{chunk-VH4B5OYM.mjs → chunk-5PRVMXKB.mjs} +27 -2
- package/dist/cli/{chunk-DSWSEHRX.mjs → chunk-6I73X254.mjs} +1 -1
- package/dist/cli/{chunk-6L4P7Z6C.mjs → chunk-7HR7BGZL.mjs} +13 -3
- package/dist/cli/{chunk-4S6X74A3.js → chunk-AARX5L4B.js} +2 -2
- package/dist/cli/{chunk-MHLCMJ2E.js → chunk-CDFH2DYL.js} +20 -19
- package/dist/cli/{chunk-PFPP4BKS.mjs → chunk-FQ6VHPW4.mjs} +2 -2
- package/dist/cli/{chunk-5JLQPVNX.js → chunk-GSK6NHVM.js} +1 -0
- package/dist/cli/{chunk-TLHF54VX.mjs → chunk-HLEESTCH.mjs} +1 -1
- package/dist/cli/{chunk-EDWJY6T4.mjs → chunk-HQXL5526.mjs} +1 -0
- package/dist/cli/{chunk-Q6EOHNRH.mjs → chunk-JI2B4RZP.mjs} +119 -6
- package/dist/cli/{chunk-I3MKXASV.mjs → chunk-KIYU2TGL.mjs} +2 -2
- package/dist/cli/{chunk-Y6VOGHF2.mjs → chunk-KZZC33PY.mjs} +0 -181
- package/dist/cli/{chunk-XBPLTHGB.js → chunk-L2SYLGRP.js} +8 -8
- package/dist/cli/{chunk-K3ODOHOI.mjs → chunk-LMMEAMA7.mjs} +3 -2
- package/dist/cli/{chunk-KWF4HJF7.mjs → chunk-LZPCDB7U.mjs} +3 -2
- package/dist/cli/{chunk-I7EVBLTW.js → chunk-M7Y7JYZX.js} +32 -7
- package/dist/cli/{chunk-QGKPTBRR.js → chunk-MS6GUPBB.js} +3 -3
- package/dist/cli/{chunk-ME2DZAHC.js → chunk-OXNOAD6Y.js} +26 -26
- package/dist/cli/{chunk-XEV5LMS2.js → chunk-PTDECJGP.js} +50 -50
- package/dist/cli/{chunk-5O4B6AUN.js → chunk-PWSWAN7Q.js} +25 -2
- package/dist/cli/{chunk-DRZFTRZJ.mjs → chunk-Q26LUSQ2.mjs} +79 -20
- package/dist/cli/{chunk-3VN4FTE3.js → chunk-RDXFGPJU.js} +1 -182
- package/dist/cli/{chunk-7E66A24V.mjs → chunk-RKMB3N7K.mjs} +25 -2
- package/dist/cli/{chunk-WLODHQ7S.js → chunk-RWHWF5ZJ.js} +3 -2
- package/dist/cli/{chunk-H3DCZOXR.mjs → chunk-STNYIMIV.mjs} +97 -8
- package/dist/cli/chunk-UBKA33KY.mjs +310 -0
- package/dist/cli/{chunk-PAIMOGCM.js → chunk-UHNDVD3P.js} +125 -12
- package/dist/cli/{chunk-H2ER7QWR.mjs → chunk-UOGZUA24.mjs} +180 -18
- package/dist/cli/{chunk-RTH26NF3.js → chunk-VIQJIX2P.js} +73 -63
- package/dist/cli/{chunk-ED42XCOW.js → chunk-VIWE6WRK.js} +629 -467
- package/dist/cli/{chunk-RA2SDTSX.js → chunk-W4WOMXR3.js} +15 -15
- package/dist/cli/chunk-XDVT3GOM.js +310 -0
- package/dist/cli/{chunk-UOSKJIXE.js → chunk-Z3OOZM5L.js} +8 -8
- package/dist/cli/{csv-V7UHOBFE.mjs → csv-KJWZHKCH.mjs} +3 -3
- package/dist/cli/{csv-2WSF5TEM.js → csv-P3Z3YE7H.js} +12 -12
- package/dist/cli/{db-TD375E4N.mjs → db-NCM4CQIS.mjs} +6 -5
- package/dist/cli/{db-42RL4FZD.js → db-NUZGJNQC.js} +31 -30
- package/dist/cli/{describe-GPUKAWOZ.mjs → describe-WT6MM2GO.mjs} +18 -17
- package/dist/cli/{describe-7WFCBZUY.js → describe-YGSEN5WC.js} +25 -24
- package/dist/cli/{executions-LGVG3FPH.js → executions-PCOLHN7Q.js} +8 -7
- package/dist/cli/{executions-M7O63JNA.mjs → executions-XS2IPJ4W.mjs} +6 -5
- package/dist/cli/{feedback-PZZA2M4S.js → feedback-4MSH7TFU.js} +7 -7
- package/dist/cli/{feedback-6PBSKVB2.mjs → feedback-CCLTS5SV.mjs} +3 -3
- package/dist/cli/{git-2VIFM5DI.mjs → git-K2DHPMPC.mjs} +4 -4
- package/dist/cli/git-RNM45FX4.js +23 -0
- package/dist/cli/index.js +96 -92
- package/dist/cli/index.mjs +46 -42
- package/dist/cli/{legacy-noops-DPUBZI7N.mjs → legacy-noops-B5S4AR3P.mjs} +4 -4
- package/dist/cli/{legacy-noops-IQ6QTZNZ.js → legacy-noops-GPNOPFXV.js} +7 -7
- package/dist/cli/{monitors-KBZVQG2U.mjs → monitors-JLMAIPRG.mjs} +6 -5
- package/dist/cli/{monitors-6GWAZFYV.js → monitors-ZAWD7NSN.js} +87 -86
- package/dist/cli/{org-JKGTHXOZ.js → org-P2KN4CES.js} +66 -65
- package/dist/cli/{org-CP3VSJBX.mjs → org-T3JTHP4G.mjs} +6 -5
- package/dist/cli/{play-DD2ME4L4.mjs → play-NK6BLRGL.mjs} +12 -11
- package/dist/cli/play-PDXR6JFB.js +158 -0
- package/dist/cli/{providers-JUNOFKI7.mjs → providers-3NQRA7IT.mjs} +6 -5
- package/dist/cli/{providers-P34QYYG6.js → providers-5QRZN3DT.js} +9 -8
- package/dist/cli/{quickstart-N6SWOK7E.mjs → quickstart-PIA2JGPV.mjs} +3 -3
- package/dist/cli/{quickstart-UL3DEFA4.js → quickstart-R7NUZGNW.js} +5 -5
- package/dist/cli/run-identity-G257GQED.js +25 -0
- package/dist/cli/run-identity-MRNCEGV2.mjs +25 -0
- package/dist/cli/{search-SC7I56HX.mjs → search-AIDIIGLY.mjs} +7 -6
- package/dist/cli/search-QRWL7MFM.js +27 -0
- package/dist/cli/{secrets-6ZJE7ZPM.js → secrets-BT3PGMWN.js} +15 -14
- package/dist/cli/{secrets-RTARNYF5.mjs → secrets-OQNHL45O.mjs} +6 -5
- package/dist/cli/{sessions-6IZ2XHGW.js → sessions-EYXWOSWK.js} +5 -5
- package/dist/cli/{sessions-7RKV2T3O.mjs → sessions-SF7WJPJP.mjs} +4 -4
- package/dist/cli/{settings-YX3XJDXE.js → settings-BA3GHBOD.js} +76 -75
- package/dist/cli/{settings-V6PXA6HY.mjs → settings-Y57JTFDD.mjs} +9 -8
- package/dist/cli/{setup-V2O4UJNT.mjs → setup-7GR5GBHA.mjs} +6 -6
- package/dist/cli/{setup-CQ5VRNNU.js → setup-RERXYC66.js} +41 -41
- package/dist/cli/{skills-VHJZWEHA.mjs → skills-SJWIQAV5.mjs} +4 -4
- package/dist/cli/skills-V7EY75HB.js +28 -0
- package/dist/cli/tools-H5HLBCHH.js +41 -0
- package/dist/cli/{tools-OD5WKEWY.mjs → tools-ZF3JXPNL.mjs} +10 -9
- package/dist/cli/update-472HA3LT.js +28 -0
- package/dist/cli/{update-CRMNOMJ3.mjs → update-FFFYQAOJ.mjs} +6 -6
- package/dist/cli/{workflow-XHORSD5T.js → workflow-SZJVQH4T.js} +17 -16
- package/dist/cli/{workflow-EVDYNCDD.mjs → workflow-XVFEEGAS.mjs} +7 -6
- package/dist/{client-DpzZi4fU.d.mts → client-DU5dtHDy.d.mts} +157 -4
- package/dist/{client-BhK9kix9.d.ts → client-mCE88hC4.d.ts} +157 -4
- package/dist/{compiler-manifest-CY2v8ab0.d.ts → compiler-manifest-DE4RdAiY.d.mts} +61 -1
- package/dist/{compiler-manifest-CY2v8ab0.d.mts → compiler-manifest-DE4RdAiY.d.ts} +61 -1
- package/dist/{index-B9fBZH1g.d.mts → index-BI6nWgaV.d.mts} +1 -1
- package/dist/{index-BZ6uzZJl.d.ts → index-Bi-f7HFT.d.ts} +1 -1
- package/dist/index.d.mts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +120 -29
- package/dist/index.mjs +111 -20
- package/dist/install-integrity.json +94 -88
- package/dist/plays/bundle-play-file.d.mts +3 -3
- package/dist/plays/bundle-play-file.d.ts +3 -3
- package/dist/plays/bundle-play-file.mjs +2 -2
- package/dist/plays/csv-data-input.d.mts +2 -0
- package/dist/plays/csv-data-input.d.ts +2 -0
- package/dist/plays/csv-data-input.js +2 -2
- package/dist/plays/csv-data-input.mjs +1 -1
- package/dist/plays/run-request.d.mts +2 -2
- package/dist/plays/run-request.d.ts +2 -2
- package/dist/plays/run-request.js +5 -2
- package/dist/plays/run-request.mjs +4 -1
- package/dist/release.d.mts +4 -1
- package/dist/release.d.ts +4 -1
- package/dist/release.js +2 -2
- package/dist/release.mjs +1 -1
- package/package.json +1 -1
- package/dist/cli/auth-6ZUH42ZA.js +0 -39
- package/dist/cli/git-ZSJD3BID.js +0 -23
- package/dist/cli/play-G5GVMIBN.js +0 -157
- package/dist/cli/search-W4GXWTOM.js +0 -26
- package/dist/cli/skills-LBZPJTPI.js +0 -28
- package/dist/cli/tools-TQNM473H.js +0 -40
- package/dist/cli/update-QY6GAK6B.js +0 -28
|
@@ -25,6 +25,10 @@ import { AsyncLocalStorage } from 'async_hooks';
|
|
|
25
25
|
import { isDatasetPayloadField } from '../play-data-plane/sheet-contract';
|
|
26
26
|
import {
|
|
27
27
|
createDeferredPlayDataset,
|
|
28
|
+
copyPlayDatasetSourceRowIdentity,
|
|
29
|
+
getPlayDatasetSourceRowIdentity,
|
|
30
|
+
getPlayDatasetSourceRowIdentityAt,
|
|
31
|
+
setPlayDatasetSourceRowIdentity,
|
|
28
32
|
deserializeLegacyPlayDataset,
|
|
29
33
|
deserializePlayDatasetCell,
|
|
30
34
|
isPlayDataset,
|
|
@@ -32,6 +36,7 @@ import {
|
|
|
32
36
|
isSerializedPlayDatasetCell,
|
|
33
37
|
iteratePlayDatasetInputPages,
|
|
34
38
|
materializePlayDatasetInput,
|
|
39
|
+
residentPlayDatasetRows,
|
|
35
40
|
resolveMaterializeLimitCap,
|
|
36
41
|
serializePlayDatasetCell,
|
|
37
42
|
} from '../plays/dataset';
|
|
@@ -69,7 +74,10 @@ import type {
|
|
|
69
74
|
ProviderPacingGrant,
|
|
70
75
|
} from './governor/rate-state-backend';
|
|
71
76
|
import type { MapRowOutcome } from './durability-store';
|
|
72
|
-
import {
|
|
77
|
+
import {
|
|
78
|
+
sanitizePostgresJsonValue,
|
|
79
|
+
stringifyPostgresJson,
|
|
80
|
+
} from './postgres-json';
|
|
73
81
|
import { providerCapacityLabel } from './activity-observation';
|
|
74
82
|
import { RUNTIME_ROW_OUTPUT_NOT_JSON_SERIALIZABLE } from './runtime-sheet-row-transition';
|
|
75
83
|
import {
|
|
@@ -91,7 +99,10 @@ import {
|
|
|
91
99
|
import {
|
|
92
100
|
DURABLE_INVOCATION_FENCE_VERSION,
|
|
93
101
|
durableInvocationFenceExecutePath,
|
|
102
|
+
buildRuntimeExecutionRecoveryKey,
|
|
103
|
+
RUNTIME_EXECUTION_RECOVERY_SUPPORTED_HEADER,
|
|
94
104
|
} from './durable-invocation-fence';
|
|
105
|
+
import { isImmutableRuntimeExecutionRecoveryOrigin } from './scheduler-backends/runtime-api-origin';
|
|
95
106
|
import {
|
|
96
107
|
isRetryableSecretResolutionStatus,
|
|
97
108
|
secretResolutionRetryDecision,
|
|
@@ -304,9 +315,11 @@ import {
|
|
|
304
315
|
classifyAsyncOperationResult,
|
|
305
316
|
executeAsyncOperationLifecycle,
|
|
306
317
|
extractAsyncOperationJobId,
|
|
318
|
+
formatAsyncOperationTimeout,
|
|
307
319
|
readAsyncOperationPath,
|
|
308
320
|
asyncOperationShouldWait,
|
|
309
321
|
hasExplicitAsyncOperationTerminalMatch,
|
|
322
|
+
resolveAsyncOperationPolling,
|
|
310
323
|
type AsyncOperationContractWire,
|
|
311
324
|
} from './async-operation';
|
|
312
325
|
import {
|
|
@@ -8206,6 +8219,10 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
8206
8219
|
options.onRowError,
|
|
8207
8220
|
);
|
|
8208
8221
|
validateOptionalPlayAuthoringField('ctx.dataset.run.mode', options.mode);
|
|
8222
|
+
validateOptionalPlayAuthoringField(
|
|
8223
|
+
'ctx.dataset.run.rowIdentity',
|
|
8224
|
+
options.rowIdentity,
|
|
8225
|
+
);
|
|
8209
8226
|
validateOptionalPlayAuthoringField(
|
|
8210
8227
|
'ctx.dataset.run.undrawnColumns',
|
|
8211
8228
|
options.undrawnColumns,
|
|
@@ -8260,11 +8277,53 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
8260
8277
|
),
|
|
8261
8278
|
);
|
|
8262
8279
|
|
|
8280
|
+
const residentSourceRows = isPlayDataset(items)
|
|
8281
|
+
? residentPlayDatasetRows(items)
|
|
8282
|
+
: Array.isArray(items)
|
|
8283
|
+
? items
|
|
8284
|
+
: null;
|
|
8285
|
+
const csvSource =
|
|
8286
|
+
(isPlayDataset(items) && items.datasetKind === 'csv') ||
|
|
8287
|
+
residentSourceRows?.some(
|
|
8288
|
+
(_row, index) =>
|
|
8289
|
+
getPlayDatasetSourceRowIdentityAt(residentSourceRows, index) !==
|
|
8290
|
+
undefined,
|
|
8291
|
+
) === true;
|
|
8292
|
+
const csvOccurrenceIdentityRequired =
|
|
8293
|
+
csvSource &&
|
|
8294
|
+
this.currentRuntimeSemantics.datasets.csvRowIdentity === 'source_row';
|
|
8295
|
+
const rowIdentityMode = csvOccurrenceIdentityRequired
|
|
8296
|
+
? 'source_row'
|
|
8297
|
+
: (options?.rowIdentity ??
|
|
8298
|
+
(options?.key !== undefined
|
|
8299
|
+
? 'key'
|
|
8300
|
+
: csvSource
|
|
8301
|
+
? this.currentRuntimeSemantics.datasets.csvRowIdentity
|
|
8302
|
+
: 'key'));
|
|
8303
|
+
const useSourceRowIdentity = rowIdentityMode === 'source_row';
|
|
8263
8304
|
const userKeyOption = options?.key;
|
|
8264
8305
|
let explicitKeyResolver:
|
|
8265
8306
|
| ((row: Record<string, unknown>, index: number) => string)
|
|
8266
8307
|
| null = null;
|
|
8267
|
-
if (
|
|
8308
|
+
if (useSourceRowIdentity) {
|
|
8309
|
+
explicitKeyResolver = (row, index) => {
|
|
8310
|
+
const source = getPlayDatasetSourceRowIdentity(row);
|
|
8311
|
+
const inputRow = Object.fromEntries(
|
|
8312
|
+
Object.entries(toSerializableCsvAliasedRow(row)).filter(
|
|
8313
|
+
([field]) => !field.startsWith('__deepline'),
|
|
8314
|
+
),
|
|
8315
|
+
);
|
|
8316
|
+
// Position distinguishes equal records; the input digest prevents a
|
|
8317
|
+
// changed file at the same path from replaying an earlier row's output.
|
|
8318
|
+
return JSON.stringify([
|
|
8319
|
+
'source-row-v1',
|
|
8320
|
+
source?.datasetId ??
|
|
8321
|
+
(isPlayDataset(items) ? items.datasetId : normalizedMapNamespace),
|
|
8322
|
+
source?.rowIndex ?? index,
|
|
8323
|
+
stableDigest(stableStringify(inputRow)),
|
|
8324
|
+
]);
|
|
8325
|
+
};
|
|
8326
|
+
} else if (userKeyOption !== undefined) {
|
|
8268
8327
|
explicitKeyResolver = (row, index) => {
|
|
8269
8328
|
const inputRow = stripFieldOutputs(row);
|
|
8270
8329
|
const raw =
|
|
@@ -8469,6 +8528,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
8469
8528
|
let failedCount = 0;
|
|
8470
8529
|
let duplicateReuseCount = 0;
|
|
8471
8530
|
let executedCount = 0;
|
|
8531
|
+
let sourceRowsCount = 0;
|
|
8472
8532
|
const failureSamples: Array<{ rowLabel: string; error: string }> = [];
|
|
8473
8533
|
const failureSampleErrors = new Set<string>();
|
|
8474
8534
|
const previewRows: Record<string, unknown>[] = [];
|
|
@@ -8572,9 +8632,17 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
8572
8632
|
offset: number;
|
|
8573
8633
|
}): Promise<PreparedPage> => {
|
|
8574
8634
|
this.assertRowAdmissionOpen();
|
|
8575
|
-
|
|
8576
|
-
|
|
8577
|
-
|
|
8635
|
+
sourceRowsCount += page.rows.length;
|
|
8636
|
+
const pageRawItems = page.rows.map((item, index) => {
|
|
8637
|
+
const row = this.toOutputRow(item as Record<string, unknown>);
|
|
8638
|
+
const sourceIdentity = getPlayDatasetSourceRowIdentityAt(
|
|
8639
|
+
page.rows,
|
|
8640
|
+
index,
|
|
8641
|
+
);
|
|
8642
|
+
return sourceIdentity
|
|
8643
|
+
? setPlayDatasetSourceRowIdentity(row, sourceIdentity)
|
|
8644
|
+
: row;
|
|
8645
|
+
});
|
|
8578
8646
|
const pageMemoryEstimate = estimateRuntimeMapRowsMemory(pageRawItems);
|
|
8579
8647
|
if (pageMemoryEstimate.estimatedResidentBytes > pageBudgetBytes) {
|
|
8580
8648
|
throw new RuntimeMapMemoryLimitError({
|
|
@@ -8588,6 +8656,9 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
8588
8656
|
const originalIndex = page.offset + index;
|
|
8589
8657
|
return {
|
|
8590
8658
|
...toSerializableCsvAliasedRow(row),
|
|
8659
|
+
...(getPlayDatasetSourceRowIdentity(row)
|
|
8660
|
+
? { __deeplineSourceRow: getPlayDatasetSourceRowIdentity(row) }
|
|
8661
|
+
: {}),
|
|
8591
8662
|
...mapRowOutcomeRuntimeFields({
|
|
8592
8663
|
key: seedRowIdentity(row, originalIndex),
|
|
8593
8664
|
inputIndex: originalIndex,
|
|
@@ -8630,9 +8701,8 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
8630
8701
|
completedRowsByKey.set(rowKey, row);
|
|
8631
8702
|
}
|
|
8632
8703
|
|
|
8633
|
-
const seededItems =
|
|
8704
|
+
const seededItems = pageRawItems.map((row, index) => {
|
|
8634
8705
|
const originalIndex = page.offset + index;
|
|
8635
|
-
const row = this.toOutputRow(item as Record<string, unknown>);
|
|
8636
8706
|
const rowKey = seedRowIdentity(row, originalIndex);
|
|
8637
8707
|
const persisted =
|
|
8638
8708
|
pendingRowsByKey.get(rowKey) ?? completedRowsByKey.get(rowKey);
|
|
@@ -8661,7 +8731,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
8661
8731
|
{ row: Record<string, unknown>; originalIndex: number }
|
|
8662
8732
|
>();
|
|
8663
8733
|
for (const { row, index } of admittedSeededItems) {
|
|
8664
|
-
if (explicitKeyResolver) {
|
|
8734
|
+
if (explicitKeyResolver && !useSourceRowIdentity) {
|
|
8665
8735
|
const explicitKey = explicitKeyResolver(row, index);
|
|
8666
8736
|
if (seenExplicitKeys.has(explicitKey)) {
|
|
8667
8737
|
duplicateReuseCount += 1;
|
|
@@ -8990,12 +9060,14 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
8990
9060
|
);
|
|
8991
9061
|
successfulCount = visibleResults.completedRows;
|
|
8992
9062
|
failedCount = visibleResults.failedRows;
|
|
8993
|
-
this.#options.onMapRowsSettled?.({
|
|
9063
|
+
await this.#options.onMapRowsSettled?.({
|
|
8994
9064
|
playName: this.#options.playName,
|
|
8995
9065
|
runId: this.#options.runId,
|
|
8996
9066
|
tableNamespace: resolvedTableNamespace,
|
|
8997
9067
|
succeededRows: successfulCount,
|
|
8998
9068
|
failedRows: failedCount,
|
|
9069
|
+
expectedRows: totalInputCount - duplicateReuseCount,
|
|
9070
|
+
staticPipeline: this.currentStaticPipeline,
|
|
8999
9071
|
});
|
|
9000
9072
|
|
|
9001
9073
|
// Isolated failures are a partial result only when at least one admitted
|
|
@@ -9181,6 +9253,13 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9181
9253
|
skipped: duplicateReuseCount,
|
|
9182
9254
|
pending: 0,
|
|
9183
9255
|
failed: failedCount,
|
|
9256
|
+
cardinality: {
|
|
9257
|
+
sourceRows: sourceRowsCount,
|
|
9258
|
+
uniqueInputRows: processedCount - duplicateReuseCount,
|
|
9259
|
+
duplicateOccurrences: duplicateReuseCount,
|
|
9260
|
+
outputRows: successfulCount,
|
|
9261
|
+
rowIdentity: rowIdentityMode,
|
|
9262
|
+
},
|
|
9184
9263
|
...(duplicateReuseCount > 0
|
|
9185
9264
|
? { duplicates: { exact: duplicateReuseCount } }
|
|
9186
9265
|
: {}),
|
|
@@ -9228,24 +9307,66 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9228
9307
|
},
|
|
9229
9308
|
});
|
|
9230
9309
|
}
|
|
9231
|
-
const rawMaterializedEntries
|
|
9310
|
+
const rawMaterializedEntries: {
|
|
9311
|
+
item: T;
|
|
9312
|
+
inputIndex: number;
|
|
9313
|
+
sourceIdentity?: ReturnType<typeof getPlayDatasetSourceRowIdentityAt>;
|
|
9314
|
+
}[] =
|
|
9232
9315
|
resumedPendingRowsOnly && suspendedInputRows
|
|
9233
|
-
? suspendedInputRows.map((row) =>
|
|
9234
|
-
|
|
9235
|
-
|
|
9236
|
-
|
|
9237
|
-
|
|
9238
|
-
|
|
9316
|
+
? suspendedInputRows.map((row) => {
|
|
9317
|
+
const retainedKey = resolveMapRowOutcomeKey(row.data);
|
|
9318
|
+
return {
|
|
9319
|
+
// Older checkpoints contain only execution data. New checkpoints
|
|
9320
|
+
// also retain the original admission input before sheet outputs
|
|
9321
|
+
// were restored; keep the already-admitted key across either path.
|
|
9322
|
+
item: cloneCsvAliasedRow(
|
|
9323
|
+
row.authoredInput?.data ?? row.data,
|
|
9324
|
+
retainedKey
|
|
9325
|
+
? mapRowOutcomeRuntimeFields({ key: retainedKey })
|
|
9326
|
+
: {},
|
|
9327
|
+
) as T,
|
|
9328
|
+
inputIndex: row.inputIndex,
|
|
9329
|
+
sourceIdentity: row.authoredInput?.source,
|
|
9330
|
+
};
|
|
9331
|
+
})
|
|
9332
|
+
: await (async () => {
|
|
9333
|
+
const materialized = await materializePlayDatasetInput(items, {
|
|
9239
9334
|
onRow: (row) => {
|
|
9240
9335
|
materializationMemoryTracker.track(row);
|
|
9241
9336
|
},
|
|
9242
|
-
})
|
|
9243
|
-
|
|
9337
|
+
});
|
|
9338
|
+
return materialized.map((item, inputIndex) => ({
|
|
9339
|
+
item,
|
|
9340
|
+
inputIndex,
|
|
9341
|
+
sourceIdentity: getPlayDatasetSourceRowIdentityAt(
|
|
9342
|
+
materialized,
|
|
9343
|
+
inputIndex,
|
|
9344
|
+
),
|
|
9345
|
+
}));
|
|
9346
|
+
})();
|
|
9244
9347
|
const rawMaterializedItems = rawMaterializedEntries.map(
|
|
9245
|
-
|
|
9348
|
+
// Occurrence identity also applies to an explicitly selected array whose
|
|
9349
|
+
// author reused the same object reference. Give each occurrence its own
|
|
9350
|
+
// execution object so the original-index lookup cannot alias the last row.
|
|
9351
|
+
(entry) => {
|
|
9352
|
+
const sourceIdentity =
|
|
9353
|
+
'sourceIdentity' in entry
|
|
9354
|
+
? entry.sourceIdentity
|
|
9355
|
+
: getPlayDatasetSourceRowIdentity(entry.item);
|
|
9356
|
+
if (!useSourceRowIdentity && !sourceIdentity) return entry.item;
|
|
9357
|
+
const row = this.toOutputRow(entry.item);
|
|
9358
|
+
return (
|
|
9359
|
+
sourceIdentity
|
|
9360
|
+
? setPlayDatasetSourceRowIdentity(row, sourceIdentity)
|
|
9361
|
+
: row
|
|
9362
|
+
) as T;
|
|
9363
|
+
},
|
|
9246
9364
|
);
|
|
9247
9365
|
const originalInputIndexByItem = new Map<T, number>(
|
|
9248
|
-
|
|
9366
|
+
rawMaterializedItems.map((item, index) => [
|
|
9367
|
+
item,
|
|
9368
|
+
rawMaterializedEntries[index]!.inputIndex,
|
|
9369
|
+
]),
|
|
9249
9370
|
);
|
|
9250
9371
|
// Silent dedupe of duplicate explicit map keys: keep the first row per
|
|
9251
9372
|
// canonical key and drop subsequent duplicates. Deduping the materialized
|
|
@@ -9253,24 +9374,75 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9253
9374
|
// counts, sheet writes, resumable keys) consistent — the resolver derives
|
|
9254
9375
|
// keys from row content, so dropped rows are excluded everywhere with no
|
|
9255
9376
|
// index mismatch. Replaces the prior throw-on-duplicate behavior.
|
|
9256
|
-
const dedupedMaterialized =
|
|
9257
|
-
|
|
9258
|
-
|
|
9259
|
-
|
|
9260
|
-
|
|
9261
|
-
|
|
9262
|
-
|
|
9263
|
-
|
|
9264
|
-
|
|
9265
|
-
|
|
9377
|
+
const dedupedMaterialized =
|
|
9378
|
+
explicitKeyResolver && !useSourceRowIdentity
|
|
9379
|
+
? dedupeExplicitMapKeyRows({
|
|
9380
|
+
rows: rawMaterializedItems,
|
|
9381
|
+
resolver: (item, index) =>
|
|
9382
|
+
explicitKeyResolver(
|
|
9383
|
+
this.toOutputRow(item as Record<string, unknown>),
|
|
9384
|
+
originalInputIndexByItem.get(item) ?? index,
|
|
9385
|
+
),
|
|
9386
|
+
})
|
|
9387
|
+
: { rows: rawMaterializedItems, droppedCount: 0, duplicateKeys: [] };
|
|
9266
9388
|
if (dedupedMaterialized.droppedCount > 0) {
|
|
9267
|
-
const keySample = dedupedMaterialized.duplicateKeys.join(', ');
|
|
9268
9389
|
this.runtimeLog(
|
|
9269
|
-
`
|
|
9270
|
-
|
|
9390
|
+
`[runtime.dataset_input_cardinality] ${JSON.stringify({
|
|
9391
|
+
dataset: normalizedMapNamespace,
|
|
9392
|
+
rowIdentity: rowIdentityMode,
|
|
9393
|
+
sourceRows: rawMaterializedEntries.length,
|
|
9394
|
+
uniqueInputRows: dedupedMaterialized.rows.length,
|
|
9395
|
+
duplicateOccurrences: dedupedMaterialized.droppedCount,
|
|
9396
|
+
duplicatePolicy: 'first_wins',
|
|
9397
|
+
})}`,
|
|
9271
9398
|
);
|
|
9272
9399
|
}
|
|
9273
9400
|
const materializedItems = dedupedMaterialized.rows;
|
|
9401
|
+
// A legacy checkpoint does not contain its pre-dedupe source cardinality.
|
|
9402
|
+
// Leave the optional summary absent rather than report its pending subset
|
|
9403
|
+
// as the original source. New checkpoints retain only fixed-size counts.
|
|
9404
|
+
const inputCardinality = resumedPendingRowsOnly
|
|
9405
|
+
? replayMapFrame?.inputCardinality
|
|
9406
|
+
: {
|
|
9407
|
+
sourceRows: rawMaterializedEntries.length,
|
|
9408
|
+
uniqueInputRows: materializedItems.length,
|
|
9409
|
+
duplicateOccurrences: dedupedMaterialized.droppedCount,
|
|
9410
|
+
rowIdentity: rowIdentityMode,
|
|
9411
|
+
};
|
|
9412
|
+
// Admission provenance is captured before onMapStart restores output
|
|
9413
|
+
// cells. The execution row stays intact; only pending inputs enter a
|
|
9414
|
+
// checkpoint, and their sheet outputs are restored by the existing owner
|
|
9415
|
+
// after the next admission. Never infer original input from enriched data.
|
|
9416
|
+
const retainedAuthoredInputsByIndex = new Map(
|
|
9417
|
+
(suspendedInputRows ?? []).flatMap((row) =>
|
|
9418
|
+
row.authoredInput ? [[row.inputIndex, row.authoredInput] as const] : [],
|
|
9419
|
+
),
|
|
9420
|
+
);
|
|
9421
|
+
const authoredInputsByIndex = new Map(
|
|
9422
|
+
canReadPersistedMapResults
|
|
9423
|
+
? materializedItems.flatMap((item, index) => {
|
|
9424
|
+
const inputIndex = originalInputIndexByItem.get(item) ?? index;
|
|
9425
|
+
if (resumedPendingRowsOnly) {
|
|
9426
|
+
const retained = retainedAuthoredInputsByIndex.get(inputIndex);
|
|
9427
|
+
// An older pending checkpoint has execution data only. Keep its
|
|
9428
|
+
// optional provenance absent rather than relabel enriched cells.
|
|
9429
|
+
return retained ? [[inputIndex, retained] as const] : [];
|
|
9430
|
+
}
|
|
9431
|
+
const row = this.toOutputRow(item as Record<string, unknown>);
|
|
9432
|
+
const source = getPlayDatasetSourceRowIdentity(row);
|
|
9433
|
+
const data = sanitizePostgresJsonValue(
|
|
9434
|
+
toSerializableCsvAliasedRow(row),
|
|
9435
|
+
);
|
|
9436
|
+
materializationMemoryTracker.track(data);
|
|
9437
|
+
return [
|
|
9438
|
+
[
|
|
9439
|
+
inputIndex,
|
|
9440
|
+
{ data, ...(source ? { source: { ...source } } : {}) },
|
|
9441
|
+
] as const,
|
|
9442
|
+
];
|
|
9443
|
+
})
|
|
9444
|
+
: [],
|
|
9445
|
+
);
|
|
9274
9446
|
const priorAuthoredDigestsByIndex = new Map(
|
|
9275
9447
|
(suspendedInputRows ?? []).flatMap((row) =>
|
|
9276
9448
|
row.authoredInputDigest
|
|
@@ -9405,6 +9577,9 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9405
9577
|
const inputIndex = itemOriginalIndexes[index] ?? index;
|
|
9406
9578
|
return {
|
|
9407
9579
|
...toSerializableCsvAliasedRow(row),
|
|
9580
|
+
...(getPlayDatasetSourceRowIdentity(row)
|
|
9581
|
+
? { __deeplineSourceRow: getPlayDatasetSourceRowIdentity(row) }
|
|
9582
|
+
: {}),
|
|
9408
9583
|
...mapRowOutcomeRuntimeFields({
|
|
9409
9584
|
key: seedRowIdentity(row, inputIndex),
|
|
9410
9585
|
inputIndex,
|
|
@@ -9513,6 +9688,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9513
9688
|
artifactTableNamespace: mapScope.artifactTableNamespace,
|
|
9514
9689
|
status: 'running',
|
|
9515
9690
|
totalRows: totalInputCount,
|
|
9691
|
+
inputCardinality,
|
|
9516
9692
|
completedRowKeys,
|
|
9517
9693
|
pendingRowKeys,
|
|
9518
9694
|
completedRowsCount: previouslyCompletedRowsCount,
|
|
@@ -9663,13 +9839,17 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9663
9839
|
executionRowIndexes: rowsToExecuteEntries.map(
|
|
9664
9840
|
(entry) => entry.originalIndex,
|
|
9665
9841
|
),
|
|
9666
|
-
...(
|
|
9842
|
+
...(canReadPersistedMapResults
|
|
9667
9843
|
? {
|
|
9844
|
+
authoredInputs: rowsToExecuteEntries.map((entry) =>
|
|
9845
|
+
authoredInputsByIndex.get(entry.originalIndex),
|
|
9846
|
+
),
|
|
9847
|
+
}
|
|
9848
|
+
: {
|
|
9668
9849
|
authoredInputDigests: rowsToExecuteEntries.map((entry) =>
|
|
9669
9850
|
authoredInputDigestsByIndex.get(entry.originalIndex),
|
|
9670
9851
|
),
|
|
9671
|
-
}
|
|
9672
|
-
: {}),
|
|
9852
|
+
}),
|
|
9673
9853
|
retainedRowsMemoryBudgetBytes:
|
|
9674
9854
|
mapMemoryLimits.materializedBudgetBytes,
|
|
9675
9855
|
progressCompletedOffset:
|
|
@@ -9854,13 +10034,15 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9854
10034
|
});
|
|
9855
10035
|
}
|
|
9856
10036
|
|
|
9857
|
-
this.#options.onMapRowsSettled?.({
|
|
10037
|
+
await this.#options.onMapRowsSettled?.({
|
|
9858
10038
|
playName: this.#options.playName,
|
|
9859
10039
|
runId: this.#options.runId,
|
|
9860
10040
|
tableNamespace: resolvedTableNamespace,
|
|
9861
10041
|
succeededRows:
|
|
9862
10042
|
previouslyCompletedRowKeys.length + visibleCompletedRows.length,
|
|
9863
10043
|
failedRows: durableFailedRows,
|
|
10044
|
+
expectedRows: totalInputCount - duplicateReuseCount,
|
|
10045
|
+
staticPipeline: this.currentStaticPipeline,
|
|
9864
10046
|
});
|
|
9865
10047
|
|
|
9866
10048
|
// This map's terminal rows have already crossed the persistence barrier
|
|
@@ -9950,8 +10132,24 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9950
10132
|
skipped: reusedCount,
|
|
9951
10133
|
pending: 0,
|
|
9952
10134
|
failed: durableFailedRows,
|
|
9953
|
-
...(
|
|
9954
|
-
? {
|
|
10135
|
+
...(inputCardinality
|
|
10136
|
+
? {
|
|
10137
|
+
cardinality: {
|
|
10138
|
+
...inputCardinality,
|
|
10139
|
+
uniqueInputRows:
|
|
10140
|
+
inputCardinality.uniqueInputRows - duplicateReuseCount,
|
|
10141
|
+
duplicateOccurrences:
|
|
10142
|
+
inputCardinality.duplicateOccurrences + duplicateReuseCount,
|
|
10143
|
+
outputRows: results.length,
|
|
10144
|
+
},
|
|
10145
|
+
}
|
|
10146
|
+
: {}),
|
|
10147
|
+
...(dedupedMaterialized.droppedCount + duplicateReuseCount > 0
|
|
10148
|
+
? {
|
|
10149
|
+
duplicates: {
|
|
10150
|
+
exact: dedupedMaterialized.droppedCount + duplicateReuseCount,
|
|
10151
|
+
},
|
|
10152
|
+
}
|
|
9955
10153
|
: {}),
|
|
9956
10154
|
},
|
|
9957
10155
|
resolvers: {
|
|
@@ -9989,6 +10187,11 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9989
10187
|
onRowError?: 'isolate' | 'fail';
|
|
9990
10188
|
executionRowKeys?: string[];
|
|
9991
10189
|
executionRowIndexes?: number[];
|
|
10190
|
+
authoredInputs?: Array<
|
|
10191
|
+
NonNullable<
|
|
10192
|
+
MapExecutionFrame['suspendedInputRows']
|
|
10193
|
+
>[number]['authoredInput']
|
|
10194
|
+
>;
|
|
9992
10195
|
authoredInputDigests?: Array<string | undefined>;
|
|
9993
10196
|
/**
|
|
9994
10197
|
* Opt-in cap on how many row resolvers run their body concurrently. When
|
|
@@ -10796,6 +10999,9 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
10796
10999
|
),
|
|
10797
11000
|
...mapRowOutcomeRuntimeFields({ key: rowKey }),
|
|
10798
11001
|
},
|
|
11002
|
+
...(runtimeOptions?.authoredInputs?.[index]
|
|
11003
|
+
? { authoredInput: runtimeOptions.authoredInputs[index] }
|
|
11004
|
+
: {}),
|
|
10799
11005
|
...(runtimeOptions?.authoredInputDigests?.[index]
|
|
10800
11006
|
? {
|
|
10801
11007
|
authoredInputDigest:
|
|
@@ -11204,6 +11410,9 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
11204
11410
|
key: keyForIndex(index),
|
|
11205
11411
|
}),
|
|
11206
11412
|
},
|
|
11413
|
+
...(runtimeOptions?.authoredInputs?.[index]
|
|
11414
|
+
? { authoredInput: runtimeOptions.authoredInputs[index] }
|
|
11415
|
+
: {}),
|
|
11207
11416
|
...(runtimeOptions?.authoredInputDigests?.[index]
|
|
11208
11417
|
? {
|
|
11209
11418
|
authoredInputDigest:
|
|
@@ -12530,7 +12739,10 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
12530
12739
|
|
|
12531
12740
|
private toOutputRow<T>(item: T): Record<string, unknown> {
|
|
12532
12741
|
if (item != null && typeof item === 'object' && !Array.isArray(item)) {
|
|
12533
|
-
return
|
|
12742
|
+
return copyPlayDatasetSourceRowIdentity(
|
|
12743
|
+
item,
|
|
12744
|
+
stripCsvProjectionMetadata(item as Record<string, unknown>),
|
|
12745
|
+
);
|
|
12534
12746
|
}
|
|
12535
12747
|
|
|
12536
12748
|
return { value: item };
|
|
@@ -17746,6 +17958,17 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
17746
17958
|
);
|
|
17747
17959
|
const shouldWaitAtExecutionBoundary =
|
|
17748
17960
|
shouldWaitForAsyncOperation && !shouldResolveAsyncOperationInRuntime;
|
|
17961
|
+
const asyncExecutionRecovery = shouldWaitAtExecutionBoundary
|
|
17962
|
+
? await this.#options.getToolAsyncExecutionRecovery?.(toolId)
|
|
17963
|
+
: null;
|
|
17964
|
+
if (
|
|
17965
|
+
asyncExecutionRecovery &&
|
|
17966
|
+
!isImmutableRuntimeExecutionRecoveryOrigin(asyncExecutionRecovery.origin)
|
|
17967
|
+
) {
|
|
17968
|
+
throw new Error(
|
|
17969
|
+
'Async execution recovery requires the serving app immutable origin. No provider request was sent.',
|
|
17970
|
+
);
|
|
17971
|
+
}
|
|
17749
17972
|
// An explicitly opted-in caller-managed launch gives the Play author the
|
|
17750
17973
|
// provider handle and leaves polling to the authored graph. Do not let a
|
|
17751
17974
|
// provider's legacy synchronous helper turn a wide start wave into
|
|
@@ -18106,6 +18329,70 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
18106
18329
|
// map cells, batched owners) supply the fuller scope.
|
|
18107
18330
|
const playNodeScope =
|
|
18108
18331
|
options?.playNodeScope ?? buildPlayNodeScope({ toolId });
|
|
18332
|
+
const asyncRecoveryInvocationKey = asyncExecutionRecovery
|
|
18333
|
+
? stableDigest(
|
|
18334
|
+
stableStringify({
|
|
18335
|
+
toolId,
|
|
18336
|
+
// A queued call/chunk supplies its exact durable transport
|
|
18337
|
+
// identity. A direct invocation owns a fresh call identity.
|
|
18338
|
+
callIdentity:
|
|
18339
|
+
options?.durableCallReceiptKey ??
|
|
18340
|
+
(options?.providerOperationForceRefreshIdentities?.length
|
|
18341
|
+
? options.providerOperationForceRefreshIdentities
|
|
18342
|
+
: (options?.providerIdempotencyKey ??
|
|
18343
|
+
executionRequestNonce)),
|
|
18344
|
+
payload: toolInputSnapshot,
|
|
18345
|
+
authScope: executionAuthScopeDigest,
|
|
18346
|
+
playNodeScope,
|
|
18347
|
+
forceRefreshIdentities:
|
|
18348
|
+
options?.providerOperationForceRefreshIdentities ?? [],
|
|
18349
|
+
}),
|
|
18350
|
+
)
|
|
18351
|
+
: null;
|
|
18352
|
+
const asyncRecoveryPolling = asyncExecutionRecovery
|
|
18353
|
+
? resolveAsyncOperationPolling({
|
|
18354
|
+
contract: asyncContract!,
|
|
18355
|
+
startInput: toolInputSnapshot,
|
|
18356
|
+
playRuntimeWait: true,
|
|
18357
|
+
})
|
|
18358
|
+
: null;
|
|
18359
|
+
const asyncRecoveryDeadlineAtMs = asyncRecoveryPolling
|
|
18360
|
+
? Date.now() + asyncRecoveryPolling.timeoutMs
|
|
18361
|
+
: null;
|
|
18362
|
+
let confirmedAsyncRecoveryPending = false;
|
|
18363
|
+
let asyncRecoveryWaitTimeout: ReturnType<
|
|
18364
|
+
typeof createToolHttpError
|
|
18365
|
+
> | null = null;
|
|
18366
|
+
const asyncRecoveryWaitTimeoutError = () =>
|
|
18367
|
+
(asyncRecoveryWaitTimeout ??= createToolHttpError(
|
|
18368
|
+
toolErrorSchemaVersion,
|
|
18369
|
+
formatAsyncOperationTimeout({
|
|
18370
|
+
startAction: asyncContract!.lifecycle.startAction,
|
|
18371
|
+
jobId: null,
|
|
18372
|
+
timeoutDescription: `${asyncRecoveryPolling!.timeoutMs}ms`,
|
|
18373
|
+
}) +
|
|
18374
|
+
' The execution remains recoverable under its original idempotency key; it will not be relaunched.',
|
|
18375
|
+
null,
|
|
18376
|
+
504,
|
|
18377
|
+
'repairable',
|
|
18378
|
+
{
|
|
18379
|
+
toolId,
|
|
18380
|
+
provider,
|
|
18381
|
+
operation: toolId,
|
|
18382
|
+
code: 'ASYNC_OPERATION_TIMEOUT',
|
|
18383
|
+
origin: 'provider',
|
|
18384
|
+
category: 'upstream',
|
|
18385
|
+
retryable: false,
|
|
18386
|
+
statusCode: 504,
|
|
18387
|
+
},
|
|
18388
|
+
));
|
|
18389
|
+
const asyncRecoveryKey = (attempt: number) =>
|
|
18390
|
+
asyncRecoveryInvocationKey
|
|
18391
|
+
? buildRuntimeExecutionRecoveryKey({
|
|
18392
|
+
parentRunId: this.currentRunId,
|
|
18393
|
+
invocationKey: `${asyncRecoveryInvocationKey}:attempt:${attempt}`,
|
|
18394
|
+
})
|
|
18395
|
+
: null;
|
|
18109
18396
|
// This is deliberately a claim about only the immediately preceding
|
|
18110
18397
|
// *durable* provider attempt. The execution gateway validates and
|
|
18111
18398
|
// attests it before a retried call can use its fresh charge
|
|
@@ -18361,6 +18648,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
18361
18648
|
transportAttempt < TOOL_EXECUTE_TRANSPORT_MAX_ATTEMPTS &&
|
|
18362
18649
|
(input.knownNotDispatched === true ||
|
|
18363
18650
|
usesDurableInvocationFence ||
|
|
18651
|
+
asyncExecutionRecovery ||
|
|
18364
18652
|
ambiguousTransportReplayAllowed ||
|
|
18365
18653
|
fixtureReplaySafe)
|
|
18366
18654
|
) {
|
|
@@ -18368,7 +18656,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
18368
18656
|
// attempt and any prior provider-429 proof belong to the logical
|
|
18369
18657
|
// invocation; only a definite retryable HTTP outcome advances it.
|
|
18370
18658
|
// Ordinary transport retries retain their physical attempt count.
|
|
18371
|
-
if (!usesDurableInvocationFence) {
|
|
18659
|
+
if (!usesDurableInvocationFence && !asyncExecutionRecovery) {
|
|
18372
18660
|
providerRateLimitRetryFromInvocationAttempt = null;
|
|
18373
18661
|
invocationAttempt += 1;
|
|
18374
18662
|
}
|
|
@@ -18454,6 +18742,12 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
18454
18742
|
|
|
18455
18743
|
while (true) {
|
|
18456
18744
|
toolAbortSignal?.throwIfAborted();
|
|
18745
|
+
if (
|
|
18746
|
+
confirmedAsyncRecoveryPending &&
|
|
18747
|
+
Date.now() >= asyncRecoveryDeadlineAtMs!
|
|
18748
|
+
) {
|
|
18749
|
+
throw asyncRecoveryWaitTimeoutError();
|
|
18750
|
+
}
|
|
18457
18751
|
if (
|
|
18458
18752
|
providerNoProgressExpired({
|
|
18459
18753
|
startedAtMsByScope:
|
|
@@ -18806,6 +19100,12 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
18806
19100
|
// A stale permit can take arbitrarily long to
|
|
18807
19101
|
// replace. Only fixture residence and the physical
|
|
18808
19102
|
// HTTP exchange spend the tool execution deadline.
|
|
19103
|
+
if (
|
|
19104
|
+
confirmedAsyncRecoveryPending &&
|
|
19105
|
+
Date.now() >= asyncRecoveryDeadlineAtMs!
|
|
19106
|
+
) {
|
|
19107
|
+
throw asyncRecoveryWaitTimeoutError();
|
|
19108
|
+
}
|
|
18809
19109
|
armProviderTimeout();
|
|
18810
19110
|
beginProviderExecution();
|
|
18811
19111
|
const fetchedAt = Date.now();
|
|
@@ -18865,6 +19165,12 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
18865
19165
|
}
|
|
18866
19166
|
: {}),
|
|
18867
19167
|
'x-deepline-request-id': deeplineRequestId,
|
|
19168
|
+
...(asyncRecoveryKey(invocationAttempt)
|
|
19169
|
+
? {
|
|
19170
|
+
'idempotency-key':
|
|
19171
|
+
asyncRecoveryKey(invocationAttempt)!,
|
|
19172
|
+
}
|
|
19173
|
+
: {}),
|
|
18868
19174
|
...(providerExecutionFenceKey
|
|
18869
19175
|
? {
|
|
18870
19176
|
'x-deepline-idempotency-key':
|
|
@@ -19247,6 +19553,12 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
19247
19553
|
receiptHeartbeatSupervisor?.stop();
|
|
19248
19554
|
}
|
|
19249
19555
|
} catch (error) {
|
|
19556
|
+
if (
|
|
19557
|
+
asyncRecoveryWaitTimeout !== null &&
|
|
19558
|
+
error === asyncRecoveryWaitTimeout
|
|
19559
|
+
) {
|
|
19560
|
+
throw error;
|
|
19561
|
+
}
|
|
19250
19562
|
if (heartbeatFailure) {
|
|
19251
19563
|
throw heartbeatFailure;
|
|
19252
19564
|
}
|
|
@@ -19366,6 +19678,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
19366
19678
|
const transportReplaySafe =
|
|
19367
19679
|
!ambiguousDispatchedFailure ||
|
|
19368
19680
|
usesDurableInvocationFence ||
|
|
19681
|
+
Boolean(asyncExecutionRecovery) ||
|
|
19369
19682
|
ambiguousTransportReplayAllowed ||
|
|
19370
19683
|
fixtureReplaySafe;
|
|
19371
19684
|
if (!transportReplaySafe) {
|
|
@@ -19444,8 +19757,62 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
19444
19757
|
continue;
|
|
19445
19758
|
}
|
|
19446
19759
|
|
|
19760
|
+
if (
|
|
19761
|
+
asyncExecutionRecovery &&
|
|
19762
|
+
response.ok &&
|
|
19763
|
+
response.headers.get(
|
|
19764
|
+
RUNTIME_EXECUTION_RECOVERY_SUPPORTED_HEADER,
|
|
19765
|
+
) !== 'true'
|
|
19766
|
+
) {
|
|
19767
|
+
throw createToolHttpError(
|
|
19768
|
+
toolErrorSchemaVersion,
|
|
19769
|
+
`Tool ${toolId} did not confirm the declared async recovery reader. The execution will not be relaunched.`,
|
|
19770
|
+
null,
|
|
19771
|
+
response.status,
|
|
19772
|
+
'repairable',
|
|
19773
|
+
);
|
|
19774
|
+
}
|
|
19775
|
+
|
|
19447
19776
|
if (!response.ok) {
|
|
19448
19777
|
const text = responseErrorText ?? '';
|
|
19778
|
+
const recoveryKey = asyncRecoveryKey(invocationAttempt);
|
|
19779
|
+
if (
|
|
19780
|
+
recoveryKey &&
|
|
19781
|
+
response.status === 409 &&
|
|
19782
|
+
response.headers.get(
|
|
19783
|
+
RUNTIME_EXECUTION_RECOVERY_SUPPORTED_HEADER,
|
|
19784
|
+
) === 'true'
|
|
19785
|
+
) {
|
|
19786
|
+
const pending = (() => {
|
|
19787
|
+
try {
|
|
19788
|
+
return JSON.parse(text) as Record<string, unknown>;
|
|
19789
|
+
} catch {
|
|
19790
|
+
return null;
|
|
19791
|
+
}
|
|
19792
|
+
})();
|
|
19793
|
+
const recovery = pending?.executionRecovery as
|
|
19794
|
+
| Record<string, unknown>
|
|
19795
|
+
| undefined;
|
|
19796
|
+
if (
|
|
19797
|
+
pending?.code === 'EXECUTION_IN_PROGRESS' &&
|
|
19798
|
+
pending.retryable === true &&
|
|
19799
|
+
recovery?.idempotencyKey === recoveryKey &&
|
|
19800
|
+
(recovery.state === 'running' ||
|
|
19801
|
+
recovery.state === 'completed')
|
|
19802
|
+
) {
|
|
19803
|
+
confirmedAsyncRecoveryPending = true;
|
|
19804
|
+
if (Date.now() >= asyncRecoveryDeadlineAtMs!) {
|
|
19805
|
+
throw asyncRecoveryWaitTimeoutError();
|
|
19806
|
+
}
|
|
19807
|
+
// This is continuation of already accepted work. Keep the
|
|
19808
|
+
// original body/key and let the route attach its saved job.
|
|
19809
|
+
await waitForToolRetry(
|
|
19810
|
+
Math.min(1_000, asyncRecoveryDeadlineAtMs! - Date.now()),
|
|
19811
|
+
false,
|
|
19812
|
+
);
|
|
19813
|
+
continue;
|
|
19814
|
+
}
|
|
19815
|
+
}
|
|
19449
19816
|
const developerTunnelOrigin502 =
|
|
19450
19817
|
isDeeplineDeveloperTunnelOrigin502({
|
|
19451
19818
|
url,
|
|
@@ -19487,7 +19854,9 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
19487
19854
|
status: response.status,
|
|
19488
19855
|
bodyText: text,
|
|
19489
19856
|
transientHttpRetrySafe:
|
|
19490
|
-
retrySafeTransientHttp ||
|
|
19857
|
+
retrySafeTransientHttp ||
|
|
19858
|
+
fixtureReplaySafe ||
|
|
19859
|
+
Boolean(asyncExecutionRecovery),
|
|
19491
19860
|
executionFenced: usesDurableInvocationFence,
|
|
19492
19861
|
});
|
|
19493
19862
|
const failure =
|
|
@@ -19501,7 +19870,9 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
19501
19870
|
retryAfterHeader: response.headers.get('retry-after'),
|
|
19502
19871
|
retryAtMs: parseToolExecuteExactProviderRetryAtMs(response),
|
|
19503
19872
|
transientHttpRetrySafe:
|
|
19504
|
-
retrySafeTransientHttp ||
|
|
19873
|
+
retrySafeTransientHttp ||
|
|
19874
|
+
fixtureReplaySafe ||
|
|
19875
|
+
Boolean(asyncExecutionRecovery),
|
|
19505
19876
|
executionFenced: usesDurableInvocationFence,
|
|
19506
19877
|
...(providerCallElapsedMs !== null
|
|
19507
19878
|
? { providerLatencyMs: providerCallElapsedMs }
|
|
@@ -19643,6 +20014,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
19643
20014
|
(failure.knownNotDispatched ||
|
|
19644
20015
|
failure.reason === 'gateway_invocation_in_progress' ||
|
|
19645
20016
|
ambiguousTransportReplayAllowed ||
|
|
20017
|
+
Boolean(asyncExecutionRecovery) ||
|
|
19646
20018
|
retrySafeTransientHttp ||
|
|
19647
20019
|
fixtureReplaySafe ||
|
|
19648
20020
|
// An explicit provider-owned 429 is a rejected operation,
|
|
@@ -19659,7 +20031,9 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
19659
20031
|
// prior-attempt sweep see an intentionally absent receipt.
|
|
19660
20032
|
if (
|
|
19661
20033
|
failure.reason !== 'gateway_invocation_in_progress' &&
|
|
19662
|
-
!shedKnownNotDispatched
|
|
20034
|
+
!shedKnownNotDispatched &&
|
|
20035
|
+
(!asyncExecutionRecovery ||
|
|
20036
|
+
failure.backpressureDelayMs !== null)
|
|
19663
20037
|
) {
|
|
19664
20038
|
const previousInvocationAttempt = invocationAttempt;
|
|
19665
20039
|
if (failure.backpressureDelayMs !== null) {
|