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.
Files changed (103) hide show
  1. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  2. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +21 -0
  3. package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +2 -0
  4. package/dist/bundling-sources/shared_libs/play-runtime/durability-store.ts +3 -0
  5. package/dist/bundling-sources/shared_libs/play-runtime/map-execution-frame.ts +39 -1
  6. package/dist/bundling-sources/shared_libs/play-runtime/map-row-outcome.ts +3 -0
  7. package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +1 -0
  8. package/dist/bundling-sources/shared_libs/play-runtime/row-failure-summary.ts +139 -0
  9. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +14 -0
  10. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +38 -3
  11. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-outbox-recovery.ts +96 -79
  12. package/dist/cli/auth-2LRYSEPY.js +33 -0
  13. package/dist/cli/{auth-IQ2FZP6X.mjs → auth-AENUBFFJ.mjs} +4 -4
  14. package/dist/cli/{billing-VGYUUXH7.mjs → billing-7YMKPYGM.mjs} +4 -4
  15. package/dist/cli/{billing-CMN3NEBG.js → billing-LATMZUM4.js} +73 -73
  16. package/dist/cli/{chunk-U3U63MG5.mjs → chunk-3PNTOTQK.mjs} +1 -1
  17. package/dist/cli/{chunk-KEAPO5WK.mjs → chunk-3WNXXXUR.mjs} +2 -2
  18. package/dist/cli/{chunk-IDDBKTDA.mjs → chunk-4DP6SNP5.mjs} +2 -2
  19. package/dist/cli/{chunk-ILJPNHLM.mjs → chunk-4JEWNPBO.mjs} +1 -1
  20. package/dist/cli/{chunk-7VVON643.mjs → chunk-4S34WZXL.mjs} +1 -1
  21. package/dist/cli/{chunk-UDGO5QUW.js → chunk-6NPZL4HA.js} +26 -26
  22. package/dist/cli/{chunk-QPVYLDIM.js → chunk-6NTOQXMC.js} +15 -15
  23. package/dist/cli/{chunk-6ZMC4V2C.js → chunk-6RCWXWL2.js} +9 -9
  24. package/dist/cli/{chunk-KEGOBVCN.js → chunk-6SOSQ7MJ.js} +62 -60
  25. package/dist/cli/{chunk-5F26AK2I.js → chunk-BEUEGHA4.js} +2 -2
  26. package/dist/cli/{chunk-TAQOS22B.js → chunk-EJ2JJS4B.js} +50 -50
  27. package/dist/cli/{chunk-SEAUNX4P.mjs → chunk-GHV63LHK.mjs} +1 -1
  28. package/dist/cli/{chunk-2BZPK44I.mjs → chunk-LIEDEDMI.mjs} +1 -1
  29. package/dist/cli/{chunk-RQ54PVVO.js → chunk-PXA5K5CH.js} +1 -1
  30. package/dist/cli/{chunk-2W4MDYJT.js → chunk-PZVTEGO3.js} +8 -8
  31. package/dist/cli/{chunk-S3K2HOXI.js → chunk-RNVO2NHH.js} +4 -4
  32. package/dist/cli/{chunk-ACOOB5IQ.js → chunk-SR5XJPYZ.js} +6 -6
  33. package/dist/cli/{chunk-DZMSXW7V.mjs → chunk-SVTTXV4G.mjs} +2 -2
  34. package/dist/cli/{chunk-GD4OBISR.mjs → chunk-SW2I3DBK.mjs} +4 -2
  35. package/dist/cli/{chunk-ZIQSRTY3.mjs → chunk-TZJHPJEX.mjs} +5 -5
  36. package/dist/cli/{chunk-R6SRXAMB.js → chunk-UJCEPJQS.js} +8 -8
  37. package/dist/cli/{chunk-C3DGSZTH.mjs → chunk-VPUCZSIS.mjs} +6 -6
  38. package/dist/cli/{chunk-NTMV3XMN.js → chunk-WNO7IIFN.js} +224 -224
  39. package/dist/cli/{chunk-FX5LBDDM.js → chunk-XABOQZ4R.js} +45 -45
  40. package/dist/cli/{chunk-BA3XHL2P.mjs → chunk-Y225FAT3.mjs} +3 -3
  41. package/dist/cli/{chunk-NPFMDH5A.mjs → chunk-ZEMABHFI.mjs} +4 -4
  42. package/dist/cli/{client-2AJKSHB7.mjs → client-5C7EWE2I.mjs} +3 -3
  43. package/dist/cli/client-UU566CHL.js +20 -0
  44. package/dist/cli/{csv-RPTXODPL.mjs → csv-HQMLZZHT.mjs} +3 -3
  45. package/dist/cli/{csv-PNXWMQAE.js → csv-KTXBVBID.js} +12 -12
  46. package/dist/cli/{db-H5G2GML7.js → db-LWGWX7JH.js} +29 -29
  47. package/dist/cli/{db-66W44BV4.mjs → db-TTRXGC6L.mjs} +4 -4
  48. package/dist/cli/{describe-TV6XMZM4.mjs → describe-4TPO6SJ7.mjs} +8 -8
  49. package/dist/cli/{describe-UX5VB257.js → describe-BHXNMNW4.js} +15 -15
  50. package/dist/cli/{executions-RSGE3X6S.mjs → executions-EYEB344B.mjs} +4 -4
  51. package/dist/cli/{executions-RALP45MH.js → executions-TUWWCJII.js} +6 -6
  52. package/dist/cli/{feedback-ZPXQK2DE.mjs → feedback-B7KMX5Q7.mjs} +3 -3
  53. package/dist/cli/{feedback-LQS7OPBD.js → feedback-T3DIQUIY.js} +7 -7
  54. package/dist/cli/git-6EBZTPWN.js +23 -0
  55. package/dist/cli/{git-SAHKYZEA.mjs → git-FBNXXKNZ.mjs} +4 -4
  56. package/dist/cli/index.js +82 -82
  57. package/dist/cli/index.mjs +32 -32
  58. package/dist/cli/{legacy-noops-ZRGJQUR2.mjs → legacy-noops-CUTE7NQM.mjs} +4 -4
  59. package/dist/cli/{legacy-noops-42HDAUYJ.js → legacy-noops-NRL7F5CQ.js} +7 -7
  60. package/dist/cli/{monitors-VOBRVLDA.js → monitors-MZ4ED4ZB.js} +85 -85
  61. package/dist/cli/{monitors-MWEPWAP2.mjs → monitors-OCADR2JY.mjs} +4 -4
  62. package/dist/cli/{org-ABUXCXUN.mjs → org-EGY6QPC2.mjs} +4 -4
  63. package/dist/cli/{org-CYCNMJKY.js → org-ZOORMY2P.js} +60 -60
  64. package/dist/cli/play-PWZRGMFT.js +153 -0
  65. package/dist/cli/{play-I23F675X.mjs → play-UWOXSZ5S.mjs} +7 -7
  66. package/dist/cli/{providers-ONIGWKOI.mjs → providers-6LEQ2IWJ.mjs} +4 -4
  67. package/dist/cli/{providers-R7CFCS3A.js → providers-ARFRC3BO.js} +7 -7
  68. package/dist/cli/{quickstart-FE25MVGS.mjs → quickstart-7KOYCNWZ.mjs} +3 -3
  69. package/dist/cli/{quickstart-KHN5U2QW.js → quickstart-FTAH5VJS.js} +5 -5
  70. package/dist/cli/{search-ZTSNEBWW.mjs → search-F72JTURT.mjs} +5 -5
  71. package/dist/cli/search-FNJ5RAJG.js +26 -0
  72. package/dist/cli/{secrets-7FG3EFOM.mjs → secrets-Q46WCJV5.mjs} +4 -4
  73. package/dist/cli/{secrets-B66VQKTE.js → secrets-RRLXGTKD.js} +13 -13
  74. package/dist/cli/{sessions-NL7374WB.js → sessions-5E7N7PV4.js} +5 -5
  75. package/dist/cli/{sessions-W2INUE2N.mjs → sessions-M6VVMEB7.mjs} +4 -4
  76. package/dist/cli/{settings-TI4VEXF4.js → settings-DM2R5WKS.js} +74 -74
  77. package/dist/cli/{settings-BSJVCDJQ.mjs → settings-FAMIU4TW.mjs} +7 -7
  78. package/dist/cli/{setup-OO46BANM.mjs → setup-72X5PM4V.mjs} +6 -6
  79. package/dist/cli/{setup-IMABWEEX.js → setup-T3GYC7VU.js} +41 -41
  80. package/dist/cli/skills-FXSNHINV.js +28 -0
  81. package/dist/cli/{skills-DNPINX75.mjs → skills-JMZVXK4C.mjs} +4 -4
  82. package/dist/cli/tools-EDJJXAQK.js +40 -0
  83. package/dist/cli/{tools-P3A7JT26.mjs → tools-NWPWZ6MB.mjs} +6 -6
  84. package/dist/cli/{update-RKRE2LRM.mjs → update-ICF4SXJY.mjs} +6 -6
  85. package/dist/cli/update-WZYLWV3J.js +28 -0
  86. package/dist/cli/{workflow-MLVL6RYS.js → workflow-JPKNILNY.js} +15 -15
  87. package/dist/cli/{workflow-W2HXBSMK.mjs → workflow-RC5HBGYX.mjs} +5 -5
  88. package/dist/index.js +6 -6
  89. package/dist/index.mjs +3 -3
  90. package/dist/install-integrity.json +75 -74
  91. package/dist/release.d.mts +1 -1
  92. package/dist/release.d.ts +1 -1
  93. package/dist/release.js +2 -2
  94. package/dist/release.mjs +1 -1
  95. package/package.json +1 -1
  96. package/dist/cli/auth-FIW2P4ZF.js +0 -33
  97. package/dist/cli/client-JKCTNBFD.js +0 -20
  98. package/dist/cli/git-KHHRY6X4.js +0 -23
  99. package/dist/cli/play-Z7PGORYB.js +0 -153
  100. package/dist/cli/search-E4FWDQZN.js +0 -26
  101. package/dist/cli/skills-AE5KUFID.js +0 -28
  102. package/dist/cli/tools-2FUIU3DH.js +0 -40
  103. 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.362',
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
- this.failed += Math.max(0, input.failedDelta ?? 0);
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: Record<string, number>;
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: Record<string, number> = {};
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 ${runLedgerPendingRunsSql(options, options?.lane === undefined ? undefined : lanes[0])}, heads AS MATERIALIZED (
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;