deepline 0.3.362 → 0.3.364
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/shared_libs/play-runtime/context.ts +21 -0
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/durability-store.ts +3 -0
- package/dist/bundling-sources/shared_libs/play-runtime/map-execution-frame.ts +39 -1
- package/dist/bundling-sources/shared_libs/play-runtime/map-row-outcome.ts +3 -0
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/row-failure-summary.ts +139 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +14 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +38 -3
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-outbox-recovery.ts +96 -79
- package/dist/cli/auth-2LRYSEPY.js +33 -0
- package/dist/cli/{auth-IQ2FZP6X.mjs → auth-AENUBFFJ.mjs} +4 -4
- package/dist/cli/{billing-VGYUUXH7.mjs → billing-7YMKPYGM.mjs} +4 -4
- package/dist/cli/{billing-CMN3NEBG.js → billing-LATMZUM4.js} +73 -73
- package/dist/cli/{chunk-U3U63MG5.mjs → chunk-3PNTOTQK.mjs} +1 -1
- package/dist/cli/{chunk-KEAPO5WK.mjs → chunk-3WNXXXUR.mjs} +2 -2
- package/dist/cli/{chunk-IDDBKTDA.mjs → chunk-4DP6SNP5.mjs} +2 -2
- package/dist/cli/{chunk-ILJPNHLM.mjs → chunk-4JEWNPBO.mjs} +1 -1
- package/dist/cli/{chunk-7VVON643.mjs → chunk-4S34WZXL.mjs} +1 -1
- package/dist/cli/{chunk-UDGO5QUW.js → chunk-6NPZL4HA.js} +26 -26
- package/dist/cli/{chunk-QPVYLDIM.js → chunk-6NTOQXMC.js} +15 -15
- package/dist/cli/{chunk-6ZMC4V2C.js → chunk-6RCWXWL2.js} +9 -9
- package/dist/cli/{chunk-KEGOBVCN.js → chunk-6SOSQ7MJ.js} +62 -60
- package/dist/cli/{chunk-5F26AK2I.js → chunk-BEUEGHA4.js} +2 -2
- package/dist/cli/{chunk-TAQOS22B.js → chunk-EJ2JJS4B.js} +50 -50
- package/dist/cli/{chunk-SEAUNX4P.mjs → chunk-GHV63LHK.mjs} +1 -1
- package/dist/cli/{chunk-2BZPK44I.mjs → chunk-LIEDEDMI.mjs} +1 -1
- package/dist/cli/{chunk-RQ54PVVO.js → chunk-PXA5K5CH.js} +1 -1
- package/dist/cli/{chunk-2W4MDYJT.js → chunk-PZVTEGO3.js} +8 -8
- package/dist/cli/{chunk-S3K2HOXI.js → chunk-RNVO2NHH.js} +4 -4
- package/dist/cli/{chunk-ACOOB5IQ.js → chunk-SR5XJPYZ.js} +6 -6
- package/dist/cli/{chunk-DZMSXW7V.mjs → chunk-SVTTXV4G.mjs} +2 -2
- package/dist/cli/{chunk-GD4OBISR.mjs → chunk-SW2I3DBK.mjs} +4 -2
- package/dist/cli/{chunk-ZIQSRTY3.mjs → chunk-TZJHPJEX.mjs} +5 -5
- package/dist/cli/{chunk-R6SRXAMB.js → chunk-UJCEPJQS.js} +8 -8
- package/dist/cli/{chunk-C3DGSZTH.mjs → chunk-VPUCZSIS.mjs} +6 -6
- package/dist/cli/{chunk-NTMV3XMN.js → chunk-WNO7IIFN.js} +224 -224
- package/dist/cli/{chunk-FX5LBDDM.js → chunk-XABOQZ4R.js} +45 -45
- package/dist/cli/{chunk-BA3XHL2P.mjs → chunk-Y225FAT3.mjs} +3 -3
- package/dist/cli/{chunk-NPFMDH5A.mjs → chunk-ZEMABHFI.mjs} +4 -4
- package/dist/cli/{client-2AJKSHB7.mjs → client-5C7EWE2I.mjs} +3 -3
- package/dist/cli/client-UU566CHL.js +20 -0
- package/dist/cli/{csv-RPTXODPL.mjs → csv-HQMLZZHT.mjs} +3 -3
- package/dist/cli/{csv-PNXWMQAE.js → csv-KTXBVBID.js} +12 -12
- package/dist/cli/{db-H5G2GML7.js → db-LWGWX7JH.js} +29 -29
- package/dist/cli/{db-66W44BV4.mjs → db-TTRXGC6L.mjs} +4 -4
- package/dist/cli/{describe-TV6XMZM4.mjs → describe-4TPO6SJ7.mjs} +8 -8
- package/dist/cli/{describe-UX5VB257.js → describe-BHXNMNW4.js} +15 -15
- package/dist/cli/{executions-RSGE3X6S.mjs → executions-EYEB344B.mjs} +4 -4
- package/dist/cli/{executions-RALP45MH.js → executions-TUWWCJII.js} +6 -6
- package/dist/cli/{feedback-ZPXQK2DE.mjs → feedback-B7KMX5Q7.mjs} +3 -3
- package/dist/cli/{feedback-LQS7OPBD.js → feedback-T3DIQUIY.js} +7 -7
- package/dist/cli/git-6EBZTPWN.js +23 -0
- package/dist/cli/{git-SAHKYZEA.mjs → git-FBNXXKNZ.mjs} +4 -4
- package/dist/cli/index.js +82 -82
- package/dist/cli/index.mjs +32 -32
- package/dist/cli/{legacy-noops-ZRGJQUR2.mjs → legacy-noops-CUTE7NQM.mjs} +4 -4
- package/dist/cli/{legacy-noops-42HDAUYJ.js → legacy-noops-NRL7F5CQ.js} +7 -7
- package/dist/cli/{monitors-VOBRVLDA.js → monitors-MZ4ED4ZB.js} +85 -85
- package/dist/cli/{monitors-MWEPWAP2.mjs → monitors-OCADR2JY.mjs} +4 -4
- package/dist/cli/{org-ABUXCXUN.mjs → org-EGY6QPC2.mjs} +4 -4
- package/dist/cli/{org-CYCNMJKY.js → org-ZOORMY2P.js} +60 -60
- package/dist/cli/play-PWZRGMFT.js +153 -0
- package/dist/cli/{play-I23F675X.mjs → play-UWOXSZ5S.mjs} +7 -7
- package/dist/cli/{providers-ONIGWKOI.mjs → providers-6LEQ2IWJ.mjs} +4 -4
- package/dist/cli/{providers-R7CFCS3A.js → providers-ARFRC3BO.js} +7 -7
- package/dist/cli/{quickstart-FE25MVGS.mjs → quickstart-7KOYCNWZ.mjs} +3 -3
- package/dist/cli/{quickstart-KHN5U2QW.js → quickstart-FTAH5VJS.js} +5 -5
- package/dist/cli/{search-ZTSNEBWW.mjs → search-F72JTURT.mjs} +5 -5
- package/dist/cli/search-FNJ5RAJG.js +26 -0
- package/dist/cli/{secrets-7FG3EFOM.mjs → secrets-Q46WCJV5.mjs} +4 -4
- package/dist/cli/{secrets-B66VQKTE.js → secrets-RRLXGTKD.js} +13 -13
- package/dist/cli/{sessions-NL7374WB.js → sessions-5E7N7PV4.js} +5 -5
- package/dist/cli/{sessions-W2INUE2N.mjs → sessions-M6VVMEB7.mjs} +4 -4
- package/dist/cli/{settings-TI4VEXF4.js → settings-DM2R5WKS.js} +74 -74
- package/dist/cli/{settings-BSJVCDJQ.mjs → settings-FAMIU4TW.mjs} +7 -7
- package/dist/cli/{setup-OO46BANM.mjs → setup-72X5PM4V.mjs} +6 -6
- package/dist/cli/{setup-IMABWEEX.js → setup-T3GYC7VU.js} +41 -41
- package/dist/cli/skills-FXSNHINV.js +28 -0
- package/dist/cli/{skills-DNPINX75.mjs → skills-JMZVXK4C.mjs} +4 -4
- package/dist/cli/tools-EDJJXAQK.js +40 -0
- package/dist/cli/{tools-P3A7JT26.mjs → tools-NWPWZ6MB.mjs} +6 -6
- package/dist/cli/{update-RKRE2LRM.mjs → update-ICF4SXJY.mjs} +6 -6
- package/dist/cli/update-WZYLWV3J.js +28 -0
- package/dist/cli/{workflow-MLVL6RYS.js → workflow-JPKNILNY.js} +15 -15
- package/dist/cli/{workflow-W2HXBSMK.mjs → workflow-RC5HBGYX.mjs} +5 -5
- package/dist/index.js +6 -6
- package/dist/index.mjs +3 -3
- package/dist/install-integrity.json +75 -74
- package/dist/release.d.mts +1 -1
- package/dist/release.d.ts +1 -1
- package/dist/release.js +2 -2
- package/dist/release.mjs +1 -1
- package/package.json +1 -1
- package/dist/cli/auth-FIW2P4ZF.js +0 -33
- package/dist/cli/client-JKCTNBFD.js +0 -20
- package/dist/cli/git-KHHRY6X4.js +0 -23
- package/dist/cli/play-Z7PGORYB.js +0 -153
- package/dist/cli/search-E4FWDQZN.js +0 -26
- package/dist/cli/skills-AE5KUFID.js +0 -28
- package/dist/cli/tools-2FUIU3DH.js +0 -40
- package/dist/cli/update-U33POYJW.js +0 -28
|
@@ -200,7 +200,7 @@ export const SDK_RELEASE = {
|
|
|
200
200
|
// getters keep their established compatibility behavior.
|
|
201
201
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
202
202
|
// 0.3.90 is the first deliberately versioned SDK release for API v3.
|
|
203
|
-
version: '0.3.
|
|
203
|
+
version: '0.3.364',
|
|
204
204
|
updateSummary:
|
|
205
205
|
'Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.',
|
|
206
206
|
packageCapabilities: {
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readRowFailureCounts,
|
|
3
|
+
settledRowFailureKind,
|
|
4
|
+
} from './row-failure-summary';
|
|
1
5
|
import { RUN_EVENT_KIND } from './run-event-catalog';
|
|
2
6
|
import {
|
|
3
7
|
MapExecutionFrameStore,
|
|
@@ -8740,6 +8744,11 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
8740
8744
|
completedRowsCount: settledBeforePool.completedRows,
|
|
8741
8745
|
pendingRowsCount: Math.max(0, totalInputCount - processedCount),
|
|
8742
8746
|
failedRowsCount: settledBeforePool.failedRows,
|
|
8747
|
+
failureCounts: readRowFailureCounts(
|
|
8748
|
+
this.checkpoint.mapFrames?.[mapScope.mapInvocationId]
|
|
8749
|
+
?.failureCounts,
|
|
8750
|
+
settledBeforePool.failedRows,
|
|
8751
|
+
),
|
|
8743
8752
|
supersededRows: visibility.supersededRows,
|
|
8744
8753
|
startedAt:
|
|
8745
8754
|
this.checkpoint.mapFrames?.[mapScope.mapInvocationId]?.startedAt ??
|
|
@@ -9006,6 +9015,11 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9006
9015
|
completedRowsCount: completedRows,
|
|
9007
9016
|
pendingRowsCount: 0,
|
|
9008
9017
|
failedRowsCount: failedCount,
|
|
9018
|
+
failureCounts: readRowFailureCounts(
|
|
9019
|
+
this.checkpoint.mapFrames?.[mapScope.mapInvocationId]
|
|
9020
|
+
?.failureCounts,
|
|
9021
|
+
failedCount,
|
|
9022
|
+
),
|
|
9009
9023
|
supersededRows: visibility.supersededRows,
|
|
9010
9024
|
startedAt:
|
|
9011
9025
|
this.checkpoint.mapFrames?.[mapScope.mapInvocationId]?.startedAt ??
|
|
@@ -9044,6 +9058,10 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
9044
9058
|
completedRowsCount: terminalProgress.completedRows,
|
|
9045
9059
|
pendingRowsCount: 0,
|
|
9046
9060
|
failedRowsCount: failedCount,
|
|
9061
|
+
failureCounts: readRowFailureCounts(
|
|
9062
|
+
this.checkpoint.mapFrames?.[mapScope.mapInvocationId]?.failureCounts,
|
|
9063
|
+
failedCount,
|
|
9064
|
+
),
|
|
9047
9065
|
supersededRows: visibility.supersededRows,
|
|
9048
9066
|
startedAt:
|
|
9049
9067
|
this.checkpoint.mapFrames?.[mapScope.mapInvocationId]?.startedAt ??
|
|
@@ -10535,6 +10553,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
10535
10553
|
? { cellMetaPatch: this.activeMapCellMeta.get(rowKey) }
|
|
10536
10554
|
: {}),
|
|
10537
10555
|
error: formattedError,
|
|
10556
|
+
failureKind: settledRowFailureKind(error),
|
|
10538
10557
|
});
|
|
10539
10558
|
// This row has not entered incremental persistence because the
|
|
10540
10559
|
// fail-fast error is about to unwind the resolver. Retain this
|
|
@@ -10558,6 +10577,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
10558
10577
|
data: failedData,
|
|
10559
10578
|
...(cellMetaPatch ? { cellMetaPatch } : {}),
|
|
10560
10579
|
error: formattedError,
|
|
10580
|
+
failureKind: settledRowFailureKind(error),
|
|
10561
10581
|
});
|
|
10562
10582
|
failedRowCount += 1;
|
|
10563
10583
|
if (
|
|
@@ -11295,6 +11315,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
11295
11315
|
// optional source-run pointer may share a later writer batch. A map must
|
|
11296
11316
|
// not announce a terminal state with that bounded egress still retained.
|
|
11297
11317
|
await this.runtimeStepReceiptCompletionWriter.flush();
|
|
11318
|
+
mapProgress.flushCommitted();
|
|
11298
11319
|
if (emitTerminalEvent) {
|
|
11299
11320
|
mapProgress.update({
|
|
11300
11321
|
status: 'completed',
|
|
@@ -507,6 +507,8 @@ export interface MapExecutionFrame {
|
|
|
507
507
|
completedRowsCount?: number;
|
|
508
508
|
pendingRowsCount?: number;
|
|
509
509
|
failedRowsCount?: number;
|
|
510
|
+
/** Bounded final-row causes, additive for checkpoint compatibility. */
|
|
511
|
+
failureCounts?: import('./row-failure-summary').RowFailureCounts;
|
|
510
512
|
/** Terminal rows omitted from this run's latest view because a newer write won. */
|
|
511
513
|
supersededRows?: number;
|
|
512
514
|
activeBoundaryId?: string | null;
|
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
SkipRuntimeStepReceiptInput,
|
|
9
9
|
} from './ctx-types';
|
|
10
10
|
import type { PlayRunLedgerEvent } from './run-ledger';
|
|
11
|
+
import type { RowFailureKind } from './row-failure-summary';
|
|
11
12
|
|
|
12
13
|
export type MapRowOutcomeStatus = 'completed' | 'failed';
|
|
13
14
|
|
|
@@ -28,6 +29,8 @@ export type MapRowOutcome = {
|
|
|
28
29
|
status?: MapRowOutcomeStatus;
|
|
29
30
|
/** Row-level error message persisted when `status` is failed. */
|
|
30
31
|
error?: string | null;
|
|
32
|
+
/** Final resolver error class; successful retries never produce this fact. */
|
|
33
|
+
failureKind?: RowFailureKind;
|
|
31
34
|
};
|
|
32
35
|
|
|
33
36
|
/**
|
|
@@ -7,10 +7,18 @@ import type {
|
|
|
7
7
|
import type { MapRowOutcome, MapRowOutcomeStatus } from './durability-store';
|
|
8
8
|
import { cloneCsvAliasedRow } from './csv-rename';
|
|
9
9
|
import { RuntimeSheetAttemptSupersededError } from './runtime-sheet-errors';
|
|
10
|
+
import {
|
|
11
|
+
emptyRowFailureCounts,
|
|
12
|
+
readRowFailureCounts,
|
|
13
|
+
type RowFailureCounts,
|
|
14
|
+
} from './row-failure-summary';
|
|
10
15
|
|
|
11
16
|
export function cloneMapFrame(frame: MapExecutionFrame): MapExecutionFrame {
|
|
12
17
|
return {
|
|
13
18
|
...frame,
|
|
19
|
+
...(frame.failureCounts
|
|
20
|
+
? { failureCounts: { ...frame.failureCounts } }
|
|
21
|
+
: {}),
|
|
14
22
|
completedRowKeys: [...frame.completedRowKeys],
|
|
15
23
|
pendingRowKeys: [...frame.pendingRowKeys],
|
|
16
24
|
...(frame.suspendedInputRows
|
|
@@ -278,6 +286,7 @@ type ProgressChange = {
|
|
|
278
286
|
failedRowKey?: string | null;
|
|
279
287
|
failedRowKeys?: readonly string[];
|
|
280
288
|
failedDelta?: number;
|
|
289
|
+
failureCountsDelta?: RowFailureCounts;
|
|
281
290
|
activeBoundaryId?: string | null;
|
|
282
291
|
emitEventType?: PlayExecutionEvent['type'];
|
|
283
292
|
};
|
|
@@ -298,6 +307,8 @@ export class MapExecutionProgress {
|
|
|
298
307
|
private readonly completed: Set<string>;
|
|
299
308
|
private readonly pending: Set<string>;
|
|
300
309
|
private failed = 0;
|
|
310
|
+
private readonly failureCounts: RowFailureCounts;
|
|
311
|
+
private readonly initialSuperseded: number;
|
|
301
312
|
private rowsSinceFlush = 0;
|
|
302
313
|
private lastFlushAt = 0;
|
|
303
314
|
private started = 0;
|
|
@@ -330,6 +341,11 @@ export class MapExecutionProgress {
|
|
|
330
341
|
// Freeze settled admission once: live superseded facts can include a
|
|
331
342
|
// resolver already counted in started, whereas skipped inputs are new.
|
|
332
343
|
const baseline = this.settlement();
|
|
344
|
+
this.failureCounts = readRowFailureCounts(
|
|
345
|
+
frame?.failureCounts,
|
|
346
|
+
baseline.failedRows,
|
|
347
|
+
);
|
|
348
|
+
this.initialSuperseded = source.visibility.supersededRows;
|
|
333
349
|
this.initiallyStarted =
|
|
334
350
|
baseline.completedRows + baseline.failedRows + baseline.supersededRows;
|
|
335
351
|
this.initiallySkipped = readCount(source.skippedInputs);
|
|
@@ -404,7 +420,14 @@ export class MapExecutionProgress {
|
|
|
404
420
|
const key = raw?.trim();
|
|
405
421
|
if (key) this.pending.delete(key);
|
|
406
422
|
}
|
|
407
|
-
|
|
423
|
+
const failedDelta = Math.max(0, input.failedDelta ?? 0);
|
|
424
|
+
this.failed += failedDelta;
|
|
425
|
+
if (input.failureCountsDelta) {
|
|
426
|
+
for (const key of Object.keys(this.failureCounts) as Array<
|
|
427
|
+
keyof RowFailureCounts
|
|
428
|
+
>)
|
|
429
|
+
this.failureCounts[key] += input.failureCountsDelta[key];
|
|
430
|
+
} else this.failureCounts.unknown += failedDelta;
|
|
408
431
|
this.rowsSinceFlush += this.completed.size - before;
|
|
409
432
|
this.status = input.status ?? this.status;
|
|
410
433
|
if (input.activeBoundaryId !== undefined)
|
|
@@ -430,18 +453,27 @@ export class MapExecutionProgress {
|
|
|
430
453
|
recordCommittedRows(rows: readonly MapRowOutcome[]): void {
|
|
431
454
|
const completed: string[] = [];
|
|
432
455
|
const failed: string[] = [];
|
|
456
|
+
const failureCountsDelta = emptyRowFailureCounts();
|
|
433
457
|
for (const row of rows) {
|
|
434
458
|
if (typeof row.inputIndex === 'number')
|
|
435
459
|
this.suspendedInputs?.delete(row.inputIndex);
|
|
436
460
|
(row.status === 'failed' ? failed : completed).push(row.key);
|
|
461
|
+
if (row.status === 'failed' && this.source.visibility.isVisible(row.key))
|
|
462
|
+
failureCountsDelta[row.failureKind ?? 'unknown'] += 1;
|
|
437
463
|
}
|
|
438
464
|
this.update({
|
|
439
465
|
completedRowKeys: completed,
|
|
440
466
|
failedRowKeys: failed,
|
|
441
467
|
failedDelta: failed.length,
|
|
468
|
+
failureCountsDelta,
|
|
442
469
|
});
|
|
443
470
|
}
|
|
444
471
|
|
|
472
|
+
/** Publish a settled pool after its writer barrier, even within the progress throttle. */
|
|
473
|
+
flushCommitted(): void {
|
|
474
|
+
this.flush(Date.now());
|
|
475
|
+
}
|
|
476
|
+
|
|
445
477
|
private flush(now: number): void {
|
|
446
478
|
const frame = this.frame();
|
|
447
479
|
if (!frame) return;
|
|
@@ -465,6 +497,12 @@ export class MapExecutionProgress {
|
|
|
465
497
|
: {}),
|
|
466
498
|
completedRowsCount: progress.completedRows,
|
|
467
499
|
failedRowsCount: progress.failedRows,
|
|
500
|
+
// A later superseding write can remove a previously counted cause. Its
|
|
501
|
+
// row identity remains in the sheet; do not guess which cause survived.
|
|
502
|
+
failureCounts:
|
|
503
|
+
this.source.visibility.supersededRows === this.initialSuperseded
|
|
504
|
+
? readRowFailureCounts(this.failureCounts, progress.failedRows)
|
|
505
|
+
: readRowFailureCounts(undefined, progress.failedRows),
|
|
468
506
|
supersededRows: progress.supersededRows,
|
|
469
507
|
...(this.suspendedInputs !== undefined
|
|
470
508
|
? { suspendedInputRows: [...this.suspendedInputs.values()] }
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { MapRowOutcome, MapRowOutcomeStatus } from './durability-store';
|
|
2
|
+
import type { RowFailureKind } from './row-failure-summary';
|
|
2
3
|
|
|
3
4
|
export const MAP_ROW_OUTCOME_RUNTIME_FIELDS = {
|
|
4
5
|
rowKey: '__deeplineRowKey',
|
|
@@ -75,11 +76,13 @@ export function failedMapRowOutcome(input: {
|
|
|
75
76
|
data: Record<string, unknown>;
|
|
76
77
|
cellMetaPatch?: Record<string, unknown> | null;
|
|
77
78
|
error?: string | null;
|
|
79
|
+
failureKind?: RowFailureKind;
|
|
78
80
|
}): MapRowOutcome {
|
|
79
81
|
return {
|
|
80
82
|
...completedMapRowOutcome(input),
|
|
81
83
|
status: 'failed',
|
|
82
84
|
error: input.error ?? null,
|
|
85
|
+
...(input.failureKind ? { failureKind: input.failureKind } : {}),
|
|
83
86
|
};
|
|
84
87
|
}
|
|
85
88
|
|
|
@@ -480,6 +480,7 @@ export type PlayRunnerRowOutcomeSummary = {
|
|
|
480
480
|
totalRows: number;
|
|
481
481
|
/** Rows omitted because a newer Runtime Sheet write took precedence. */
|
|
482
482
|
supersededRows?: number;
|
|
483
|
+
failureCounts?: import('./row-failure-summary').RowFailureCounts;
|
|
483
484
|
};
|
|
484
485
|
|
|
485
486
|
export const RETAINED_PLAY_PIPELINE_CONTRACT = deployed.object({
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isProviderTransientFailure,
|
|
3
|
+
normalizeToolExecutionCategory,
|
|
4
|
+
normalizeToolExecutionOrigin,
|
|
5
|
+
} from '../plays/tool-execution-error';
|
|
6
|
+
/** Fixed-size facts about final failed rows, never about retry attempts. */
|
|
7
|
+
export const ROW_FAILURE_KINDS = [
|
|
8
|
+
'billingUnavailable',
|
|
9
|
+
'runtimeInternal',
|
|
10
|
+
'providerTransient',
|
|
11
|
+
'billingConstraint',
|
|
12
|
+
'inputOrAuth',
|
|
13
|
+
'resourceLimit',
|
|
14
|
+
'unknown',
|
|
15
|
+
] as const;
|
|
16
|
+
export type RowFailureKind = (typeof ROW_FAILURE_KINDS)[number];
|
|
17
|
+
export type RowFailureCounts = Record<RowFailureKind, number>;
|
|
18
|
+
|
|
19
|
+
export function emptyRowFailureCounts(): RowFailureCounts {
|
|
20
|
+
return Object.fromEntries(
|
|
21
|
+
ROW_FAILURE_KINDS.map((key) => [key, 0]),
|
|
22
|
+
) as RowFailureCounts;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Preserve old checkpoints as unknown, rather than manufacturing zero errors. */
|
|
26
|
+
export function readRowFailureCounts(
|
|
27
|
+
value: unknown,
|
|
28
|
+
failedRows: number,
|
|
29
|
+
): RowFailureCounts {
|
|
30
|
+
if (!Number.isSafeInteger(failedRows) || failedRows < 0)
|
|
31
|
+
throw new Error('Invalid final failed-row count.');
|
|
32
|
+
if (value === undefined || value === null)
|
|
33
|
+
return { ...emptyRowFailureCounts(), unknown: failedRows };
|
|
34
|
+
if (typeof value !== 'object' || Array.isArray(value))
|
|
35
|
+
throw new Error('Invalid final row failure summary.');
|
|
36
|
+
const record = value as Record<string, unknown>;
|
|
37
|
+
const counts = emptyRowFailureCounts();
|
|
38
|
+
for (const key of ROW_FAILURE_KINDS) {
|
|
39
|
+
const count = record[key];
|
|
40
|
+
if (typeof count !== 'number' || !Number.isSafeInteger(count) || count < 0)
|
|
41
|
+
throw new Error(`Invalid final row failure count: ${key}.`);
|
|
42
|
+
counts[key] = count;
|
|
43
|
+
}
|
|
44
|
+
if (Object.values(counts).reduce((sum, n) => sum + n, 0) !== failedRows)
|
|
45
|
+
throw new Error(
|
|
46
|
+
'Final row failure causes do not reconcile with failed rows.',
|
|
47
|
+
);
|
|
48
|
+
return counts;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Only structured terminal error metadata establishes a cause. */
|
|
52
|
+
export function settledRowFailureKind(error: unknown): RowFailureKind {
|
|
53
|
+
if (!error || typeof error !== 'object') return 'unknown';
|
|
54
|
+
const e = error as {
|
|
55
|
+
code?: unknown;
|
|
56
|
+
name?: unknown;
|
|
57
|
+
status?: unknown;
|
|
58
|
+
category?: unknown;
|
|
59
|
+
origin?: unknown;
|
|
60
|
+
};
|
|
61
|
+
const code = typeof e.code === 'string' ? e.code : '';
|
|
62
|
+
if (code === 'BILLING_UNAVAILABLE') return 'billingUnavailable';
|
|
63
|
+
if (
|
|
64
|
+
[
|
|
65
|
+
'INSUFFICIENT_CREDITS',
|
|
66
|
+
'RUN_BILLING_CAP_EXCEEDED',
|
|
67
|
+
'RUN_CREDIT_CAP_EXCEEDED',
|
|
68
|
+
'MONTHLY_BILLING_LIMIT_EXCEEDED',
|
|
69
|
+
'MONTHLY_CREDIT_CAP_EXCEEDED',
|
|
70
|
+
'billing_cap_exceeded',
|
|
71
|
+
].includes(code)
|
|
72
|
+
)
|
|
73
|
+
return 'billingConstraint';
|
|
74
|
+
if (
|
|
75
|
+
[
|
|
76
|
+
'RUNTIME_LIMIT_EXCEEDED',
|
|
77
|
+
'OUTPUT_TOO_LARGE',
|
|
78
|
+
'RUNTIME_SANDBOX_MEMORY_ABORT',
|
|
79
|
+
].includes(code)
|
|
80
|
+
)
|
|
81
|
+
return 'resourceLimit';
|
|
82
|
+
if (
|
|
83
|
+
[
|
|
84
|
+
'INTEGRATION_CREDENTIALS_MISSING',
|
|
85
|
+
'UPSTREAM_BAD_INPUT',
|
|
86
|
+
'UPSTREAM_VALIDATION_ERROR',
|
|
87
|
+
'VALIDATION_ERROR',
|
|
88
|
+
'UPSTREAM_NOT_FOUND',
|
|
89
|
+
'PROVIDER_AUTHORIZATION_FAILED',
|
|
90
|
+
'PROVIDER_AUTHENTICATION_FAILED',
|
|
91
|
+
'PROVIDER_ACCOUNT_CAPACITY',
|
|
92
|
+
].includes(code)
|
|
93
|
+
)
|
|
94
|
+
return 'inputOrAuth';
|
|
95
|
+
if (
|
|
96
|
+
[
|
|
97
|
+
'INTEGRATION_EXECUTION_ERROR',
|
|
98
|
+
'INTERNAL_ERROR',
|
|
99
|
+
'runtime_durability_internal',
|
|
100
|
+
'DURABLE_INVOCATION_OUTCOME_UNKNOWN',
|
|
101
|
+
'RUNTIME_CONTROL_PLANE_UNAVAILABLE',
|
|
102
|
+
'RUNTIME_RUNNER_LOST',
|
|
103
|
+
'RUNTIME_SANDBOX_START_FAILED',
|
|
104
|
+
'RUNTIME_SCHEDULER_UNAVAILABLE',
|
|
105
|
+
].includes(code)
|
|
106
|
+
)
|
|
107
|
+
return 'runtimeInternal';
|
|
108
|
+
if (
|
|
109
|
+
[
|
|
110
|
+
'RuntimeMapRowFeedStalledError',
|
|
111
|
+
'RuntimePersistenceCircuitOpenError',
|
|
112
|
+
'RuntimeReceiptLeaseLostError',
|
|
113
|
+
'RuntimeReceiptWaitTimeoutError',
|
|
114
|
+
'RuntimeReceiptWriterRetryDeadlineError',
|
|
115
|
+
'ToolCallReceiptCohortWaitTimeoutError',
|
|
116
|
+
].includes(String(e.name))
|
|
117
|
+
)
|
|
118
|
+
return 'runtimeInternal';
|
|
119
|
+
if (
|
|
120
|
+
[
|
|
121
|
+
'UPSTREAM_NETWORK_ERROR',
|
|
122
|
+
'UPSTREAM_RATE_LIMIT',
|
|
123
|
+
'CTX_FETCH_TIMEOUT',
|
|
124
|
+
].includes(code) ||
|
|
125
|
+
e.name === 'CtxFetchTimeoutError'
|
|
126
|
+
)
|
|
127
|
+
return 'providerTransient';
|
|
128
|
+
if (
|
|
129
|
+
isProviderTransientFailure({
|
|
130
|
+
origin: normalizeToolExecutionOrigin(e.origin),
|
|
131
|
+
category: normalizeToolExecutionCategory(e.category),
|
|
132
|
+
retryable: false,
|
|
133
|
+
})
|
|
134
|
+
)
|
|
135
|
+
return 'providerTransient';
|
|
136
|
+
if (e.origin === 'deepline' && e.category === 'internal')
|
|
137
|
+
return 'runtimeInternal';
|
|
138
|
+
return 'unknown';
|
|
139
|
+
}
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
emptyRowFailureCounts,
|
|
3
|
+
readRowFailureCounts,
|
|
4
|
+
type RowFailureCounts,
|
|
5
|
+
} from '../row-failure-summary';
|
|
1
6
|
import { RUN_EVENT_KIND } from '../run-event-catalog';
|
|
2
7
|
import { hydratePlayRunnerConfig } from '../protocol';
|
|
3
8
|
import { appendFileSync, createReadStream, writeFileSync } from 'node:fs';
|
|
@@ -2032,6 +2037,7 @@ function rowOutcomesFromCheckpoint(checkpoint: {
|
|
|
2032
2037
|
completedRowsCount?: number;
|
|
2033
2038
|
completedRowKeys?: string[];
|
|
2034
2039
|
failedRowsCount?: number;
|
|
2040
|
+
failureCounts?: RowFailureCounts;
|
|
2035
2041
|
totalRows?: number;
|
|
2036
2042
|
supersededRows?: number;
|
|
2037
2043
|
}
|
|
@@ -2041,6 +2047,7 @@ function rowOutcomesFromCheckpoint(checkpoint: {
|
|
|
2041
2047
|
failedRows: number;
|
|
2042
2048
|
totalRows: number;
|
|
2043
2049
|
supersededRows?: number;
|
|
2050
|
+
failureCounts: RowFailureCounts;
|
|
2044
2051
|
} | null {
|
|
2045
2052
|
const frames = Object.values(checkpoint.mapFrames ?? {});
|
|
2046
2053
|
if (frames.length === 0) return null;
|
|
@@ -2048,6 +2055,7 @@ function rowOutcomesFromCheckpoint(checkpoint: {
|
|
|
2048
2055
|
let failedRows = 0;
|
|
2049
2056
|
let totalRows = 0;
|
|
2050
2057
|
let supersededRows = 0;
|
|
2058
|
+
const failureCounts = emptyRowFailureCounts();
|
|
2051
2059
|
for (const frame of frames) {
|
|
2052
2060
|
const completed = Number.isFinite(frame.completedRowsCount)
|
|
2053
2061
|
? Math.max(0, Math.floor(frame.completedRowsCount ?? 0))
|
|
@@ -2060,6 +2068,11 @@ function rowOutcomesFromCheckpoint(checkpoint: {
|
|
|
2060
2068
|
: 0;
|
|
2061
2069
|
completedRows += completed;
|
|
2062
2070
|
failedRows += failed;
|
|
2071
|
+
const frameCounts = readRowFailureCounts(frame.failureCounts, failed);
|
|
2072
|
+
for (const key of Object.keys(failureCounts) as Array<
|
|
2073
|
+
keyof RowFailureCounts
|
|
2074
|
+
>)
|
|
2075
|
+
failureCounts[key] += frameCounts[key];
|
|
2063
2076
|
supersededRows += superseded;
|
|
2064
2077
|
totalRows +=
|
|
2065
2078
|
Number.isFinite(frame.totalRows) && (frame.totalRows ?? 0) >= 0
|
|
@@ -2071,6 +2084,7 @@ function rowOutcomesFromCheckpoint(checkpoint: {
|
|
|
2071
2084
|
failedRows,
|
|
2072
2085
|
totalRows,
|
|
2073
2086
|
supersededRows,
|
|
2087
|
+
failureCounts,
|
|
2074
2088
|
};
|
|
2075
2089
|
}
|
|
2076
2090
|
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readRowFailureCounts,
|
|
3
|
+
type RowFailureCounts,
|
|
4
|
+
} from '../row-failure-summary';
|
|
1
5
|
import {
|
|
2
6
|
parseRunLedgerDispatchLaunch,
|
|
3
7
|
runLedgerDispatchLaunchSql,
|
|
@@ -14548,7 +14552,13 @@ function finiteRunnerNumber(value: unknown): number | null {
|
|
|
14548
14552
|
}
|
|
14549
14553
|
|
|
14550
14554
|
function terminalRowOutcomesSummary(value: unknown): {
|
|
14551
|
-
rowOutcomes:
|
|
14555
|
+
rowOutcomes: {
|
|
14556
|
+
completedRows?: number;
|
|
14557
|
+
failedRows?: number;
|
|
14558
|
+
totalRows?: number;
|
|
14559
|
+
supersededRows?: number;
|
|
14560
|
+
failureCounts?: RowFailureCounts;
|
|
14561
|
+
};
|
|
14552
14562
|
} | null {
|
|
14553
14563
|
if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
|
|
14554
14564
|
const record = value as Record<string, unknown>;
|
|
@@ -14556,11 +14566,25 @@ function terminalRowOutcomesSummary(value: unknown): {
|
|
|
14556
14566
|
const failedRows = finiteRunnerNumber(record.failedRows);
|
|
14557
14567
|
const totalRows = finiteRunnerNumber(record.totalRows);
|
|
14558
14568
|
const supersededRows = finiteRunnerNumber(record.supersededRows);
|
|
14559
|
-
const rowOutcomes:
|
|
14569
|
+
const rowOutcomes: {
|
|
14570
|
+
completedRows?: number;
|
|
14571
|
+
failedRows?: number;
|
|
14572
|
+
totalRows?: number;
|
|
14573
|
+
supersededRows?: number;
|
|
14574
|
+
failureCounts?: RowFailureCounts;
|
|
14575
|
+
} = {};
|
|
14560
14576
|
if (completedRows !== null) rowOutcomes.completedRows = completedRows;
|
|
14561
14577
|
if (failedRows !== null) rowOutcomes.failedRows = failedRows;
|
|
14562
14578
|
if (totalRows !== null) rowOutcomes.totalRows = totalRows;
|
|
14563
14579
|
if (supersededRows !== null) rowOutcomes.supersededRows = supersededRows;
|
|
14580
|
+
if (record.failureCounts !== undefined) {
|
|
14581
|
+
if (failedRows === null)
|
|
14582
|
+
throw new Error('Final row causes require a failed-row count.');
|
|
14583
|
+
rowOutcomes.failureCounts = readRowFailureCounts(
|
|
14584
|
+
record.failureCounts,
|
|
14585
|
+
failedRows,
|
|
14586
|
+
);
|
|
14587
|
+
}
|
|
14564
14588
|
return Object.keys(rowOutcomes).length > 0 ? { rowOutcomes } : null;
|
|
14565
14589
|
}
|
|
14566
14590
|
|
|
@@ -18788,6 +18812,8 @@ export async function readPostgresSchedulerOutboxNextDueAt(
|
|
|
18788
18812
|
client: PostgresSchedulerQueryClient,
|
|
18789
18813
|
options?: Pick<PostgresSchedulerOptions, 'schema'> & {
|
|
18790
18814
|
lane?: 'lifecycle' | 'observability';
|
|
18815
|
+
/** Read one notified run's clock instead of enumerating every pending run. */
|
|
18816
|
+
runId?: string;
|
|
18791
18817
|
},
|
|
18792
18818
|
): Promise<Date | null> {
|
|
18793
18819
|
const lanes =
|
|
@@ -18796,10 +18822,18 @@ export async function readPostgresSchedulerOutboxNextDueAt(
|
|
|
18796
18822
|
: options?.lane === 'observability'
|
|
18797
18823
|
? [true]
|
|
18798
18824
|
: [false, true];
|
|
18825
|
+
const runId = options?.runId?.trim() || null;
|
|
18799
18826
|
const result = await client.query<{ due_at: Date | string | null }>(
|
|
18800
18827
|
`
|
|
18801
18828
|
-- Seek distinct run identities without reading their pending fact tails.
|
|
18802
|
-
WITH RECURSIVE ${
|
|
18829
|
+
WITH RECURSIVE ${
|
|
18830
|
+
runId
|
|
18831
|
+
? 'pending_runs AS (SELECT $1::text AS run_id)'
|
|
18832
|
+
: runLedgerPendingRunsSql(
|
|
18833
|
+
options,
|
|
18834
|
+
options?.lane === undefined ? undefined : lanes[0],
|
|
18835
|
+
)
|
|
18836
|
+
}, heads AS MATERIALIZED (
|
|
18803
18837
|
${lanes
|
|
18804
18838
|
.map(
|
|
18805
18839
|
(
|
|
@@ -18836,6 +18870,7 @@ export async function readPostgresSchedulerOutboxNextDueAt(
|
|
|
18836
18870
|
-- A failed head quarantines its lane; never wake on its successor.
|
|
18837
18871
|
WHERE candidate.failed_at IS NULL
|
|
18838
18872
|
`,
|
|
18873
|
+
runId ? [runId] : undefined,
|
|
18839
18874
|
);
|
|
18840
18875
|
const value = result.rows[0]?.due_at;
|
|
18841
18876
|
if (value instanceof Date) return value;
|