deepline 0.3.290 → 0.3.292
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundling-sources/sdk/src/client.ts +8 -6
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/sdk/src/runs/overview.ts +16 -6
- package/dist/bundling-sources/sdk/src/types.ts +21 -2
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +11 -1
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +209 -51
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +10 -2
- package/dist/bundling-sources/shared_libs/play-runtime/log-context.ts +36 -0
- package/dist/bundling-sources/shared_libs/play-runtime/log-provenance.ts +2 -1
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +5 -1
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger-event-transport.ts +54 -41
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +50 -3
- package/dist/bundling-sources/shared_libs/play-runtime/run-log-buffer.ts +9 -2
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +35 -7
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-terminal-push.ts +55 -9
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +16 -4
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-progress.ts +140 -4
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +559 -313
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-outbox-recovery.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-event-search.ts +116 -24
- package/dist/bundling-sources/shared_libs/play-runtime/step-progress.ts +6 -1
- package/dist/bundling-sources/shared_libs/play-runtime/tool-call/runtime-step-receipts-adapter.ts +22 -5
- package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +35 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +68 -8
- package/dist/bundling-sources/shared_libs/play-runtime/worker-api-types.ts +2 -0
- package/dist/bundling-sources/shared_libs/plays/tool-result-types.ts +2 -0
- package/dist/cli/index.js +209 -588
- package/dist/cli/index.mjs +209 -588
- package/dist/{client-FR_u0dxT.d.ts → client-Ct4jmvAg.d.ts} +29 -8
- package/dist/{client-phK1Ixcl.d.mts → client-Ct9GKrwu.d.mts} +29 -8
- package/dist/{compiler-manifest-9SQtUuKb.d.mts → compiler-manifest-qtTNYvBl.d.mts} +2 -0
- package/dist/{compiler-manifest-9SQtUuKb.d.ts → compiler-manifest-qtTNYvBl.d.ts} +2 -0
- package/dist/index.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +12 -5
- package/dist/index.mjs +12 -5
- package/dist/install-integrity.json +4 -4
- package/dist/plays/bundle-play-file.d.mts +2 -2
- package/dist/plays/bundle-play-file.d.ts +2 -2
- package/dist/plays/run-request.d.mts +2 -2
- package/dist/plays/run-request.d.ts +2 -2
- 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
|
@@ -3,7 +3,10 @@ import {
|
|
|
3
3
|
type PlayRunnerEvent,
|
|
4
4
|
type PlayRunnerResult,
|
|
5
5
|
} from '../protocol';
|
|
6
|
-
import type {
|
|
6
|
+
import type {
|
|
7
|
+
PlayLogContext,
|
|
8
|
+
PlayLogMetadata,
|
|
9
|
+
} from '../log-context';
|
|
7
10
|
import { parseCtxFetchHttpFailureDiagnostic } from '../log-provenance';
|
|
8
11
|
import {
|
|
9
12
|
RUNNER_TERMINAL_PUSH_MAX_BODY_BYTES,
|
|
@@ -201,9 +204,14 @@ function retainArrayWithinJsonByteLimit<T>(
|
|
|
201
204
|
* Retain the normal newest log tail, but reserve space for the canonical
|
|
202
205
|
* ctx.fetch failure diagnostics first. `runtimeDiagnostics` powers the final
|
|
203
206
|
* warning, while the matching raw log line is the durable evidence customers
|
|
204
|
-
* see through `runs logs
|
|
207
|
+
* see through `runs logs` after progress events could not be pushed.
|
|
205
208
|
*/
|
|
206
|
-
function retainTerminalLogTailWithRuntimeDiagnostics(
|
|
209
|
+
function retainTerminalLogTailWithRuntimeDiagnostics(
|
|
210
|
+
logs: readonly string[],
|
|
211
|
+
logContexts?: readonly (PlayLogContext | null)[],
|
|
212
|
+
logMetadata?: readonly (PlayLogMetadata | null)[],
|
|
213
|
+
logIds?: readonly string[],
|
|
214
|
+
): {
|
|
207
215
|
values: string[];
|
|
208
216
|
indexes: number[];
|
|
209
217
|
truncated: boolean;
|
|
@@ -215,8 +223,16 @@ function retainTerminalLogTailWithRuntimeDiagnostics(logs: readonly string[]): {
|
|
|
215
223
|
// array. Re-serializing the retained 128 KiB tail for every historical
|
|
216
224
|
// line made large completed runs exhaust their terminal-flush deadline.
|
|
217
225
|
const separatorBytes = retainedIndexes.size ? 1 : 0;
|
|
218
|
-
|
|
226
|
+
// The aligned arrays cross this same transport. Counting their bytes
|
|
227
|
+
// preserves the existing diagnostic allowance and customer-output headroom.
|
|
228
|
+
const entry = [
|
|
219
229
|
logs[index]!,
|
|
230
|
+
...(logContexts ? [logContexts[index] ?? null] : []),
|
|
231
|
+
...(logMetadata ? [logMetadata[index] ?? null] : []),
|
|
232
|
+
...(logIds ? [logIds[index]] : []),
|
|
233
|
+
];
|
|
234
|
+
const measurement = jsonByteLengthUpTo(
|
|
235
|
+
entry.length === 1 ? entry[0] : entry,
|
|
220
236
|
TERMINAL_TRANSPORT_LOG_TAIL_MAX_BYTES - retainedBytes - separatorBytes,
|
|
221
237
|
);
|
|
222
238
|
if (measurement.exceeded) return;
|
|
@@ -270,20 +286,29 @@ function appendTerminalDiagnosticsMarker(
|
|
|
270
286
|
truncatedFields: string[],
|
|
271
287
|
logContexts?: Array<PlayLogContext | null>,
|
|
272
288
|
logIds?: string[],
|
|
289
|
+
logMetadata?: Array<PlayLogMetadata | null>,
|
|
273
290
|
): {
|
|
274
291
|
logs: string[];
|
|
275
292
|
logContexts?: Array<PlayLogContext | null>;
|
|
276
293
|
logIds?: string[];
|
|
294
|
+
logMetadata?: Array<PlayLogMetadata | null>;
|
|
277
295
|
} {
|
|
278
296
|
const marker =
|
|
279
297
|
`[deepline] terminal diagnostic metadata was bounded (${truncatedFields.join(', ')}); ` +
|
|
280
298
|
'the authored play output is complete.';
|
|
281
299
|
const marked = [marker, ...logs];
|
|
282
300
|
const markedContexts = logContexts ? [null, ...logContexts] : undefined;
|
|
301
|
+
const markedMetadata = logMetadata ? [null, ...logMetadata] : undefined;
|
|
283
302
|
const markedIds = logIds ? [crypto.randomUUID(), ...logIds] : undefined;
|
|
303
|
+
const logPayload = () => ({
|
|
304
|
+
logs: marked,
|
|
305
|
+
...(markedContexts ? { logContexts: markedContexts } : {}),
|
|
306
|
+
...(markedMetadata ? { logMetadata: markedMetadata } : {}),
|
|
307
|
+
...(markedIds ? { logIds: markedIds } : {}),
|
|
308
|
+
});
|
|
284
309
|
while (
|
|
285
310
|
marked.length > 1 &&
|
|
286
|
-
!fitsJsonByteLimit(
|
|
311
|
+
!fitsJsonByteLimit(logPayload(), TERMINAL_TRANSPORT_LOG_TAIL_MAX_BYTES)
|
|
287
312
|
) {
|
|
288
313
|
// Preserve canonical ctx.fetch evidence when a marker itself needs room.
|
|
289
314
|
// Other tail lines are diagnostic context, but this line is the durable
|
|
@@ -295,16 +320,20 @@ function appendTerminalDiagnosticsMarker(
|
|
|
295
320
|
const removeAt = removableIndex === -1 ? 1 : removableIndex;
|
|
296
321
|
marked.splice(removeAt, 1);
|
|
297
322
|
markedContexts?.splice(removeAt, 1);
|
|
323
|
+
markedMetadata?.splice(removeAt, 1);
|
|
298
324
|
markedIds?.splice(removeAt, 1);
|
|
299
325
|
}
|
|
300
326
|
const logsFit = fitsJsonByteLimit(
|
|
301
|
-
|
|
327
|
+
logPayload(),
|
|
302
328
|
TERMINAL_TRANSPORT_LOG_TAIL_MAX_BYTES,
|
|
303
329
|
);
|
|
304
330
|
const retainedLogs = logsFit ? marked : [marker];
|
|
305
331
|
const retainedContexts = logsFit ? markedContexts : [null];
|
|
306
332
|
return {
|
|
307
333
|
logs: retainedLogs,
|
|
334
|
+
...(markedMetadata?.some(Boolean)
|
|
335
|
+
? { logMetadata: logsFit ? markedMetadata : [null] }
|
|
336
|
+
: {}),
|
|
308
337
|
...(markedIds
|
|
309
338
|
? { logIds: logsFit ? markedIds : markedIds.slice(0, 1) }
|
|
310
339
|
: {}),
|
|
@@ -323,18 +352,29 @@ function appendTerminalDiagnosticsMarker(
|
|
|
323
352
|
function boundSettledTerminalTransportDiagnostics(
|
|
324
353
|
result: SettledRunnerTerminalTransportResult,
|
|
325
354
|
): SettledRunnerTerminalTransportResult {
|
|
326
|
-
const logProjection = retainTerminalLogTailWithRuntimeDiagnostics(
|
|
327
|
-
result.logs,
|
|
328
|
-
);
|
|
329
355
|
if (
|
|
330
356
|
result.logContexts !== undefined &&
|
|
331
357
|
result.logContexts.length !== result.logs.length
|
|
332
358
|
) {
|
|
333
359
|
throw new TypeError('Terminal log contexts must align with result logs.');
|
|
334
360
|
}
|
|
361
|
+
if (
|
|
362
|
+
result.logMetadata !== undefined &&
|
|
363
|
+
result.logMetadata.length !== result.logs.length
|
|
364
|
+
)
|
|
365
|
+
throw new TypeError('Terminal log metadata must align with logs.');
|
|
366
|
+
const logProjection = retainTerminalLogTailWithRuntimeDiagnostics(
|
|
367
|
+
result.logs,
|
|
368
|
+
result.logContexts,
|
|
369
|
+
result.logMetadata,
|
|
370
|
+
result.logIds,
|
|
371
|
+
);
|
|
335
372
|
const logContexts = result.logContexts?.length
|
|
336
373
|
? logProjection.indexes.map((index) => result.logContexts![index] ?? null)
|
|
337
374
|
: undefined;
|
|
375
|
+
const logMetadata = result.logMetadata?.length
|
|
376
|
+
? logProjection.indexes.map((index) => result.logMetadata![index] ?? null)
|
|
377
|
+
: undefined;
|
|
338
378
|
if (result.logIds && result.logIds.length !== result.logs.length)
|
|
339
379
|
throw new TypeError('Terminal log identities must align with result logs.');
|
|
340
380
|
const logIds = result.logIds
|
|
@@ -384,16 +424,19 @@ function boundSettledTerminalTransportDiagnostics(
|
|
|
384
424
|
truncatedFields,
|
|
385
425
|
logContexts,
|
|
386
426
|
logIds,
|
|
427
|
+
logMetadata,
|
|
387
428
|
)
|
|
388
429
|
: {
|
|
389
430
|
logs: logProjection.values,
|
|
390
431
|
...(logIds ? { logIds } : {}),
|
|
391
432
|
...(logContexts?.some(Boolean) ? { logContexts } : {}),
|
|
433
|
+
...(logMetadata?.some(Boolean) ? { logMetadata } : {}),
|
|
392
434
|
};
|
|
393
435
|
return {
|
|
394
436
|
...result,
|
|
395
437
|
logs: boundedLogs.logs,
|
|
396
438
|
logContexts: boundedLogs.logContexts,
|
|
439
|
+
logMetadata: boundedLogs.logMetadata,
|
|
397
440
|
logIds: boundedLogs.logIds,
|
|
398
441
|
stats: statsProjection.value,
|
|
399
442
|
steps: stepsProjection.values,
|
|
@@ -427,17 +470,20 @@ function boundSettledTerminalTransportDiagnostics(
|
|
|
427
470
|
truncatedFields,
|
|
428
471
|
logContexts,
|
|
429
472
|
logIds,
|
|
473
|
+
logMetadata,
|
|
430
474
|
)
|
|
431
475
|
: {
|
|
432
476
|
logs: logProjection.values,
|
|
433
477
|
...(logIds ? { logIds } : {}),
|
|
434
478
|
...(logContexts?.some(Boolean) ? { logContexts } : {}),
|
|
479
|
+
...(logMetadata?.some(Boolean) ? { logMetadata } : {}),
|
|
435
480
|
};
|
|
436
481
|
return {
|
|
437
482
|
...result,
|
|
438
483
|
error: error.value,
|
|
439
484
|
logs: boundedLogs.logs,
|
|
440
485
|
logContexts: boundedLogs.logContexts,
|
|
486
|
+
logMetadata: boundedLogs.logMetadata,
|
|
441
487
|
logIds: boundedLogs.logIds,
|
|
442
488
|
stats: statsProjection.value,
|
|
443
489
|
steps: stepsProjection.values,
|
package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts
CHANGED
|
@@ -3040,8 +3040,14 @@ async function executeAssignedAbsurdAttempt(input: {
|
|
|
3040
3040
|
});
|
|
3041
3041
|
}
|
|
3042
3042
|
|
|
3043
|
-
//
|
|
3044
|
-
//
|
|
3043
|
+
// Terminal step errors and failed-map settled counts ride the existing
|
|
3044
|
+
// terminal outbox row. They no longer need a progress write immediately
|
|
3045
|
+
// ahead of the terminal in the same ordered run stream.
|
|
3046
|
+
const observedTerminalProgressEvents =
|
|
3047
|
+
progressReporter.collectTerminalFailureProgressEvents?.() ?? [];
|
|
3048
|
+
if (observedTerminalProgressEvents.length > 0) {
|
|
3049
|
+
progressReporter.markTerminalFailureProgressPersisted?.();
|
|
3050
|
+
}
|
|
3045
3051
|
const failedTerminalProgressEvents =
|
|
3046
3052
|
runnerResult.status === 'failed'
|
|
3047
3053
|
? await collectFailedTerminalSettledProgressEvents({
|
|
@@ -3059,8 +3065,14 @@ async function executeAssignedAbsurdAttempt(input: {
|
|
|
3059
3065
|
...options,
|
|
3060
3066
|
terminalLogOutbox: input.fromDetachedRunner ? 'ctx_log_tail' : 'omit',
|
|
3061
3067
|
sandboxCrashDiagnostic,
|
|
3062
|
-
...(
|
|
3063
|
-
|
|
3068
|
+
...(observedTerminalProgressEvents.length > 0 ||
|
|
3069
|
+
failedTerminalProgressEvents.length > 0
|
|
3070
|
+
? {
|
|
3071
|
+
terminalProgressEvents: [
|
|
3072
|
+
...observedTerminalProgressEvents,
|
|
3073
|
+
...failedTerminalProgressEvents,
|
|
3074
|
+
],
|
|
3075
|
+
}
|
|
3064
3076
|
: {}),
|
|
3065
3077
|
},
|
|
3066
3078
|
),
|
package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-progress.ts
CHANGED
|
@@ -24,6 +24,8 @@ import type { PlayExecutionEvent } from '../ctx-types';
|
|
|
24
24
|
import type { PlayActivityObservation } from '../activity-observation';
|
|
25
25
|
import {
|
|
26
26
|
normalizePlayLogContext,
|
|
27
|
+
normalizePlayLogMetadata,
|
|
28
|
+
type PlayLogMetadata,
|
|
27
29
|
type PlayLogContext,
|
|
28
30
|
} from '../log-context';
|
|
29
31
|
import type {
|
|
@@ -62,6 +64,7 @@ const PLAY_TOOL_BATCH_LOG_PATTERN =
|
|
|
62
64
|
/(?:^|\]\s*)Executing tool batch\s+([A-Za-z0-9_:-]+):\s+(\d+)\s+calls\b/i;
|
|
63
65
|
const CTX_LOG_TIMESTAMP_PREFIX_RE = /^\[\d{4}-\d{2}-\d{2}T[\d:.]+Z\]\s+/;
|
|
64
66
|
const LIVE_LOG_TAIL_LIMIT = 1_000;
|
|
67
|
+
const STEP_FAILURE_PROGRESS_DEBOUNCE_MS = 250;
|
|
65
68
|
|
|
66
69
|
function docflowInvocationSequence(invocationId?: string): number {
|
|
67
70
|
if (!invocationId) return -1;
|
|
@@ -257,6 +260,11 @@ export type PostgresSchedulerProgressReporter = {
|
|
|
257
260
|
skipTerminalLogReplay?: boolean;
|
|
258
261
|
},
|
|
259
262
|
): Promise<void>;
|
|
263
|
+
/** Fold observed step failures into the terminal outbox instead of a
|
|
264
|
+
* separate, immediately-preceding progress write. */
|
|
265
|
+
collectTerminalFailureProgressEvents?(): PlayRunLedgerEvent[];
|
|
266
|
+
/** Cancel deferred failure progress once the terminal transaction owns it. */
|
|
267
|
+
markTerminalFailureProgressPersisted?(): void;
|
|
260
268
|
};
|
|
261
269
|
|
|
262
270
|
export type PostgresSchedulerDurableLogMode =
|
|
@@ -295,11 +303,13 @@ type TerminalLogReplayPayload = NonNullable<
|
|
|
295
303
|
function terminalReplayEntriesForMode(input: {
|
|
296
304
|
resultLogs: readonly string[];
|
|
297
305
|
resultLogContexts?: readonly (PlayLogContext | null)[];
|
|
306
|
+
resultLogMetadata?: readonly (PlayLogMetadata | null)[];
|
|
298
307
|
resultLogIds?: readonly string[];
|
|
299
308
|
durableLogMode: PostgresSchedulerDurableLogMode;
|
|
300
309
|
}): Array<{
|
|
301
310
|
line: string;
|
|
302
311
|
context: PlayLogContext | null;
|
|
312
|
+
metadata: PlayLogMetadata | null;
|
|
303
313
|
entryId: string | null;
|
|
304
314
|
}> {
|
|
305
315
|
if (
|
|
@@ -308,6 +318,12 @@ function terminalReplayEntriesForMode(input: {
|
|
|
308
318
|
) {
|
|
309
319
|
throw new TypeError('Terminal log contexts must align with result logs.');
|
|
310
320
|
}
|
|
321
|
+
if (
|
|
322
|
+
input.resultLogMetadata !== undefined &&
|
|
323
|
+
input.resultLogMetadata.length !== input.resultLogs.length
|
|
324
|
+
) {
|
|
325
|
+
throw new TypeError('Terminal log metadata must align with result logs.');
|
|
326
|
+
}
|
|
311
327
|
if (
|
|
312
328
|
input.resultLogIds &&
|
|
313
329
|
input.resultLogIds.length !== input.resultLogs.length
|
|
@@ -326,6 +342,7 @@ function terminalReplayEntriesForMode(input: {
|
|
|
326
342
|
{
|
|
327
343
|
line,
|
|
328
344
|
context: normalizePlayLogContext(input.resultLogContexts?.[index]),
|
|
345
|
+
metadata: normalizePlayLogMetadata(input.resultLogMetadata?.[index]),
|
|
329
346
|
entryId: input.resultLogIds?.[index] ?? null,
|
|
330
347
|
},
|
|
331
348
|
];
|
|
@@ -335,6 +352,7 @@ function terminalReplayEntriesForMode(input: {
|
|
|
335
352
|
function terminalReplayPayloadForMode(input: {
|
|
336
353
|
resultLogs: readonly string[];
|
|
337
354
|
resultLogContexts?: readonly (PlayLogContext | null)[];
|
|
355
|
+
resultLogMetadata?: readonly (PlayLogMetadata | null)[];
|
|
338
356
|
resultLogIds?: readonly string[];
|
|
339
357
|
durableLogMode: PostgresSchedulerDurableLogMode;
|
|
340
358
|
liveLogs: readonly string[];
|
|
@@ -381,9 +399,11 @@ function terminalReplayPayloadForMode(input: {
|
|
|
381
399
|
}
|
|
382
400
|
const lines = entries.map((entry) => entry.line);
|
|
383
401
|
const lineContexts = entries.map((entry) => entry.context);
|
|
402
|
+
const lineMetadata = entries.map((entry) => entry.metadata);
|
|
384
403
|
return {
|
|
385
404
|
lines,
|
|
386
405
|
...(lineContexts.some(Boolean) ? { lineContexts } : {}),
|
|
406
|
+
...(lineMetadata.some(Boolean) ? { lineMetadata } : {}),
|
|
387
407
|
...(input.resultLogIds
|
|
388
408
|
? { lineIds: entries.map((entry) => entry.entryId) }
|
|
389
409
|
: {}),
|
|
@@ -446,6 +466,7 @@ export function createPostgresSchedulerProgressReporter(input: {
|
|
|
446
466
|
let nodeProgressSealed = false;
|
|
447
467
|
let liveLogs: string[] = [];
|
|
448
468
|
let liveLogContexts: Array<PlayLogContext | null> = [];
|
|
469
|
+
let liveLogMetadata: Array<PlayLogMetadata | null> = [];
|
|
449
470
|
let liveLogIds: Array<string | null> = [];
|
|
450
471
|
let liveLogsTotalCount = input.initialLiveLogTotalCount ?? 0;
|
|
451
472
|
let liveLogsDirty = false;
|
|
@@ -487,6 +508,9 @@ export function createPostgresSchedulerProgressReporter(input: {
|
|
|
487
508
|
|
|
488
509
|
let activeProgressUpload: Promise<void> | null = null;
|
|
489
510
|
let pendingProgressUpload: ProgressFlushRequest | null = null;
|
|
511
|
+
let deferredStepFailureFlushTimer: ReturnType<typeof setTimeout> | null =
|
|
512
|
+
null;
|
|
513
|
+
let terminalFailureProgressPersisted = false;
|
|
490
514
|
|
|
491
515
|
const mergeProgressFlushRequest = (
|
|
492
516
|
current: ProgressFlushRequest | null,
|
|
@@ -549,6 +573,7 @@ export function createPostgresSchedulerProgressReporter(input: {
|
|
|
549
573
|
: undefined;
|
|
550
574
|
const nextLogs = liveLogs.slice();
|
|
551
575
|
const nextLogContexts = liveLogContexts.slice();
|
|
576
|
+
const nextLogMetadata = liveLogMetadata.slice();
|
|
552
577
|
const nextLogIds = liveLogIds.slice();
|
|
553
578
|
const nextLogTotalCount = liveLogsTotalCount;
|
|
554
579
|
const nextDatasetLifecycleEvents = includeProgress
|
|
@@ -572,9 +597,42 @@ export function createPostgresSchedulerProgressReporter(input: {
|
|
|
572
597
|
await publishStatusUpdate({
|
|
573
598
|
playId: launch.runId,
|
|
574
599
|
status: request.status,
|
|
575
|
-
|
|
600
|
+
// On failure only observed causal errors may precede the
|
|
601
|
+
// terminal. Partial map counters cannot prove completion.
|
|
602
|
+
liveNodeProgress: nodeProgressSealed
|
|
603
|
+
? terminalFailureProgressPersisted
|
|
604
|
+
? {}
|
|
605
|
+
: Object.fromEntries(
|
|
606
|
+
Object.entries(nextProgress ?? {})
|
|
607
|
+
.filter(
|
|
608
|
+
([, progress]) =>
|
|
609
|
+
progress.error || progress.nodeIo?.error,
|
|
610
|
+
)
|
|
611
|
+
.map(([id, progress]) => [
|
|
612
|
+
id,
|
|
613
|
+
{
|
|
614
|
+
startedAt: progress.startedAt,
|
|
615
|
+
completedAt: progress.completedAt,
|
|
616
|
+
updatedAt: progress.updatedAt,
|
|
617
|
+
error:
|
|
618
|
+
progress.error ??
|
|
619
|
+
progress.nodeIo?.error ??
|
|
620
|
+
undefined,
|
|
621
|
+
nodeIo: progress.nodeIo,
|
|
622
|
+
},
|
|
623
|
+
]),
|
|
624
|
+
)
|
|
625
|
+
: terminalFailureProgressPersisted
|
|
626
|
+
? Object.fromEntries(
|
|
627
|
+
Object.entries(nextProgress ?? {}).filter(
|
|
628
|
+
([, progress]) =>
|
|
629
|
+
!progress.error && !progress.nodeIo?.error,
|
|
630
|
+
),
|
|
631
|
+
)
|
|
632
|
+
: nextProgress,
|
|
576
633
|
liveLogs: nextLogs,
|
|
577
634
|
liveLogContexts: nextLogContexts,
|
|
635
|
+
liveLogMetadata: nextLogMetadata,
|
|
578
636
|
liveLogIds: nextLogIds,
|
|
579
637
|
liveLogTotalCount: nextLogTotalCount,
|
|
580
638
|
...(input.initialLiveLogTotalCount !== undefined
|
|
@@ -684,6 +742,7 @@ export function createPostgresSchedulerProgressReporter(input: {
|
|
|
684
742
|
line: string,
|
|
685
743
|
context?: unknown,
|
|
686
744
|
entryId?: unknown,
|
|
745
|
+
metadata?: unknown,
|
|
687
746
|
) => {
|
|
688
747
|
const trimmed = line.trim();
|
|
689
748
|
if (!trimmed) {
|
|
@@ -692,9 +751,11 @@ export function createPostgresSchedulerProgressReporter(input: {
|
|
|
692
751
|
liveLogsTotalCount += 1;
|
|
693
752
|
liveLogs = [...liveLogs, trimmed];
|
|
694
753
|
liveLogContexts = [...liveLogContexts, normalizePlayLogContext(context)];
|
|
754
|
+
liveLogMetadata = [...liveLogMetadata, normalizePlayLogMetadata(metadata)];
|
|
695
755
|
liveLogIds = [...liveLogIds, typeof entryId === 'string' ? entryId : null];
|
|
696
756
|
if (liveLogs.length > LIVE_LOG_TAIL_LIMIT) {
|
|
697
757
|
liveLogs = liveLogs.slice(liveLogs.length - LIVE_LOG_TAIL_LIMIT);
|
|
758
|
+
liveLogMetadata = liveLogMetadata.slice(-LIVE_LOG_TAIL_LIMIT);
|
|
698
759
|
liveLogIds = liveLogIds.slice(liveLogIds.length - LIVE_LOG_TAIL_LIMIT);
|
|
699
760
|
liveLogContexts = liveLogContexts.slice(
|
|
700
761
|
liveLogContexts.length - LIVE_LOG_TAIL_LIMIT,
|
|
@@ -761,6 +822,7 @@ export function createPostgresSchedulerProgressReporter(input: {
|
|
|
761
822
|
...(current.completedAt !== undefined && !clearCompletedAt
|
|
762
823
|
? { completedAt: current.completedAt }
|
|
763
824
|
: {}),
|
|
825
|
+
...(current.error !== undefined ? { error: current.error } : {}),
|
|
764
826
|
...(current.nodeIo !== undefined ? { nodeIo: current.nodeIo } : {}),
|
|
765
827
|
...(current.conditionCounts !== undefined
|
|
766
828
|
? { conditionCounts: current.conditionCounts }
|
|
@@ -810,6 +872,7 @@ export function createPostgresSchedulerProgressReporter(input: {
|
|
|
810
872
|
...(patch.completedAt !== undefined
|
|
811
873
|
? { completedAt: patch.completedAt }
|
|
812
874
|
: {}),
|
|
875
|
+
...(patch.error !== undefined ? { error: patch.error } : {}),
|
|
813
876
|
...(patch.nodeIo !== undefined ? { nodeIo: patch.nodeIo } : {}),
|
|
814
877
|
...(patch.conditionCounts !== undefined
|
|
815
878
|
? { conditionCounts: patch.conditionCounts }
|
|
@@ -994,6 +1057,34 @@ export function createPostgresSchedulerProgressReporter(input: {
|
|
|
994
1057
|
};
|
|
995
1058
|
|
|
996
1059
|
const applyExecutionEvent = (event: PlayExecutionEvent) => {
|
|
1060
|
+
if (
|
|
1061
|
+
event.type === 'step.started' ||
|
|
1062
|
+
event.type === 'step.completed' ||
|
|
1063
|
+
event.type === 'step.failed'
|
|
1064
|
+
) {
|
|
1065
|
+
setNodeProgress(event.stepId, {
|
|
1066
|
+
...(event.type === 'step.started'
|
|
1067
|
+
? { startedAt: event.at }
|
|
1068
|
+
: { completedAt: event.at }),
|
|
1069
|
+
...(event.type === 'step.failed' ? { error: event.error } : {}),
|
|
1070
|
+
});
|
|
1071
|
+
if (event.type === 'step.failed') {
|
|
1072
|
+
// A failing step is often followed immediately by run.failed. Give
|
|
1073
|
+
// that terminal transaction a chance to carry the error in its own
|
|
1074
|
+
// ordered outbox row, avoiding a redundant Convex append ahead of it.
|
|
1075
|
+
if (deferredStepFailureFlushTimer !== null) {
|
|
1076
|
+
clearTimeout(deferredStepFailureFlushTimer);
|
|
1077
|
+
}
|
|
1078
|
+
deferredStepFailureFlushTimer = setTimeout(() => {
|
|
1079
|
+
deferredStepFailureFlushTimer = null;
|
|
1080
|
+
if (!terminalFailureProgressPersisted) scheduleFlush(true);
|
|
1081
|
+
}, STEP_FAILURE_PROGRESS_DEBOUNCE_MS);
|
|
1082
|
+
deferredStepFailureFlushTimer.unref?.();
|
|
1083
|
+
} else {
|
|
1084
|
+
scheduleFlush(true);
|
|
1085
|
+
}
|
|
1086
|
+
return;
|
|
1087
|
+
}
|
|
997
1088
|
if (event.type === 'docflow.node.hit') {
|
|
998
1089
|
setNodeProgress(event.nodeId, {
|
|
999
1090
|
startedAt: event.at,
|
|
@@ -1340,6 +1431,7 @@ export function createPostgresSchedulerProgressReporter(input: {
|
|
|
1340
1431
|
line?: unknown;
|
|
1341
1432
|
source?: unknown;
|
|
1342
1433
|
context?: unknown;
|
|
1434
|
+
metadata?: unknown;
|
|
1343
1435
|
entryId?: unknown;
|
|
1344
1436
|
};
|
|
1345
1437
|
const rawLine = logEvent.line;
|
|
@@ -1347,7 +1439,12 @@ export function createPostgresSchedulerProgressReporter(input: {
|
|
|
1347
1439
|
return;
|
|
1348
1440
|
}
|
|
1349
1441
|
if (shouldPublishDurableLogEvent(durableLogMode, logEvent)) {
|
|
1350
|
-
appendLiveLog(
|
|
1442
|
+
appendLiveLog(
|
|
1443
|
+
rawLine,
|
|
1444
|
+
logEvent.context,
|
|
1445
|
+
logEvent.entryId,
|
|
1446
|
+
logEvent.metadata,
|
|
1447
|
+
);
|
|
1351
1448
|
}
|
|
1352
1449
|
markMapStartedFromLog(rawLine);
|
|
1353
1450
|
markMapStartedFromPerfLog(rawLine);
|
|
@@ -1613,13 +1710,14 @@ export function createPostgresSchedulerProgressReporter(input: {
|
|
|
1613
1710
|
// A failed result's terminal is already decided: every caller that
|
|
1614
1711
|
// finalizes one does so after (or while) the scheduler commits
|
|
1615
1712
|
// `run.failed`, and the ledger freezes a terminal run's steps. Its step
|
|
1616
|
-
//
|
|
1713
|
+
// counts from here are projections of this reporter's own partial view — a
|
|
1617
1714
|
// parked worker's fold saw page-sized map totals and never saw the
|
|
1618
1715
|
// runner's failures — so they used to land after `run.failed` claiming
|
|
1619
1716
|
// docflow nodes were "running" and a 50,000-row map had "completed" 2,000.
|
|
1620
1717
|
// The settled counts that ARE true travel in the terminal outbox row
|
|
1621
1718
|
// (`buildFailedTerminalSettledProgressEvents`). Logs, work counters,
|
|
1622
|
-
// dataset and activity
|
|
1719
|
+
// dataset and activity still flush; causal step failures already ride the
|
|
1720
|
+
// terminal outbox row when one was committed.
|
|
1623
1721
|
if (result?.status === 'failed') {
|
|
1624
1722
|
nodeProgressSealed = true;
|
|
1625
1723
|
} else if (result?.status === 'completed') {
|
|
@@ -1649,6 +1747,7 @@ export function createPostgresSchedulerProgressReporter(input: {
|
|
|
1649
1747
|
: terminalReplayPayloadForMode({
|
|
1650
1748
|
resultLogs: result.logs,
|
|
1651
1749
|
resultLogContexts: result.logContexts,
|
|
1750
|
+
resultLogMetadata: result.logMetadata,
|
|
1652
1751
|
resultLogIds: result.logIds,
|
|
1653
1752
|
durableLogMode,
|
|
1654
1753
|
liveLogs,
|
|
@@ -1661,11 +1760,47 @@ export function createPostgresSchedulerProgressReporter(input: {
|
|
|
1661
1760
|
});
|
|
1662
1761
|
};
|
|
1663
1762
|
|
|
1763
|
+
const collectTerminalFailureProgressEvents = (): PlayRunLedgerEvent[] => {
|
|
1764
|
+
const failedProgress = Object.fromEntries(
|
|
1765
|
+
Object.entries(liveNodeProgress).filter(
|
|
1766
|
+
([, progress]) => progress.error || progress.nodeIo?.error,
|
|
1767
|
+
),
|
|
1768
|
+
);
|
|
1769
|
+
if (Object.keys(failedProgress).length === 0) return [];
|
|
1770
|
+
return buildPlayRunLedgerEventsFromStatusPatch({
|
|
1771
|
+
patch: {
|
|
1772
|
+
runId: launch.runId,
|
|
1773
|
+
status: 'running',
|
|
1774
|
+
lastCheckpointAt: Date.now(),
|
|
1775
|
+
liveNodeProgress: failedProgress,
|
|
1776
|
+
},
|
|
1777
|
+
previousSnapshot: createEmptyPlayRunLedgerSnapshot({
|
|
1778
|
+
runId: launch.runId,
|
|
1779
|
+
status: 'queued',
|
|
1780
|
+
}),
|
|
1781
|
+
source: 'worker',
|
|
1782
|
+
}).filter(
|
|
1783
|
+
(event) =>
|
|
1784
|
+
event.type === RUN_EVENT_KIND.stepProgress ||
|
|
1785
|
+
event.type === RUN_EVENT_KIND.stepFailed,
|
|
1786
|
+
);
|
|
1787
|
+
};
|
|
1788
|
+
|
|
1789
|
+
const markTerminalFailureProgressPersisted = (): void => {
|
|
1790
|
+
terminalFailureProgressPersisted = true;
|
|
1791
|
+
if (deferredStepFailureFlushTimer !== null) {
|
|
1792
|
+
clearTimeout(deferredStepFailureFlushTimer);
|
|
1793
|
+
deferredStepFailureFlushTimer = null;
|
|
1794
|
+
}
|
|
1795
|
+
};
|
|
1796
|
+
|
|
1664
1797
|
return {
|
|
1665
1798
|
onRunnerEvent,
|
|
1666
1799
|
applyDurableObservations,
|
|
1667
1800
|
applyTerminalSheetOutcomes,
|
|
1668
1801
|
finalize,
|
|
1802
|
+
collectTerminalFailureProgressEvents,
|
|
1803
|
+
markTerminalFailureProgressPersisted,
|
|
1669
1804
|
};
|
|
1670
1805
|
}
|
|
1671
1806
|
|
|
@@ -1853,6 +1988,7 @@ export async function collectGatewayRunnerTerminalProgressLedgerEvents(input: {
|
|
|
1853
1988
|
liveNodeProgress: finalUpdate.liveNodeProgress,
|
|
1854
1989
|
liveLogs,
|
|
1855
1990
|
liveLogContexts: finalUpdate.liveLogContexts,
|
|
1991
|
+
liveLogMetadata: finalUpdate.liveLogMetadata,
|
|
1856
1992
|
liveLogIds: finalUpdate.liveLogIds,
|
|
1857
1993
|
liveLogsChannelOffset: Math.max(0, liveLogTotalCount - liveLogs.length),
|
|
1858
1994
|
liveLogsProducerAttempt:
|