deepline 0.3.290 → 0.3.291

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 (46) hide show
  1. package/dist/bundling-sources/sdk/src/client.ts +8 -6
  2. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  3. package/dist/bundling-sources/sdk/src/runs/overview.ts +16 -6
  4. package/dist/bundling-sources/sdk/src/types.ts +21 -2
  5. package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +11 -1
  6. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +209 -51
  7. package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +10 -2
  8. package/dist/bundling-sources/shared_libs/play-runtime/log-context.ts +36 -0
  9. package/dist/bundling-sources/shared_libs/play-runtime/log-provenance.ts +2 -1
  10. package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +5 -1
  11. package/dist/bundling-sources/shared_libs/play-runtime/run-ledger-event-transport.ts +54 -41
  12. package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +50 -3
  13. package/dist/bundling-sources/shared_libs/play-runtime/run-log-buffer.ts +9 -2
  14. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +35 -7
  15. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-terminal-push.ts +55 -9
  16. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +16 -4
  17. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-progress.ts +140 -4
  18. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +559 -313
  19. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-outbox-recovery.ts +1 -1
  20. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-event-search.ts +116 -24
  21. package/dist/bundling-sources/shared_libs/play-runtime/step-progress.ts +6 -1
  22. package/dist/bundling-sources/shared_libs/play-runtime/tool-call/runtime-step-receipts-adapter.ts +22 -5
  23. package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +35 -0
  24. package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +68 -8
  25. package/dist/bundling-sources/shared_libs/play-runtime/worker-api-types.ts +2 -0
  26. package/dist/bundling-sources/shared_libs/plays/tool-result-types.ts +2 -0
  27. package/dist/cli/index.js +193 -582
  28. package/dist/cli/index.mjs +193 -582
  29. package/dist/{client-FR_u0dxT.d.ts → client-Ct4jmvAg.d.ts} +29 -8
  30. package/dist/{client-phK1Ixcl.d.mts → client-Ct9GKrwu.d.mts} +29 -8
  31. package/dist/{compiler-manifest-9SQtUuKb.d.mts → compiler-manifest-qtTNYvBl.d.mts} +2 -0
  32. package/dist/{compiler-manifest-9SQtUuKb.d.ts → compiler-manifest-qtTNYvBl.d.ts} +2 -0
  33. package/dist/index.d.mts +4 -4
  34. package/dist/index.d.ts +4 -4
  35. package/dist/index.js +12 -5
  36. package/dist/index.mjs +12 -5
  37. package/dist/install-integrity.json +4 -4
  38. package/dist/plays/bundle-play-file.d.mts +2 -2
  39. package/dist/plays/bundle-play-file.d.ts +2 -2
  40. package/dist/plays/run-request.d.mts +2 -2
  41. package/dist/plays/run-request.d.ts +2 -2
  42. package/dist/release.d.mts +1 -1
  43. package/dist/release.d.ts +1 -1
  44. package/dist/release.js +1 -1
  45. package/dist/release.mjs +1 -1
  46. package/package.json +1 -1
@@ -5,6 +5,42 @@
5
5
  export type PlayLogContextValue = string | number | boolean;
6
6
  export type PlayLogContext = Readonly<Record<string, PlayLogContextValue>>;
7
7
 
8
+ /** Producer-owned facts for one line, separate from authored context. */
9
+ export type PlayLogMetadata = {
10
+ level: 'debug' | 'info' | 'warn' | 'error';
11
+ occurredAt: number;
12
+ stepId?: string;
13
+ };
14
+
15
+ export function normalizePlayLogMetadata(
16
+ value: unknown,
17
+ ): PlayLogMetadata | null {
18
+ if (value === undefined || value === null) return null;
19
+ if (typeof value !== 'object' || Array.isArray(value)) {
20
+ throw new TypeError('Run log metadata must be an object.');
21
+ }
22
+ const entry = value as Record<string, unknown>;
23
+ if (
24
+ !['debug', 'info', 'warn', 'error'].includes(String(entry.level)) ||
25
+ typeof entry.occurredAt !== 'number' ||
26
+ !Number.isSafeInteger(entry.occurredAt) ||
27
+ entry.occurredAt <= 0 ||
28
+ (entry.stepId !== undefined &&
29
+ (typeof entry.stepId !== 'string' ||
30
+ !entry.stepId.trim() ||
31
+ entry.stepId.length > 256))
32
+ ) {
33
+ throw new TypeError(
34
+ 'Run log metadata requires a severity, timestamp and optional step ID.',
35
+ );
36
+ }
37
+ return {
38
+ level: entry.level as PlayLogMetadata['level'],
39
+ occurredAt: entry.occurredAt,
40
+ ...(typeof entry.stepId === 'string' ? { stepId: entry.stepId } : {}),
41
+ };
42
+ }
43
+
8
44
  export const PLAY_LOG_CONTEXT_LIMITS = {
9
45
  fields: 8,
10
46
  keyBytes: 64,
@@ -341,7 +341,8 @@ export function classifyLegacyLogLine(line: string): LogProvenance {
341
341
 
342
342
  // Scheduler / sandbox / worker internals.
343
343
  if (
344
- /^\[perf\] runtime (?:map|state)\b/i.test(message) ||
344
+ /^\[perf\]/i.test(message) ||
345
+ /^\[runtime\.(?:phase_summary|logs|log_transport)\]/i.test(message) ||
345
346
  /\[worker\] picked up run\b/.test(message) ||
346
347
  /\[worker\] heartbeat\b/.test(message) ||
347
348
  /\[worker\] progress completedRows=\d+ totalRows=\d+ rowUpdates=\d+/.test(
@@ -14,7 +14,7 @@ import type {
14
14
  import type { PlayExecutionSuspension } from './suspension';
15
15
  import type { PlayStaticPipeline } from '../plays/static-pipeline';
16
16
  import type { PlayLiveEventSource } from './live-events';
17
- import type { PlayLogContext } from './log-context';
17
+ import type { PlayLogContext, PlayLogMetadata } from './log-context';
18
18
  import type { PreloadedRuntimeDbSession } from './db-session';
19
19
  import type { PacingRule } from './governor/rate-state-backend';
20
20
  import type { GovernanceSnapshot } from './governor/governor';
@@ -326,6 +326,7 @@ export type PlayRunnerLogEvent = {
326
326
  entryId?: string;
327
327
  /** Search-only context; omitted before the compatible Convex projection. */
328
328
  context?: PlayLogContext;
329
+ metadata?: PlayLogMetadata;
329
330
  };
330
331
 
331
332
  export type PlayRunnerEvent =
@@ -418,6 +419,7 @@ export type PlayRunnerResult =
418
419
  logIds?: string[];
419
420
  /** Search-only context aligned to logs; omitted when every entry is null. */
420
421
  logContexts?: Array<PlayLogContext | null>;
422
+ logMetadata?: Array<PlayLogMetadata | null>;
421
423
  stats: Record<string, unknown>;
422
424
  steps: PlayStep[];
423
425
  checkpoint: PlayCheckpoint;
@@ -434,6 +436,7 @@ export type PlayRunnerResult =
434
436
  logIds?: string[];
435
437
  /** Search-only context aligned to logs; omitted when every entry is null. */
436
438
  logContexts?: Array<PlayLogContext | null>;
439
+ logMetadata?: Array<PlayLogMetadata | null>;
437
440
  stats: Record<string, unknown>;
438
441
  steps: PlayStep[];
439
442
  checkpoint: PlayCheckpoint;
@@ -451,6 +454,7 @@ export type PlayRunnerResult =
451
454
  logIds?: string[];
452
455
  /** Search-only context aligned to logs; omitted when every entry is null. */
453
456
  logContexts?: Array<PlayLogContext | null>;
457
+ logMetadata?: Array<PlayLogMetadata | null>;
454
458
  stats: Record<string, unknown>;
455
459
  steps: PlayStep[];
456
460
  checkpoint?: PlayCheckpoint | null;
@@ -1,7 +1,6 @@
1
1
  import { normalizeRunLogIds } from './run-log-buffer';
2
2
  import { RUN_EVENT_KIND } from './run-event-catalog';
3
3
  import type { PlayRunLedgerIngressEvent } from './run-ledger';
4
- import type { PlayLogContext } from './log-context';
5
4
  import type { RunLedgerProjectionChunk } from './run-ledger-projection-contract';
6
5
 
7
6
  const APP_RUNTIME_LOG_EVENT_MAX_JSON_BYTES = 128 * 1024;
@@ -30,25 +29,27 @@ function splitRunEventForAppRuntime(
30
29
  : [];
31
30
  if (eventType !== RUN_EVENT_KIND.logAppended || lines.length === 0)
32
31
  return [event];
33
- const payloadOwnsLineContexts =
34
- payload !== null && Object.hasOwn(payload, 'lineContexts');
35
- const eventOwnsLineContexts = Object.hasOwn(event, 'lineContexts');
36
- if (payloadOwnsLineContexts && eventOwnsLineContexts) {
37
- throw new TypeError('Run log contexts must have one event owner.');
38
- }
39
- const rawLineContexts = payloadOwnsLineContexts
40
- ? payload?.lineContexts
41
- : event.lineContexts;
42
- const hasLineContexts = payloadOwnsLineContexts || eventOwnsLineContexts;
43
- if (
44
- hasLineContexts &&
45
- (!Array.isArray(rawLineContexts) || rawLineContexts.length !== lines.length)
46
- ) {
47
- throw new TypeError('Run log contexts must align with their lines.');
48
- }
49
- const lineContexts = hasLineContexts
50
- ? (rawLineContexts as Array<PlayLogContext | null>)
51
- : null;
32
+ const alignedFields = ['lineContexts', 'lineMetadata'] as const;
33
+ const aligned = alignedFields.map((key) => {
34
+ const label = key === 'lineContexts' ? 'contexts' : 'metadata';
35
+ const inPayload = payload !== null && Object.hasOwn(payload, key);
36
+ const inEvent = Object.hasOwn(event, key);
37
+ if (inPayload && inEvent)
38
+ throw new TypeError(`Run log ${label} must have one event owner.`);
39
+ const value = inPayload ? payload?.[key] : event[key];
40
+ if (
41
+ (inPayload || inEvent) &&
42
+ (!Array.isArray(value) || value.length !== lines.length)
43
+ ) {
44
+ throw new TypeError(`Run log ${label} must align with their lines.`);
45
+ }
46
+ return {
47
+ key,
48
+ inPayload,
49
+ inEvent,
50
+ value: Array.isArray(value) ? value : undefined,
51
+ };
52
+ });
52
53
  const payloadOwnsLineIds =
53
54
  payload !== null && Object.hasOwn(payload, 'lineIds');
54
55
  if (payloadOwnsLineIds && Object.hasOwn(event, 'lineIds'))
@@ -67,6 +68,7 @@ function splitRunEventForAppRuntime(
67
68
  const {
68
69
  lines: _outerLines,
69
70
  lineContexts: _outerLineContexts,
71
+ lineMetadata: _outerLineMetadata,
70
72
  lineIds: _outerLineIds,
71
73
  ...eventWithoutOuterLines
72
74
  } = event;
@@ -75,18 +77,20 @@ function splitRunEventForAppRuntime(
75
77
  const start = index;
76
78
  let bytes = 2;
77
79
  while (index < lines.length) {
78
- const lineBytes = appRuntimeTextEncoder.encode(
79
- JSON.stringify(lines[index]!),
80
- ).length;
81
- const metadataBytes =
82
- (lineContexts
83
- ? appRuntimeTextEncoder.encode(JSON.stringify(lineContexts[index]))
84
- .length + 1
85
- : 0) +
86
- (lineIds
87
- ? appRuntimeTextEncoder.encode(JSON.stringify(lineIds[index]))
88
- .length + 1
89
- : 0);
80
+ const lineBytes =
81
+ appRuntimeTextEncoder.encode(JSON.stringify(lines[index]!)).length +
82
+ aligned.reduce(
83
+ (total, field) =>
84
+ total +
85
+ (field.value
86
+ ? appRuntimeTextEncoder.encode(JSON.stringify(field.value[index]))
87
+ .length + 1
88
+ : 0),
89
+ 0,
90
+ );
91
+ const metadataBytes = lineIds
92
+ ? appRuntimeTextEncoder.encode(JSON.stringify(lineIds[index])).length + 1
93
+ : 0;
90
94
  if (
91
95
  index > start &&
92
96
  bytes + lineBytes + metadataBytes + 1 >
@@ -98,15 +102,22 @@ function splitRunEventForAppRuntime(
98
102
  index += 1;
99
103
  }
100
104
  const batchLines = lines.slice(start, index);
101
- const batchLineContexts = lineContexts?.slice(start, index);
102
105
  const batchLineIds = lineIds?.slice(start, index);
106
+ const eventFields = Object.fromEntries(
107
+ aligned
108
+ .filter((field) => field.inEvent)
109
+ .map((field) => [field.key, field.value!.slice(start, index)]),
110
+ );
111
+ const payloadFields = Object.fromEntries(
112
+ aligned
113
+ .filter((field) => field.inPayload)
114
+ .map((field) => [field.key, field.value!.slice(start, index)]),
115
+ );
103
116
  split.push(
104
117
  payload
105
118
  ? {
106
119
  ...eventWithoutOuterLines,
107
- ...(eventOwnsLineContexts && batchLineContexts
108
- ? { lineContexts: batchLineContexts }
109
- : {}),
120
+ ...eventFields,
110
121
  ...(payloadOwnsLines ? {} : { lines: batchLines }),
111
122
  ...(!payloadOwnsLineIds && batchLineIds
112
123
  ? { lineIds: batchLineIds }
@@ -114,16 +125,18 @@ function splitRunEventForAppRuntime(
114
125
  payload: {
115
126
  ...Object.fromEntries(
116
127
  Object.entries(payload).filter(
117
- ([key]) => key !== 'lineContexts' && key !== 'lineIds',
128
+ ([key]) =>
129
+ key !== 'lineIds' &&
130
+ !alignedFields.includes(
131
+ key as (typeof alignedFields)[number],
132
+ ),
118
133
  ),
119
134
  ),
120
135
  ...(payloadOwnsLines ? { lines: batchLines } : {}),
136
+ ...payloadFields,
121
137
  ...(payloadOwnsLineIds && batchLineIds
122
138
  ? { lineIds: batchLineIds }
123
139
  : {}),
124
- ...(payloadOwnsLineContexts && batchLineContexts
125
- ? { lineContexts: batchLineContexts }
126
- : {}),
127
140
  ...(payloadChannelOffset === undefined
128
141
  ? {}
129
142
  : { channelOffset: payloadChannelOffset + start }),
@@ -135,7 +148,7 @@ function splitRunEventForAppRuntime(
135
148
  : {
136
149
  ...eventWithoutOuterLines,
137
150
  lines: batchLines,
138
- ...(batchLineContexts ? { lineContexts: batchLineContexts } : {}),
151
+ ...eventFields,
139
152
  ...(batchLineIds ? { lineIds: batchLineIds } : {}),
140
153
  ...(eventChannelOffset === undefined
141
154
  ? {}
@@ -24,7 +24,7 @@ import {
24
24
  type PlayConditionCounts,
25
25
  } from './cell-provenance';
26
26
  import { isLedgerTerminalResultRef } from './output-size-limits';
27
- import type { PlayLogContext } from './log-context';
27
+ import type { PlayLogContext, PlayLogMetadata } from './log-context';
28
28
  import { RUN_EVENT_KIND, type RunEventSource } from './run-event-catalog';
29
29
 
30
30
  export type PlayRunLedgerStatus =
@@ -100,6 +100,8 @@ export type PlayRunLedgerStepProgress = {
100
100
  completedAt?: number | null;
101
101
  updatedAt?: number | null;
102
102
  nodeIo?: PlayDocflowNodeIoState;
103
+ /** Error reported by this step, distinct from a run stopping other work. */
104
+ error?: string;
103
105
  conditionCounts?: PlayConditionCounts;
104
106
  };
105
107
 
@@ -320,6 +322,7 @@ export type PlayRunLedgerEvent =
320
322
  lines: string[];
321
323
  /** Search-only context aligned to `lines`; stripped before Convex. */
322
324
  lineContexts?: Array<PlayLogContext | null>;
325
+ lineMetadata?: Array<PlayLogMetadata | null>;
323
326
  lineIds?: Array<string | null>;
324
327
  /**
325
328
  * Absolute per-run sequence (1-based) of `lines[0]`. Assigned by Run
@@ -431,6 +434,7 @@ export type PlayRunLedgerStatusPatch = {
431
434
  liveLogs?: readonly string[] | null;
432
435
  /** Search-only context aligned to `liveLogs`; stripped before Convex. */
433
436
  liveLogContexts?: readonly (PlayLogContext | null)[] | null;
437
+ liveLogMetadata?: readonly (PlayLogMetadata | null)[] | null;
434
438
  liveLogIds?: readonly (string | null)[] | null;
435
439
  /**
436
440
  * Positional cursor for `liveLogs[0]` on this producer's log channel (the
@@ -1383,6 +1387,9 @@ function normalizeStepProgress(
1383
1387
  ...(optionalFiniteNumber(value.supersededRows) !== undefined
1384
1388
  ? { supersededRows: optionalFiniteNumber(value.supersededRows) }
1385
1389
  : {}),
1390
+ ...(optionalString(value.error)
1391
+ ? { error: optionalString(value.error) }
1392
+ : {}),
1386
1393
  ...(optionalString(value.message)
1387
1394
  ? { message: optionalString(value.message) }
1388
1395
  : {}),
@@ -2329,6 +2336,7 @@ function progressSignature(
2329
2336
  completedRows: progress?.completedRows ?? null,
2330
2337
  supersededRows: progress?.supersededRows ?? null,
2331
2338
  message: progress?.message ?? null,
2339
+ error: progress?.error ?? null,
2332
2340
  artifactTableNamespace: progress?.artifactTableNamespace ?? null,
2333
2341
  startedAt: progress?.startedAt ?? null,
2334
2342
  completedAt: progress?.completedAt ?? null,
@@ -2371,6 +2379,7 @@ function terminalEventTypeForStatus(
2371
2379
  export function slicePositionalLogLines(input: {
2372
2380
  bufferLines: readonly string[];
2373
2381
  bufferContexts?: readonly (PlayLogContext | null)[];
2382
+ bufferMetadata?: readonly (PlayLogMetadata | null)[];
2374
2383
  bufferIds?: readonly (string | null)[];
2375
2384
  bufferTotalCount: number;
2376
2385
  sentCount: number;
@@ -2378,6 +2387,7 @@ export function slicePositionalLogLines(input: {
2378
2387
  lines: string[];
2379
2388
  channelOffset: number;
2380
2389
  lineContexts?: Array<PlayLogContext | null>;
2390
+ lineMetadata?: Array<PlayLogMetadata | null>;
2381
2391
  lineIds?: Array<string | null>;
2382
2392
  } | null {
2383
2393
  if (
@@ -2386,6 +2396,11 @@ export function slicePositionalLogLines(input: {
2386
2396
  ) {
2387
2397
  throw new TypeError('Run log contexts must align with the retained lines.');
2388
2398
  }
2399
+ if (
2400
+ input.bufferMetadata !== undefined &&
2401
+ input.bufferMetadata.length !== input.bufferLines.length
2402
+ )
2403
+ throw new TypeError('Run log metadata must align with lines.');
2389
2404
  if (input.bufferIds && input.bufferIds.length !== input.bufferLines.length) {
2390
2405
  throw new TypeError('Run log identities must align with retained lines.');
2391
2406
  }
@@ -2402,11 +2417,13 @@ export function slicePositionalLogLines(input: {
2402
2417
  }
2403
2418
  const start = sendFromOffset - bufferFirstOffset;
2404
2419
  const lineContexts = input.bufferContexts?.slice(start);
2420
+ const lineMetadata = input.bufferMetadata?.slice(start);
2405
2421
  return {
2406
2422
  lines: [...lines],
2407
2423
  channelOffset: sendFromOffset,
2408
2424
  ...(input.bufferIds ? { lineIds: input.bufferIds.slice(start) } : {}),
2409
2425
  ...(lineContexts?.some(Boolean) ? { lineContexts: [...lineContexts] } : {}),
2426
+ ...(lineMetadata?.some(Boolean) ? { lineMetadata: [...lineMetadata] } : {}),
2410
2427
  };
2411
2428
  }
2412
2429
 
@@ -2435,6 +2452,7 @@ export function buildPlayRunLedgerEventsFromLogLines(input: {
2435
2452
  runId: string;
2436
2453
  lines: readonly string[];
2437
2454
  lineContexts?: readonly (PlayLogContext | null)[];
2455
+ lineMetadata?: readonly (PlayLogMetadata | null)[];
2438
2456
  lineIds?: readonly (string | null)[];
2439
2457
  source?: PlayRunLedgerEventSource;
2440
2458
  occurredAt?: number;
@@ -2457,6 +2475,11 @@ export function buildPlayRunLedgerEventsFromLogLines(input: {
2457
2475
  ) {
2458
2476
  throw new TypeError('Run log contexts must align with their lines.');
2459
2477
  }
2478
+ if (
2479
+ input.lineMetadata !== undefined &&
2480
+ input.lineMetadata.length !== input.lines.length
2481
+ )
2482
+ throw new TypeError('Run log metadata must align with lines.');
2460
2483
  if (input.lineIds && input.lineIds.length !== input.lines.length) {
2461
2484
  throw new TypeError('Run log identities must align with their lines.');
2462
2485
  }
@@ -2464,6 +2487,7 @@ export function buildPlayRunLedgerEventsFromLogLines(input: {
2464
2487
  .map((line, index) => ({
2465
2488
  line: positional ? line.trim() || '(blank log line)' : line.trim(),
2466
2489
  context: input.lineContexts?.[index] ?? null,
2490
+ metadata: input.lineMetadata?.[index] ?? null,
2467
2491
  entryId: input.lineIds?.[index] ?? null,
2468
2492
  }))
2469
2493
  .filter((entry) => positional || entry.line.length > 0);
@@ -2499,6 +2523,15 @@ export function buildPlayRunLedgerEventsFromLogLines(input: {
2499
2523
  .map((entry) => entry.context),
2500
2524
  }
2501
2525
  : {}),
2526
+ ...(entries
2527
+ .slice(index, index + MAX_LEDGER_LOG_LINES_PER_EVENT)
2528
+ .some((entry) => entry.metadata)
2529
+ ? {
2530
+ lineMetadata: entries
2531
+ .slice(index, index + MAX_LEDGER_LOG_LINES_PER_EVENT)
2532
+ .map((entry) => entry.metadata),
2533
+ }
2534
+ : {}),
2502
2535
  ...(positional ? { channelOffset: input.channelOffset! + index } : {}),
2503
2536
  ...(positional &&
2504
2537
  typeof input.producerAttempt === 'number' &&
@@ -2527,6 +2560,7 @@ export function buildTerminalLogReplayEvents(input: {
2527
2560
  runId: string;
2528
2561
  lines: readonly string[];
2529
2562
  lineContexts?: readonly (PlayLogContext | null)[];
2563
+ lineMetadata?: readonly (PlayLogMetadata | null)[];
2530
2564
  lineIds?: readonly (string | null)[];
2531
2565
  source?: PlayRunLedgerEventSource;
2532
2566
  occurredAt?: number;
@@ -2542,6 +2576,7 @@ export function buildTerminalLogReplayEvents(input: {
2542
2576
  runId: input.runId,
2543
2577
  lines: input.lines,
2544
2578
  lineContexts: input.lineContexts,
2579
+ lineMetadata: input.lineMetadata,
2545
2580
  lineIds: input.lineIds,
2546
2581
  source: 'coordinator',
2547
2582
  occurredAt: input.occurredAt,
@@ -2584,6 +2619,7 @@ export function buildPlayRunLedgerEventsFromStatusPatch(input: {
2584
2619
  runId: patch.runId,
2585
2620
  lines: patch.liveLogs,
2586
2621
  lineContexts: patch.liveLogContexts ?? undefined,
2622
+ lineMetadata: patch.liveLogMetadata ?? undefined,
2587
2623
  lineIds: patch.liveLogIds ?? undefined,
2588
2624
  source,
2589
2625
  occurredAt: checkpointAt,
@@ -2666,6 +2702,7 @@ export function buildPlayRunLedgerEventsFromStatusPatch(input: {
2666
2702
  ? { supersededRows: progress.supersededRows }
2667
2703
  : {}),
2668
2704
  ...(progress.message !== undefined ? { message: progress.message } : {}),
2705
+ ...(progress.error !== undefined ? { error: progress.error } : {}),
2669
2706
  ...(progress.artifactTableNamespace !== undefined
2670
2707
  ? { artifactTableNamespace: progress.artifactTableNamespace }
2671
2708
  : {}),
@@ -2692,6 +2729,7 @@ export function buildPlayRunLedgerEventsFromStatusPatch(input: {
2692
2729
  occurredAt: progress.updatedAt ?? checkpointAt,
2693
2730
  stepId,
2694
2731
  status:
2732
+ typeof progress.error === 'string' ||
2695
2733
  typeof progress.nodeIo?.error === 'string'
2696
2734
  ? 'failed'
2697
2735
  : typeof progress.completedAt === 'number'
@@ -2703,9 +2741,18 @@ export function buildPlayRunLedgerEventsFromStatusPatch(input: {
2703
2741
  });
2704
2742
  }
2705
2743
 
2706
- if (progress.completedAt && !previousStep?.completedAt) {
2744
+ const stepError = progress.error ?? progress.nodeIo?.error;
2745
+ if (
2746
+ progress.completedAt &&
2747
+ (!previousStep?.completedAt ||
2748
+ (typeof stepError === 'string' && previousStep.status !== 'failed'))
2749
+ ) {
2707
2750
  events.push({
2708
- type: RUN_EVENT_KIND.stepCompleted,
2751
+ type:
2752
+ typeof stepError === 'string'
2753
+ ? RUN_EVENT_KIND.stepFailed
2754
+ : RUN_EVENT_KIND.stepCompleted,
2755
+ ...(typeof stepError === 'string' ? { error: stepError } : {}),
2709
2756
  runId: patch.runId,
2710
2757
  source,
2711
2758
  occurredAt: progress.completedAt,
@@ -1,5 +1,8 @@
1
- import type { PlayLogContext } from './log-context';
2
- import { normalizePlayLogContext } from './log-context';
1
+ import type { PlayLogContext, PlayLogMetadata } from './log-context';
2
+ import {
3
+ normalizePlayLogContext,
4
+ normalizePlayLogMetadata,
5
+ } from './log-context';
3
6
 
4
7
  /** Limits apply before logs enter runner memory or a transport request. */
5
8
  export const RUNNER_LOG_MAX_CHARS = 2_000;
@@ -28,6 +31,7 @@ export class RunLogBuffer {
28
31
  readonly ids: string[] = [];
29
32
  private readonly origin = crypto.randomUUID();
30
33
  readonly contexts: Array<PlayLogContext | null> = [];
34
+ readonly metadata: Array<PlayLogMetadata | null> = [];
31
35
  private bytes = 0;
32
36
  private contextBytes = 0;
33
37
  private omittedContexts = 0;
@@ -38,10 +42,12 @@ export class RunLogBuffer {
38
42
  raw: string,
39
43
  terminal = false,
40
44
  context?: PlayLogContext | null,
45
+ metadata?: PlayLogMetadata | null,
41
46
  ): string[] {
42
47
  const line = boundRunLogLine(raw);
43
48
  const bytes = encoder.encode(line).byteLength;
44
49
  const normalizedContext = normalizePlayLogContext(context);
50
+ const normalizedMetadata = normalizePlayLogMetadata(metadata);
45
51
  const contextBytes = normalizedContext
46
52
  ? encoder.encode(JSON.stringify(normalizedContext)).byteLength
47
53
  : 0;
@@ -60,6 +66,7 @@ export class RunLogBuffer {
60
66
  }
61
67
  this.ids.push(`${this.origin}:${this.lines.length}`);
62
68
  this.lines.push(line);
69
+ this.metadata.push(normalizedMetadata);
63
70
  const retainContext =
64
71
  normalizedContext !== null &&
65
72
  this.contextBytes + contextBytes <= RUNNER_LOG_CONTEXT_MAX_BYTES;
@@ -36,7 +36,10 @@ import {
36
36
  DURABLE_INVOCATION_FENCE_ATTESTATION_VALUE,
37
37
  } from '../durable-invocation-fence';
38
38
  import type { PlayRunInputPayload } from '../play-input';
39
- import type { PlayLogContext } from '../log-context';
39
+ import type {
40
+ PlayLogContext,
41
+ PlayLogMetadata,
42
+ } from '../log-context';
40
43
  import { playRunInputObjectView } from '../play-input';
41
44
  import {
42
45
  normalizePlayRunFailure,
@@ -1737,6 +1740,13 @@ async function executeRuntimePlayCode(input: {
1737
1740
  }
1738
1741
 
1739
1742
  const CAPTURED_CONSOLE_MAX_CHARS = 2_000;
1743
+ const CAPTURED_CONSOLE_PREFIX_RESERVE_CHARS = 64;
1744
+ const CAPTURED_CONSOLE_SHORTENED_MARKER =
1745
+ '… [log shortened; save complete values in a dataset and use runs export]';
1746
+ const CAPTURED_CONSOLE_MAX_MESSAGE_CHARS =
1747
+ CAPTURED_CONSOLE_MAX_CHARS - CAPTURED_CONSOLE_PREFIX_RESERVE_CHARS;
1748
+ const CAPTURED_CONSOLE_FORMAT_TRUNCATION =
1749
+ /(\.\.\.|…)\s*\d+\s+more\s+(?:characters?|items?)/i;
1740
1750
 
1741
1751
  function logLevelForCapturedConsoleMethod(
1742
1752
  method: string,
@@ -1763,14 +1773,20 @@ function createCapturedCustomerConsole(
1763
1773
  compact: true,
1764
1774
  depth: 4,
1765
1775
  maxArrayLength: 20,
1766
- maxStringLength: 2_000,
1776
+ maxStringLength: CAPTURED_CONSOLE_MAX_MESSAGE_CHARS,
1767
1777
  },
1768
1778
  ...args,
1769
1779
  );
1770
- const message =
1771
- formatted.length > CAPTURED_CONSOLE_MAX_CHARS
1772
- ? `${formatted.slice(0, CAPTURED_CONSOLE_MAX_CHARS - 1)}…`
1773
- : formatted;
1780
+ const shortened =
1781
+ formatted.length > CAPTURED_CONSOLE_MAX_MESSAGE_CHARS ||
1782
+ CAPTURED_CONSOLE_FORMAT_TRUNCATION.test(formatted);
1783
+ const message = shortened
1784
+ ? `${formatted.slice(
1785
+ 0,
1786
+ CAPTURED_CONSOLE_MAX_MESSAGE_CHARS -
1787
+ CAPTURED_CONSOLE_SHORTENED_MARKER.length,
1788
+ )}${CAPTURED_CONSOLE_SHORTENED_MARKER}`
1789
+ : formatted;
1774
1790
  ctx.log(`[console.${method}] ${message}`, {
1775
1791
  level: logLevelForCapturedConsoleMethod(method),
1776
1792
  });
@@ -1923,6 +1939,13 @@ function terminalLogsWithPhaseSummary(ctx: {
1923
1939
  ];
1924
1940
  }
1925
1941
 
1942
+ function terminalLogMetadataWithPhaseSummary(ctx: {
1943
+ getLogMetadata(): Array<PlayLogMetadata | null>;
1944
+ }): Array<PlayLogMetadata | null> | undefined {
1945
+ const metadata = ctx.getLogMetadata();
1946
+ return metadata.some(Boolean) ? [...metadata, null, null] : undefined;
1947
+ }
1948
+
1926
1949
  function terminalLogContextsWithPhaseSummary(ctx: {
1927
1950
  getLogContexts(): Array<PlayLogContext | null>;
1928
1951
  }): Array<PlayLogContext | null> | undefined {
@@ -2496,7 +2519,7 @@ async function run(
2496
2519
  });
2497
2520
  }
2498
2521
  : undefined,
2499
- onLog: (line, context, entryId) => {
2522
+ onLog: (line, context, entryId, metadata) => {
2500
2523
  const timestampMatch = line.match(/^\[([^\]]+)\]/);
2501
2524
  const timestamp = timestampMatch?.[1];
2502
2525
  const at =
@@ -2513,6 +2536,7 @@ async function run(
2513
2536
  ? { entryId }
2514
2537
  : {}),
2515
2538
  ...(context ? { context } : {}),
2539
+ ...(metadata ? { metadata } : {}),
2516
2540
  },
2517
2541
  options,
2518
2542
  );
@@ -3143,6 +3167,7 @@ async function run(
3143
3167
  ? { logIds: terminalLogIdsWithPhaseSummary(ctx) }
3144
3168
  : {}),
3145
3169
  logContexts: terminalLogContextsWithPhaseSummary(ctx),
3170
+ logMetadata: terminalLogMetadataWithPhaseSummary(ctx),
3146
3171
  stats: runtimeStats(),
3147
3172
  steps: ctx.getSteps(),
3148
3173
  tableNamespace: null,
@@ -3390,6 +3415,7 @@ async function run(
3390
3415
  ? { logIds: terminalLogIdsWithPhaseSummary(ctx) }
3391
3416
  : {}),
3392
3417
  logContexts: terminalLogContextsWithPhaseSummary(ctx),
3418
+ logMetadata: terminalLogMetadataWithPhaseSummary(ctx),
3393
3419
  ...(runtimeDiagnostics.length > 0 ? { runtimeDiagnostics } : {}),
3394
3420
  ...(rowOutcomes ? { rowOutcomes } : {}),
3395
3421
  stats: runtimeStats(),
@@ -3448,6 +3474,7 @@ async function run(
3448
3474
  ? { logIds: terminalLogIdsWithPhaseSummary(ctx) }
3449
3475
  : {}),
3450
3476
  logContexts: terminalLogContextsWithPhaseSummary(ctx),
3477
+ logMetadata: terminalLogMetadataWithPhaseSummary(ctx),
3451
3478
  stats: runtimeStats(),
3452
3479
  steps: ctx.getSteps(),
3453
3480
  checkpoint: ctx.getCheckpoint(),
@@ -3487,6 +3514,7 @@ async function run(
3487
3514
  ? { logIds: terminalLogIdsWithPhaseSummary(ctx) }
3488
3515
  : {}),
3489
3516
  logContexts: terminalLogContextsWithPhaseSummary(ctx),
3517
+ logMetadata: terminalLogMetadataWithPhaseSummary(ctx),
3490
3518
  ...(rowOutcomes ? { rowOutcomes } : {}),
3491
3519
  stats: runtimeStats(),
3492
3520
  steps: ctx.getSteps(),