deepline 0.3.141 → 0.3.143
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/release.ts +1 -1
- package/dist/bundling-sources/sdk/src/types.ts +65 -2
- package/dist/bundling-sources/shared_libs/observability/dlq.ts +25 -0
- package/dist/bundling-sources/shared_libs/observability/queue-health.ts +111 -0
- package/dist/bundling-sources/shared_libs/play-runtime/bettercontact-batching.ts +34 -8
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +271 -44
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +25 -6
- package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +132 -8
- package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-batching-registry.ts +3 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +49 -54
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +14 -7
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-state.ts +118 -29
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-queue-health.ts +183 -36
- package/dist/bundling-sources/shared_libs/product-notifications/events.ts +4 -1
- package/dist/cli/index.js +242 -12
- package/dist/cli/index.mjs +242 -12
- package/dist/index.d.mts +54 -2
- package/dist/index.d.ts +54 -2
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/release.d.mts +1 -1
- package/dist/release.d.ts +1 -1
- package/dist/release.js +1 -1
- package/dist/release.mjs +1 -1
- package/package.json +1 -1
|
@@ -2712,6 +2712,8 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
2712
2712
|
private toolDispatchGroupRoundTripEwmaMs: number | null = null;
|
|
2713
2713
|
private readonly toolDispatcherWakeWaiters = new Set<() => void>();
|
|
2714
2714
|
private toolDispatcherFailure: unknown | null = null;
|
|
2715
|
+
/** Active provider HTTP attempts in this Play execution, grouped by provider. */
|
|
2716
|
+
private readonly inFlightProviderRequests = new Map<string, number>();
|
|
2715
2717
|
/**
|
|
2716
2718
|
* Deepline's own zero-credit denial is run-fatal. Keep it independently of
|
|
2717
2719
|
* customer control flow so a broad `try/catch` cannot turn it into a
|
|
@@ -8457,6 +8459,52 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
8457
8459
|
: derivePlayRowIdentity(stableInputRow(row), resolvedTableNamespace);
|
|
8458
8460
|
};
|
|
8459
8461
|
|
|
8462
|
+
// A terminal write can be durably accepted into this run's history while
|
|
8463
|
+
// losing the mutable latest-view race to newer intent. That outcome must
|
|
8464
|
+
// not leak into the dataset returned by this execution. A later same-run
|
|
8465
|
+
// reconciliation can explicitly recover a key; only that positive fact
|
|
8466
|
+
// makes it visible again.
|
|
8467
|
+
const staleCompletionKeys = new Set<string>();
|
|
8468
|
+
const staleCompletedKeys = new Set<string>();
|
|
8469
|
+
const staleFailedKeys = new Set<string>();
|
|
8470
|
+
const recoveredStaleCompletionKeys = new Set<string>();
|
|
8471
|
+
const observeMapWriteResult = (
|
|
8472
|
+
writeResult: unknown,
|
|
8473
|
+
writtenRows: readonly PersistableMapRow[] = [],
|
|
8474
|
+
): void => {
|
|
8475
|
+
if (!writeResult || typeof writeResult !== 'object') return;
|
|
8476
|
+
const result = writeResult as {
|
|
8477
|
+
staleDroppedKeys?: unknown;
|
|
8478
|
+
recoveredStaleKeys?: unknown;
|
|
8479
|
+
};
|
|
8480
|
+
const rowsByKey = new Map(writtenRows.map((row) => [row.key, row]));
|
|
8481
|
+
if (Array.isArray(result.staleDroppedKeys)) {
|
|
8482
|
+
for (const key of result.staleDroppedKeys) {
|
|
8483
|
+
if (typeof key !== 'string' || !key) continue;
|
|
8484
|
+
staleCompletionKeys.add(key);
|
|
8485
|
+
if (rowsByKey.get(key)?.status === 'failed') {
|
|
8486
|
+
staleFailedKeys.add(key);
|
|
8487
|
+
staleCompletedKeys.delete(key);
|
|
8488
|
+
} else {
|
|
8489
|
+
staleCompletedKeys.add(key);
|
|
8490
|
+
staleFailedKeys.delete(key);
|
|
8491
|
+
}
|
|
8492
|
+
}
|
|
8493
|
+
}
|
|
8494
|
+
if (Array.isArray(result.recoveredStaleKeys)) {
|
|
8495
|
+
for (const key of result.recoveredStaleKeys) {
|
|
8496
|
+
if (typeof key !== 'string' || !key) continue;
|
|
8497
|
+
recoveredStaleCompletionKeys.add(key);
|
|
8498
|
+
staleCompletionKeys.delete(key);
|
|
8499
|
+
staleCompletedKeys.delete(key);
|
|
8500
|
+
staleFailedKeys.delete(key);
|
|
8501
|
+
}
|
|
8502
|
+
}
|
|
8503
|
+
};
|
|
8504
|
+
const isVisibleTerminalRow = (row: PersistableMapRow): boolean =>
|
|
8505
|
+
!staleCompletionKeys.has(row.key) ||
|
|
8506
|
+
recoveredStaleCompletionKeys.has(row.key);
|
|
8507
|
+
|
|
8460
8508
|
// Identity for SEED rows (pre-execution raw inputs), matching the server's
|
|
8461
8509
|
// own derivation in startRuntimeSheetDataset: hash the FULL cleaned input
|
|
8462
8510
|
// row, output-named columns INCLUDED. Two distinct input rows that differ
|
|
@@ -8604,6 +8652,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
8604
8652
|
),
|
|
8605
8653
|
staticPipeline: this.currentStaticPipeline ?? null,
|
|
8606
8654
|
});
|
|
8655
|
+
observeMapWriteResult(writeResult, chunk);
|
|
8607
8656
|
const supersededKeys =
|
|
8608
8657
|
typeof writeResult === 'object' && writeResult !== null
|
|
8609
8658
|
? (writeResult.supersededKeys ?? [])
|
|
@@ -8831,9 +8880,12 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
8831
8880
|
totalRows: totalInputCount,
|
|
8832
8881
|
completedRowKeys: [],
|
|
8833
8882
|
pendingRowKeys: execution.rowFeed ? [] : [...execution.rowKeys],
|
|
8834
|
-
completedRowsCount: Math.max(
|
|
8883
|
+
completedRowsCount: Math.max(
|
|
8884
|
+
0,
|
|
8885
|
+
processedCount - failedCount - staleCompletedKeys.size,
|
|
8886
|
+
),
|
|
8835
8887
|
pendingRowsCount: Math.max(0, totalInputCount - processedCount),
|
|
8836
|
-
failedRowsCount: failedCount,
|
|
8888
|
+
failedRowsCount: Math.max(0, failedCount - staleFailedKeys.size),
|
|
8837
8889
|
startedAt:
|
|
8838
8890
|
this.checkpoint.mapFrames?.[mapScope.mapInvocationId]?.startedAt ??
|
|
8839
8891
|
Date.now(),
|
|
@@ -8857,8 +8909,12 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
8857
8909
|
executionRowKeys: execution.rowKeys,
|
|
8858
8910
|
executionRowIndexes: execution.rowIndexes,
|
|
8859
8911
|
progressCompletedOffset: () =>
|
|
8860
|
-
Math.max(
|
|
8861
|
-
|
|
8912
|
+
Math.max(
|
|
8913
|
+
0,
|
|
8914
|
+
processedCount - failedCount - staleCompletedKeys.size,
|
|
8915
|
+
),
|
|
8916
|
+
progressFailedOffset: () =>
|
|
8917
|
+
Math.max(0, failedCount - staleFailedKeys.size),
|
|
8862
8918
|
progressTotalRows: () => totalInputCount,
|
|
8863
8919
|
rowFeed: execution.rowFeed,
|
|
8864
8920
|
retainedRowsMemoryBudgetBytes:
|
|
@@ -8885,9 +8941,13 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
8885
8941
|
}
|
|
8886
8942
|
|
|
8887
8943
|
await incrementalPersistence.flush();
|
|
8944
|
+
const visibleCompletedRows =
|
|
8945
|
+
mapResult.completedRows.filter(isVisibleTerminalRow);
|
|
8946
|
+
const visibleFailedRows =
|
|
8947
|
+
mapResult.failedRows.filter(isVisibleTerminalRow);
|
|
8888
8948
|
const mapCellMeta = this.activeMapCellMeta;
|
|
8889
8949
|
const persistRows: PersistableMapRow[] = [];
|
|
8890
|
-
for (const row of
|
|
8950
|
+
for (const row of visibleCompletedRows) {
|
|
8891
8951
|
if (incrementalPersistence.isPersisted(row)) continue;
|
|
8892
8952
|
const meta = mapCellMeta?.get(row.key);
|
|
8893
8953
|
persistRows.push(
|
|
@@ -8903,7 +8963,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
8903
8963
|
);
|
|
8904
8964
|
}
|
|
8905
8965
|
persistRows.push(
|
|
8906
|
-
...
|
|
8966
|
+
...visibleFailedRows.filter(
|
|
8907
8967
|
(row) => !incrementalPersistence.isPersisted(row),
|
|
8908
8968
|
),
|
|
8909
8969
|
);
|
|
@@ -8919,7 +8979,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
8919
8979
|
'the current execution page exceeded the in-memory result budget',
|
|
8920
8980
|
);
|
|
8921
8981
|
}
|
|
8922
|
-
for (const row of
|
|
8982
|
+
for (const row of visibleCompletedRows) {
|
|
8923
8983
|
const materializedRow = this.toMaterializedOutputRow(row.data);
|
|
8924
8984
|
if (
|
|
8925
8985
|
immediateMaterializedCacheEnabled &&
|
|
@@ -9060,6 +9120,9 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9060
9120
|
}
|
|
9061
9121
|
}
|
|
9062
9122
|
|
|
9123
|
+
successfulCount = Math.max(0, successfulCount - staleCompletedKeys.size);
|
|
9124
|
+
failedCount = Math.max(0, failedCount - staleFailedKeys.size);
|
|
9125
|
+
|
|
9063
9126
|
// Isolated failures are a partial result only when at least one admitted
|
|
9064
9127
|
// row completed. A fully failed admission run is systemic: preserve the
|
|
9065
9128
|
// failed sheet rows for recovery, but fail the enclosing play loudly.
|
|
@@ -9110,7 +9173,13 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9110
9173
|
totalRows: totalInputCount,
|
|
9111
9174
|
completedRowKeys: [],
|
|
9112
9175
|
pendingRowKeys: [],
|
|
9113
|
-
completedRowsCount: Math.max(
|
|
9176
|
+
completedRowsCount: Math.max(
|
|
9177
|
+
0,
|
|
9178
|
+
processedCount -
|
|
9179
|
+
failedCount -
|
|
9180
|
+
staleFailedKeys.size -
|
|
9181
|
+
staleCompletedKeys.size,
|
|
9182
|
+
),
|
|
9114
9183
|
pendingRowsCount: 0,
|
|
9115
9184
|
failedRowsCount: failedCount,
|
|
9116
9185
|
startedAt:
|
|
@@ -9124,7 +9193,13 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9124
9193
|
mapNodeId: mapScope.mapNodeId ?? null,
|
|
9125
9194
|
logicalNamespace: mapScope.logicalNamespace,
|
|
9126
9195
|
artifactTableNamespace: resolvedTableNamespace,
|
|
9127
|
-
completedRows: Math.max(
|
|
9196
|
+
completedRows: Math.max(
|
|
9197
|
+
0,
|
|
9198
|
+
processedCount -
|
|
9199
|
+
failedCount -
|
|
9200
|
+
staleFailedKeys.size -
|
|
9201
|
+
staleCompletedKeys.size,
|
|
9202
|
+
),
|
|
9128
9203
|
failedRows: failedCount,
|
|
9129
9204
|
totalRows: totalInputCount,
|
|
9130
9205
|
at: Date.now(),
|
|
@@ -9500,7 +9575,6 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9500
9575
|
|
|
9501
9576
|
this.beginActiveMapCellMeta();
|
|
9502
9577
|
this.beginActiveMapCheckpointUpdates();
|
|
9503
|
-
const staleCompletionKeys = new Set<string>();
|
|
9504
9578
|
const persistMapRows = async (rows: PersistableMapRow[]) => {
|
|
9505
9579
|
if (!this.#options.onMapRowsCompleted || rows.length === 0) {
|
|
9506
9580
|
return;
|
|
@@ -9529,15 +9603,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9529
9603
|
),
|
|
9530
9604
|
staticPipeline: this.currentStaticPipeline ?? null,
|
|
9531
9605
|
});
|
|
9532
|
-
|
|
9533
|
-
const recoveredStaleKeys = new Set(
|
|
9534
|
-
writeResult.recoveredStaleKeys ?? [],
|
|
9535
|
-
);
|
|
9536
|
-
for (const key of writeResult.staleDroppedKeys ?? []) {
|
|
9537
|
-
if (recoveredStaleKeys.has(key)) continue;
|
|
9538
|
-
staleCompletionKeys.add(key);
|
|
9539
|
-
}
|
|
9540
|
-
}
|
|
9606
|
+
observeMapWriteResult(writeResult, chunk);
|
|
9541
9607
|
this.resourceGovernor.observe({
|
|
9542
9608
|
sheetFlushBytes: chunkBytes,
|
|
9543
9609
|
sheetFlushLatencyMs: Date.now() - flushStartedAt,
|
|
@@ -9623,23 +9689,31 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9623
9689
|
this.clearActiveMapCheckpointUpdates();
|
|
9624
9690
|
}
|
|
9625
9691
|
}
|
|
9692
|
+
const visibleCompletedRows =
|
|
9693
|
+
mapResult.completedRows.filter(isVisibleTerminalRow);
|
|
9694
|
+
const visibleFailedRows = mapResult.failedRows.filter(isVisibleTerminalRow);
|
|
9626
9695
|
const resultsByKey = new Map<string, Record<string, unknown>>();
|
|
9627
|
-
for (const row of
|
|
9696
|
+
for (const row of visibleCompletedRows) {
|
|
9628
9697
|
if (row.key) resultsByKey.set(row.key, row.data);
|
|
9629
9698
|
}
|
|
9630
9699
|
const failedRowKeys = new Set(
|
|
9631
|
-
|
|
9700
|
+
visibleFailedRows.map((row) => row.key).filter(Boolean),
|
|
9632
9701
|
);
|
|
9633
|
-
const directCompletedResults =
|
|
9702
|
+
const directCompletedResults = visibleCompletedRows.map((row) =>
|
|
9634
9703
|
this.toPublicOutputRow(row.data),
|
|
9635
9704
|
);
|
|
9636
9705
|
let results =
|
|
9637
|
-
|
|
9706
|
+
visibleFailedRows.length === 0 &&
|
|
9638
9707
|
directCompletedResults.length === rawItems.length
|
|
9639
9708
|
? directCompletedResults
|
|
9640
9709
|
: rawItems.flatMap((rawItem, index) => {
|
|
9641
9710
|
const rowKey = rowIdentity(rawItem, index);
|
|
9642
|
-
if (
|
|
9711
|
+
if (
|
|
9712
|
+
rowKey &&
|
|
9713
|
+
(failedRowKeys.has(rowKey) ||
|
|
9714
|
+
(staleCompletionKeys.has(rowKey) &&
|
|
9715
|
+
!recoveredStaleCompletionKeys.has(rowKey)))
|
|
9716
|
+
) {
|
|
9643
9717
|
return [];
|
|
9644
9718
|
}
|
|
9645
9719
|
return [
|
|
@@ -9652,11 +9726,11 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9652
9726
|
// Persist executed rows to the tenant runtime sheet — the sheet is the
|
|
9653
9727
|
// source of truth, not this in-memory results array. Chunked by rows AND
|
|
9654
9728
|
// bytes so large cells (scraped pages) never produce oversized writes.
|
|
9655
|
-
if (resultsByKey.size > 0 ||
|
|
9729
|
+
if (resultsByKey.size > 0 || visibleFailedRows.length > 0) {
|
|
9656
9730
|
await incrementalPersistence?.flush();
|
|
9657
9731
|
const mapCellMeta = this.activeMapCellMeta;
|
|
9658
9732
|
const persistRows: PersistableMapRow[] = [];
|
|
9659
|
-
for (const row of
|
|
9733
|
+
for (const row of visibleCompletedRows) {
|
|
9660
9734
|
if (incrementalPersistence?.isPersisted(row)) continue;
|
|
9661
9735
|
const rowKey = row.key;
|
|
9662
9736
|
const meta = mapCellMeta?.get(rowKey);
|
|
@@ -9672,7 +9746,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9672
9746
|
);
|
|
9673
9747
|
}
|
|
9674
9748
|
persistRows.push(
|
|
9675
|
-
...
|
|
9749
|
+
...visibleFailedRows.filter(
|
|
9676
9750
|
(row) => !incrementalPersistence?.isPersisted(row),
|
|
9677
9751
|
),
|
|
9678
9752
|
);
|
|
@@ -9687,18 +9761,11 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9687
9761
|
this.clearActiveMapCellMeta();
|
|
9688
9762
|
this.clearActiveMapCheckpointUpdates();
|
|
9689
9763
|
|
|
9690
|
-
if (staleCompletionKeys.size > 0) {
|
|
9691
|
-
results = results.filter((row, index) => {
|
|
9692
|
-
const key = rowIdentity(row, itemOriginalIndexes[index] ?? index);
|
|
9693
|
-
return !staleCompletionKeys.has(key);
|
|
9694
|
-
});
|
|
9695
|
-
}
|
|
9696
|
-
|
|
9697
9764
|
const durableCompletedRows = Math.max(
|
|
9698
9765
|
0,
|
|
9699
|
-
reusedCount +
|
|
9766
|
+
reusedCount + visibleCompletedRows.length,
|
|
9700
9767
|
);
|
|
9701
|
-
const durableFailedRows =
|
|
9768
|
+
const durableFailedRows = visibleFailedRows.length;
|
|
9702
9769
|
const terminalFrame = this.checkpoint.mapFrames?.[mapScope.mapInvocationId];
|
|
9703
9770
|
if (terminalFrame) {
|
|
9704
9771
|
this.setMapFrame({
|
|
@@ -9730,7 +9797,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9730
9797
|
at: Date.now(),
|
|
9731
9798
|
});
|
|
9732
9799
|
|
|
9733
|
-
if (this.#options.onMapRowsCompleted
|
|
9800
|
+
if (this.#options.onMapRowsCompleted) {
|
|
9734
9801
|
const finalizationPageReader = createRuntimeSheetDatasetPageReader({
|
|
9735
9802
|
context: {
|
|
9736
9803
|
baseUrl: this.#options.baseUrl!,
|
|
@@ -9748,9 +9815,9 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9748
9815
|
mapName: normalizedMapNamespace,
|
|
9749
9816
|
tableNamespace: resolvedTableNamespace,
|
|
9750
9817
|
runId: this.#options.runId,
|
|
9751
|
-
expectedRows: totalInputCount,
|
|
9818
|
+
expectedRows: Math.max(0, totalInputCount - staleCompletionKeys.size),
|
|
9752
9819
|
currentRows: results,
|
|
9753
|
-
failedRowCount:
|
|
9820
|
+
failedRowCount: visibleFailedRows.length,
|
|
9754
9821
|
log: (line) => this.runtimeLog(line),
|
|
9755
9822
|
readPersistedRows: async (readInput) => {
|
|
9756
9823
|
if (
|
|
@@ -9870,7 +9937,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9870
9937
|
reused: reusedCount,
|
|
9871
9938
|
skipped: reusedCount,
|
|
9872
9939
|
pending: 0,
|
|
9873
|
-
failed:
|
|
9940
|
+
failed: visibleFailedRows.length,
|
|
9874
9941
|
...(duplicateReuseCount > 0
|
|
9875
9942
|
? { duplicates: { exact: duplicateReuseCount } }
|
|
9876
9943
|
: {}),
|
|
@@ -12350,6 +12417,31 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
12350
12417
|
);
|
|
12351
12418
|
}
|
|
12352
12419
|
|
|
12420
|
+
private beginProviderRequest(provider: string): {
|
|
12421
|
+
inFlightCount: () => number;
|
|
12422
|
+
release: () => void;
|
|
12423
|
+
} {
|
|
12424
|
+
const providerKey = provider.trim().toLowerCase() || 'unknown';
|
|
12425
|
+
this.inFlightProviderRequests.set(
|
|
12426
|
+
providerKey,
|
|
12427
|
+
(this.inFlightProviderRequests.get(providerKey) ?? 0) + 1,
|
|
12428
|
+
);
|
|
12429
|
+
let released = false;
|
|
12430
|
+
return {
|
|
12431
|
+
inFlightCount: () => this.inFlightProviderRequests.get(providerKey) ?? 0,
|
|
12432
|
+
release: () => {
|
|
12433
|
+
if (released) return;
|
|
12434
|
+
released = true;
|
|
12435
|
+
const remaining = this.inFlightProviderRequests.get(providerKey) ?? 0;
|
|
12436
|
+
if (remaining <= 1) {
|
|
12437
|
+
this.inFlightProviderRequests.delete(providerKey);
|
|
12438
|
+
} else {
|
|
12439
|
+
this.inFlightProviderRequests.set(providerKey, remaining - 1);
|
|
12440
|
+
}
|
|
12441
|
+
},
|
|
12442
|
+
};
|
|
12443
|
+
}
|
|
12444
|
+
|
|
12353
12445
|
private async executeTool(
|
|
12354
12446
|
key: string,
|
|
12355
12447
|
toolId: string,
|
|
@@ -12408,6 +12500,9 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
12408
12500
|
(await this.resolveToolAuthScopeDigest(toolId))?.trim() ?? null;
|
|
12409
12501
|
const eventWaitHandler =
|
|
12410
12502
|
(await this.#options.getIntegrationEventWaitHandler?.(toolId)) ?? null;
|
|
12503
|
+
const explicitForce =
|
|
12504
|
+
options?.force === true ||
|
|
12505
|
+
toolExecutionOverrides.getStore()?.force === true;
|
|
12411
12506
|
const cacheableToolResult =
|
|
12412
12507
|
!eventWaitHandler &&
|
|
12413
12508
|
!isQueryResultDatasetReadRequest(toolId, input) &&
|
|
@@ -12462,6 +12557,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
12462
12557
|
toolId,
|
|
12463
12558
|
input,
|
|
12464
12559
|
eventWaitHandler,
|
|
12560
|
+
explicitForce,
|
|
12465
12561
|
);
|
|
12466
12562
|
}
|
|
12467
12563
|
if (toolId === 'run_javascript') {
|
|
@@ -13226,6 +13322,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
13226
13322
|
toolId: string,
|
|
13227
13323
|
input: Record<string, unknown>,
|
|
13228
13324
|
handler: IntegrationEventWaitHandler,
|
|
13325
|
+
force = false,
|
|
13229
13326
|
): Promise<unknown> {
|
|
13230
13327
|
this.assertInlineChildContract('suspending_child');
|
|
13231
13328
|
if (!this.#options.durableBoundaries) {
|
|
@@ -13295,6 +13392,12 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
13295
13392
|
...(existing.messageRef
|
|
13296
13393
|
? { messageRef: existing.messageRef }
|
|
13297
13394
|
: {}),
|
|
13395
|
+
...(existing.requestKey
|
|
13396
|
+
? { requestKey: existing.requestKey }
|
|
13397
|
+
: {}),
|
|
13398
|
+
...(existing.interactionId
|
|
13399
|
+
? { interactionId: existing.interactionId }
|
|
13400
|
+
: {}),
|
|
13298
13401
|
...(timeoutPolicy ? { timeoutPolicy } : {}),
|
|
13299
13402
|
},
|
|
13300
13403
|
});
|
|
@@ -13344,8 +13447,52 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
13344
13447
|
payload: input,
|
|
13345
13448
|
context: eventContext,
|
|
13346
13449
|
boundary: preparedBoundary,
|
|
13450
|
+
...(force ? { force: true } : {}),
|
|
13347
13451
|
});
|
|
13348
13452
|
|
|
13453
|
+
if (boundary.resolvedOutput) {
|
|
13454
|
+
this.checkpoint.resolvedBoundaries = {
|
|
13455
|
+
...(this.checkpoint.resolvedBoundaries ?? {}),
|
|
13456
|
+
[boundary.boundaryId]: {
|
|
13457
|
+
kind: 'integration_event',
|
|
13458
|
+
eventKey: boundary.eventKey,
|
|
13459
|
+
timeoutMs: boundary.timeoutMs,
|
|
13460
|
+
provider: boundary.provider,
|
|
13461
|
+
toolId: boundary.toolId,
|
|
13462
|
+
output: boundary.resolvedOutput,
|
|
13463
|
+
...(boundary.timeoutPolicy
|
|
13464
|
+
? { timeoutPolicy: boundary.timeoutPolicy }
|
|
13465
|
+
: {}),
|
|
13466
|
+
...(rowScope
|
|
13467
|
+
? {
|
|
13468
|
+
scope: {
|
|
13469
|
+
type: 'map_row' as const,
|
|
13470
|
+
tableNamespace: rowScope.tableNamespace,
|
|
13471
|
+
rowKey: rowScope.rowKey,
|
|
13472
|
+
rowIndex: rowScope.rowId,
|
|
13473
|
+
fieldName: rowScope.fieldName,
|
|
13474
|
+
},
|
|
13475
|
+
}
|
|
13476
|
+
: { scope: { type: 'workflow' as const } }),
|
|
13477
|
+
...(boundary.messageRef ? { messageRef: boundary.messageRef } : {}),
|
|
13478
|
+
...(boundary.requestKey ? { requestKey: boundary.requestKey } : {}),
|
|
13479
|
+
...(boundary.interactionId
|
|
13480
|
+
? { interactionId: boundary.interactionId }
|
|
13481
|
+
: {}),
|
|
13482
|
+
},
|
|
13483
|
+
};
|
|
13484
|
+
this.#options.onBatchComplete?.(this.checkpoint);
|
|
13485
|
+
return await this.wrapToolExecutionResult({
|
|
13486
|
+
toolId,
|
|
13487
|
+
status: 'completed',
|
|
13488
|
+
result: boundary.resolvedOutput,
|
|
13489
|
+
execution: toolExecutionMetadataForOutcome({
|
|
13490
|
+
kind: 'checkpoint',
|
|
13491
|
+
cacheKey: `integration_event:${boundary.boundaryId}`,
|
|
13492
|
+
}),
|
|
13493
|
+
});
|
|
13494
|
+
}
|
|
13495
|
+
|
|
13349
13496
|
this.runtimeLog(
|
|
13350
13497
|
`Armed ${handler.provider} integration event wait: ${boundary.eventKey}`,
|
|
13351
13498
|
);
|
|
@@ -13357,6 +13504,10 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
13357
13504
|
timeoutMs: boundary.timeoutMs,
|
|
13358
13505
|
provider: boundary.provider,
|
|
13359
13506
|
toolId: boundary.toolId,
|
|
13507
|
+
...(boundary.requestKey ? { requestKey: boundary.requestKey } : {}),
|
|
13508
|
+
...(boundary.interactionId
|
|
13509
|
+
? { interactionId: boundary.interactionId }
|
|
13510
|
+
: {}),
|
|
13360
13511
|
...(boundary.timeoutPolicy
|
|
13361
13512
|
? { timeoutPolicy: boundary.timeoutPolicy }
|
|
13362
13513
|
: {}),
|
|
@@ -13954,7 +14105,8 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
13954
14105
|
? `${rowStore.mapScope.mapInvocationId}:${rowStore.rowKey ?? rowStore.rowId}`
|
|
13955
14106
|
: `row:${rowStore.rowKey ?? rowStore.rowId}`;
|
|
13956
14107
|
const scopedIndexKey = `${this.currentExecutionScope.receipt.namespace}:${rowScope}`;
|
|
13957
|
-
const scopedIndex =
|
|
14108
|
+
const scopedIndex =
|
|
14109
|
+
this.sleepBoundaryIndexByScope.get(scopedIndexKey) ?? 0;
|
|
13958
14110
|
this.sleepBoundaryIndexByScope.set(scopedIndexKey, scopedIndex + 1);
|
|
13959
14111
|
const scopedBoundaryId = this.durableBoundaryId(
|
|
13960
14112
|
`sleep-${stableDigest(`${rowScope}:${scopedIndex}`)}-${delayMs}`,
|
|
@@ -14409,9 +14561,22 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
14409
14561
|
this.runtimeLog(
|
|
14410
14562
|
`ctx.step(${normalizedKey}): recovered result from checkpoint`,
|
|
14411
14563
|
);
|
|
14564
|
+
// The step callback is not evaluated on replay, so any sleeps that it
|
|
14565
|
+
// contained would otherwise disappear from the positional sleep
|
|
14566
|
+
// cursor. Preserve the authored boundary sequence before evaluating
|
|
14567
|
+
// the next sibling operation. This is deliberately run-local state;
|
|
14568
|
+
// it is not a cross-run sleep cache or an input-derived identity.
|
|
14569
|
+
if (
|
|
14570
|
+
Number.isSafeInteger(existing.sleepCount) &&
|
|
14571
|
+
existing.sleepCount !== undefined &&
|
|
14572
|
+
existing.sleepCount > 0
|
|
14573
|
+
) {
|
|
14574
|
+
this.sleepBoundaryIndex += existing.sleepCount;
|
|
14575
|
+
}
|
|
14412
14576
|
return existing.output as T;
|
|
14413
14577
|
}
|
|
14414
14578
|
|
|
14579
|
+
const sleepBoundaryIndexBefore = this.sleepBoundaryIndex;
|
|
14415
14580
|
const output = await run();
|
|
14416
14581
|
assertJsonSerializableStepOutput(normalizedKey, output);
|
|
14417
14582
|
// A caught durable suspension is still the invocation's outcome. Never
|
|
@@ -14424,6 +14589,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
14424
14589
|
kind: 'step',
|
|
14425
14590
|
stepId: normalizedKey,
|
|
14426
14591
|
output,
|
|
14592
|
+
sleepCount: this.sleepBoundaryIndex - sleepBoundaryIndexBefore,
|
|
14427
14593
|
completedAt: Date.now(),
|
|
14428
14594
|
},
|
|
14429
14595
|
};
|
|
@@ -18077,6 +18243,11 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
18077
18243
|
let providerCallStartedAt: number | null = null;
|
|
18078
18244
|
let providerCallElapsedMs: number | null = null;
|
|
18079
18245
|
let integrationFetchStartedAt: number | null = null;
|
|
18246
|
+
let providerRequestsInFlightAt429: number | null = null;
|
|
18247
|
+
let activeProviderRequest: {
|
|
18248
|
+
inFlightCount: () => number;
|
|
18249
|
+
release: () => void;
|
|
18250
|
+
} | null = null;
|
|
18080
18251
|
let fetchDispatched = false;
|
|
18081
18252
|
// A provider 429 must be reported while its server-minted
|
|
18082
18253
|
// admission lease is still live. Keep its parsed outcome here so
|
|
@@ -18342,6 +18513,8 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
18342
18513
|
providerCallStartedAt =
|
|
18343
18514
|
dispatchedProviderCallStartedAt;
|
|
18344
18515
|
fetchDispatched = true;
|
|
18516
|
+
activeProviderRequest =
|
|
18517
|
+
this.beginProviderRequest(provider);
|
|
18345
18518
|
const dispatchedResponse = await fetch(url, {
|
|
18346
18519
|
method: 'POST',
|
|
18347
18520
|
signal: abortController?.signal,
|
|
@@ -18386,6 +18559,10 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
18386
18559
|
// headers arrive; decoded body data stays scoped until
|
|
18387
18560
|
// it has been read and verified below.
|
|
18388
18561
|
response = dispatchedResponse;
|
|
18562
|
+
if (dispatchedResponse.status === 429) {
|
|
18563
|
+
providerRequestsInFlightAt429 =
|
|
18564
|
+
activeProviderRequest?.inFlightCount() ?? null;
|
|
18565
|
+
}
|
|
18389
18566
|
const dispatchedFetchToHeadersMs =
|
|
18390
18567
|
Date.now() - fetchedAt;
|
|
18391
18568
|
// The response writer owns these named wall times.
|
|
@@ -18533,6 +18710,8 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
18533
18710
|
}),
|
|
18534
18711
|
};
|
|
18535
18712
|
}
|
|
18713
|
+
activeProviderRequest?.release();
|
|
18714
|
+
activeProviderRequest = null;
|
|
18536
18715
|
return {
|
|
18537
18716
|
response: dispatchedResponse,
|
|
18538
18717
|
responseData: dispatchedResponseData,
|
|
@@ -18599,6 +18778,8 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
18599
18778
|
} catch (error) {
|
|
18600
18779
|
// `request()` can fail while waiting for its transport
|
|
18601
18780
|
// lease, before its beforeRelease callback runs.
|
|
18781
|
+
activeProviderRequest?.release();
|
|
18782
|
+
activeProviderRequest = null;
|
|
18602
18783
|
releaseProviderExecution();
|
|
18603
18784
|
throw error;
|
|
18604
18785
|
}
|
|
@@ -18657,6 +18838,10 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
18657
18838
|
}
|
|
18658
18839
|
if (error instanceof ProviderExhaustedError) {
|
|
18659
18840
|
const retryAtMs = Date.parse(error.retryAt);
|
|
18841
|
+
const providerFailure =
|
|
18842
|
+
error.cause instanceof ToolExecutionError
|
|
18843
|
+
? error.cause
|
|
18844
|
+
: null;
|
|
18660
18845
|
throw createToolHttpError(
|
|
18661
18846
|
toolErrorSchemaVersion,
|
|
18662
18847
|
error.message,
|
|
@@ -18678,6 +18863,9 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
18678
18863
|
: null,
|
|
18679
18864
|
networkKind: null,
|
|
18680
18865
|
networkScope: null,
|
|
18866
|
+
...(providerFailure?.publicDetails
|
|
18867
|
+
? { publicDetails: providerFailure.publicDetails }
|
|
18868
|
+
: {}),
|
|
18681
18869
|
},
|
|
18682
18870
|
);
|
|
18683
18871
|
}
|
|
@@ -18852,6 +19040,45 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
18852
19040
|
? { providerLatencyMs: providerCallElapsedMs }
|
|
18853
19041
|
: {}),
|
|
18854
19042
|
});
|
|
19043
|
+
let surfacedError = failure.error;
|
|
19044
|
+
if (
|
|
19045
|
+
response.status === 429 &&
|
|
19046
|
+
providerRequestsInFlightAt429 !== null &&
|
|
19047
|
+
failure.error instanceof ToolExecutionError &&
|
|
19048
|
+
failure.error.origin === 'provider'
|
|
19049
|
+
) {
|
|
19050
|
+
const providerFailure = failure.error as typeof failure.error &
|
|
19051
|
+
ToolExecutionError;
|
|
19052
|
+
surfacedError = createToolHttpError(
|
|
19053
|
+
toolErrorSchemaVersion,
|
|
19054
|
+
providerFailure.message,
|
|
19055
|
+
providerFailure.billing,
|
|
19056
|
+
providerFailure.status,
|
|
19057
|
+
providerFailure.receiptFailureKind,
|
|
19058
|
+
{
|
|
19059
|
+
toolId: providerFailure.toolId,
|
|
19060
|
+
provider: providerFailure.provider,
|
|
19061
|
+
operation: providerFailure.operation,
|
|
19062
|
+
code: providerFailure.code ?? null,
|
|
19063
|
+
origin: providerFailure.origin,
|
|
19064
|
+
category: providerFailure.category,
|
|
19065
|
+
retryable: providerFailure.retryable,
|
|
19066
|
+
statusCode: providerFailure.statusCode ?? null,
|
|
19067
|
+
requestId: providerFailure.requestId,
|
|
19068
|
+
retryAfterMs: providerFailure.retryAfterMs,
|
|
19069
|
+
networkKind: providerFailure.networkKind,
|
|
19070
|
+
networkScope: providerFailure.networkScope,
|
|
19071
|
+
publicDetails: {
|
|
19072
|
+
...(providerFailure.publicDetails ?? {}),
|
|
19073
|
+
inFlightProviderRequestsAt429:
|
|
19074
|
+
providerRequestsInFlightAt429,
|
|
19075
|
+
providerConcurrencyScope:
|
|
19076
|
+
'same_provider_in_play_execution',
|
|
19077
|
+
includesFailedRequest: true,
|
|
19078
|
+
},
|
|
19079
|
+
},
|
|
19080
|
+
);
|
|
19081
|
+
}
|
|
18855
19082
|
if (failure.reason === 'concurrency_backpressure') {
|
|
18856
19083
|
// Our own relay shed. The classifier keeps it out of provider
|
|
18857
19084
|
// backpressure (origin is not the provider); count it apart
|
|
@@ -18887,7 +19114,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
18887
19114
|
throw new ProviderExhaustedError({
|
|
18888
19115
|
provider,
|
|
18889
19116
|
retryAtMs: Date.now() + failure.backpressureDelayMs,
|
|
18890
|
-
cause:
|
|
19117
|
+
cause: surfacedError,
|
|
18891
19118
|
});
|
|
18892
19119
|
}
|
|
18893
19120
|
}
|
|
@@ -18991,11 +19218,11 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
18991
19218
|
// or operator sees; carry the identifiers that locate the
|
|
18992
19219
|
// upstream request without the debug transport trace.
|
|
18993
19220
|
this.runtimeLog(
|
|
18994
|
-
`${
|
|
19221
|
+
`${surfacedError.message} (request_id=${deeplineRequestId ?? 'none'} ` +
|
|
18995
19222
|
`gateway=${transportGatewayOriginForDiagnostic(url)} attempt=${httpFailureAttempt})`,
|
|
18996
19223
|
{ level: 'error' },
|
|
18997
19224
|
);
|
|
18998
|
-
throw
|
|
19225
|
+
throw surfacedError;
|
|
18999
19226
|
}
|
|
19000
19227
|
|
|
19001
19228
|
if (!responseData) {
|
|
@@ -675,6 +675,11 @@ export type IntegrationEventWaitBoundary = {
|
|
|
675
675
|
channel: string;
|
|
676
676
|
ts: string;
|
|
677
677
|
};
|
|
678
|
+
/** Provider identity used to finalize a durable wait after replay. */
|
|
679
|
+
requestKey?: string;
|
|
680
|
+
interactionId?: string;
|
|
681
|
+
/** A previously completed identical request can resolve without parking. */
|
|
682
|
+
resolvedOutput?: Record<string, unknown>;
|
|
678
683
|
/**
|
|
679
684
|
* Optional control-plane timeout behavior. Missing preserves the legacy
|
|
680
685
|
* "return the timed_out result" replay behavior for older app deployments.
|
|
@@ -703,6 +708,7 @@ export type IntegrationEventWaitHandler = {
|
|
|
703
708
|
payload: Record<string, unknown>;
|
|
704
709
|
context: IntegrationEventWaitRuntimeContext;
|
|
705
710
|
boundary: IntegrationEventWaitBoundary;
|
|
711
|
+
force?: boolean;
|
|
706
712
|
}) => Promise<IntegrationEventWaitBoundary>;
|
|
707
713
|
/**
|
|
708
714
|
* Runs during the resumed execution only when this boundary reached its
|
|
@@ -884,6 +890,10 @@ export interface ContextOptions {
|
|
|
884
890
|
staticPipeline?: PlayStaticPipeline | null;
|
|
885
891
|
}) => Promise<void | {
|
|
886
892
|
updated: number;
|
|
893
|
+
/**
|
|
894
|
+
* Row-level latest-view conflicts. These rows are omitted from this map's
|
|
895
|
+
* returned dataset, while the Play and independent rows continue.
|
|
896
|
+
*/
|
|
887
897
|
staleDroppedKeys?: string[];
|
|
888
898
|
/**
|
|
889
899
|
* Keys initially fenced by a terminal write but verified by the
|
|
@@ -892,12 +902,10 @@ export interface ContextOptions {
|
|
|
892
902
|
*/
|
|
893
903
|
recoveredStaleKeys?: string[];
|
|
894
904
|
/**
|
|
895
|
-
*
|
|
896
|
-
*
|
|
897
|
-
*
|
|
898
|
-
*
|
|
899
|
-
* the map fails and the persistence latch trips, so no further provider
|
|
900
|
-
* call is spent on rows that can never be persisted.
|
|
905
|
+
* Legacy attempt-wide stop signal. Ordinary row-level latest-view
|
|
906
|
+
* conflicts must use staleDroppedKeys; returning supersededKeys explicitly
|
|
907
|
+
* means this attempt cannot continue and makes the Context throw
|
|
908
|
+
* RuntimeSheetAttemptSupersededError to trip the persistence latch.
|
|
901
909
|
*/
|
|
902
910
|
supersededKeys?: string[];
|
|
903
911
|
}>;
|
|
@@ -1174,6 +1182,8 @@ export interface PlayCheckpoint {
|
|
|
1174
1182
|
channel: string;
|
|
1175
1183
|
ts: string;
|
|
1176
1184
|
};
|
|
1185
|
+
requestKey?: string;
|
|
1186
|
+
interactionId?: string;
|
|
1177
1187
|
/**
|
|
1178
1188
|
* Control-plane timeout behavior captured when the wait was armed.
|
|
1179
1189
|
* Missing fields preserve legacy checkpoint replay behavior.
|
|
@@ -1193,6 +1203,15 @@ export interface PlayCheckpoint {
|
|
|
1193
1203
|
kind: 'step';
|
|
1194
1204
|
stepId: string;
|
|
1195
1205
|
output?: unknown;
|
|
1206
|
+
/**
|
|
1207
|
+
* Number of authored ctx.sleep calls evaluated inside this step.
|
|
1208
|
+
*
|
|
1209
|
+
* A replay can recover the step output without executing its callback.
|
|
1210
|
+
* Keeping this count lets the surrounding execution advance its
|
|
1211
|
+
* run-local sleep cursor, so a later sleep cannot accidentally reuse a
|
|
1212
|
+
* nested sleep boundary that was hidden by the cached step.
|
|
1213
|
+
*/
|
|
1214
|
+
sleepCount?: number;
|
|
1196
1215
|
completedAt?: number;
|
|
1197
1216
|
}
|
|
1198
1217
|
>;
|