deepline 0.3.247 → 0.3.248
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 +177 -13
- package/dist/bundling-sources/sdk/src/index.ts +8 -0
- package/dist/bundling-sources/sdk/src/plays/input-example.ts +40 -0
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/sdk/src/runs/overview.ts +457 -0
- package/dist/bundling-sources/sdk/src/runs/receipts.ts +23 -0
- package/dist/bundling-sources/sdk/src/tool-calls.ts +21 -1
- package/dist/bundling-sources/sdk/src/types.ts +16 -1
- package/dist/bundling-sources/shared_libs/observability/scheduled-jobs.json +19 -1
- package/dist/bundling-sources/shared_libs/play-data-plane/column-names.ts +17 -3
- package/dist/bundling-sources/shared_libs/play-data-plane/sheet-contract.ts +40 -8
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +26 -1
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +142 -14
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +2 -1
- package/dist/bundling-sources/shared_libs/play-runtime/daytona-runtime-config.ts +6 -3
- package/dist/bundling-sources/shared_libs/play-runtime/daytona-sandbox-janitor.ts +26 -21
- package/dist/bundling-sources/shared_libs/play-runtime/daytona-sandbox-reaper.ts +23 -21
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-machines.ts +17 -0
- package/dist/bundling-sources/shared_libs/play-runtime/log-context.ts +105 -0
- package/dist/bundling-sources/shared_libs/play-runtime/output-size-limits.ts +8 -1
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +9 -0
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger-event-transport.ts +40 -4
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +50 -5
- package/dist/bundling-sources/shared_libs/play-runtime/run-lifecycle-policy.ts +3 -1
- package/dist/bundling-sources/shared_libs/play-runtime/run-log-buffer.ts +28 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +14 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-terminal-push.ts +56 -16
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-control-transport.ts +184 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-launch-transport.ts +327 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +87 -30
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-session-execution.ts +24 -13
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-start-admission.ts +23 -6
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +76 -33
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +90 -35
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-pool.ts +16 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-progress.ts +62 -21
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +368 -66
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/receipt-gateway.ts +22 -4
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-outbox-recovery.ts +105 -121
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-event-search.ts +1347 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-queue-health.ts +16 -19
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-receipt-value-storage.ts +14 -6
- package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +16 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +40 -0
- package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +5 -2
- package/dist/bundling-sources/shared_libs/plays/dataset-summary.ts +11 -7
- package/dist/bundling-sources/shared_libs/plays/static-pipeline-storage-codec.ts +5 -2
- package/dist/bundling-sources/shared_libs/plays/static-pipeline.ts +11 -2
- package/dist/cli/index.js +3504 -2368
- package/dist/cli/index.mjs +3188 -2052
- package/dist/{compiler-manifest-n8ZtPv2h.d.mts → compiler-manifest-DavmLa8y.d.mts} +1 -0
- package/dist/{compiler-manifest-n8ZtPv2h.d.ts → compiler-manifest-DavmLa8y.d.ts} +1 -0
- package/dist/index.d.mts +97 -5
- package/dist/index.d.ts +97 -5
- package/dist/index.js +135 -15
- package/dist/index.mjs +135 -15
- package/dist/install-integrity.json +9 -2
- package/dist/plays/bundle-play-file.d.mts +2 -2
- package/dist/plays/bundle-play-file.d.ts +2 -2
- package/dist/plays/bundle-play-file.mjs +1 -1
- package/dist/release.d.mts +1 -1
- package/dist/release.d.ts +1 -1
- package/dist/release.js +1 -1
- package/dist/release.mjs +1 -1
- package/package.json +1 -1
|
@@ -235,6 +235,10 @@ export type FlyMachine = {
|
|
|
235
235
|
init?: {
|
|
236
236
|
exec?: string[];
|
|
237
237
|
};
|
|
238
|
+
stop_config?: {
|
|
239
|
+
signal?: string;
|
|
240
|
+
timeout?: string;
|
|
241
|
+
};
|
|
238
242
|
};
|
|
239
243
|
checks?: FlyMachineCheck[];
|
|
240
244
|
};
|
|
@@ -398,6 +402,12 @@ export function buildBootMachineConfig(input: {
|
|
|
398
402
|
init: {
|
|
399
403
|
exec: [...ABSURD_RUNTIME_WORKER_EXEC],
|
|
400
404
|
},
|
|
405
|
+
// Direct Machines API creation does not load the worker fly.toml.
|
|
406
|
+
// Fly encodes this timeout as a duration string, unlike TOML seconds.
|
|
407
|
+
stop_config: {
|
|
408
|
+
signal: DEFAULT_LANE_REPLACEMENT_STOP_SIGNAL,
|
|
409
|
+
timeout: DEFAULT_LANE_REPLACEMENT_STOP_TIMEOUT,
|
|
410
|
+
},
|
|
401
411
|
restart: {
|
|
402
412
|
policy: 'on-failure',
|
|
403
413
|
max_retries: 3,
|
|
@@ -534,6 +544,13 @@ export function buildWorkerSlotCapUpdateConfig(machine: FlyMachine): {
|
|
|
534
544
|
...(existing.init ?? {}),
|
|
535
545
|
exec: [...ABSURD_RUNTIME_WORKER_EXEC],
|
|
536
546
|
},
|
|
547
|
+
// Config updates replace the complete Machine config. Apply the same
|
|
548
|
+
// stop policy to reused historical workers while preserving other fields.
|
|
549
|
+
stop_config: {
|
|
550
|
+
...(existing.stop_config ?? {}),
|
|
551
|
+
signal: DEFAULT_LANE_REPLACEMENT_STOP_SIGNAL,
|
|
552
|
+
timeout: DEFAULT_LANE_REPLACEMENT_STOP_TIMEOUT,
|
|
553
|
+
},
|
|
537
554
|
},
|
|
538
555
|
};
|
|
539
556
|
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Small, flat log context values copied into the org-scoped event search
|
|
3
|
+
* document. Keep this contract stable across the runner and gateway packages.
|
|
4
|
+
*/
|
|
5
|
+
export type PlayLogContextValue = string | number | boolean;
|
|
6
|
+
export type PlayLogContext = Readonly<Record<string, PlayLogContextValue>>;
|
|
7
|
+
|
|
8
|
+
export const PLAY_LOG_CONTEXT_LIMITS = {
|
|
9
|
+
fields: 8,
|
|
10
|
+
keyBytes: 64,
|
|
11
|
+
stringValueBytes: 256,
|
|
12
|
+
totalBytes: 2_048,
|
|
13
|
+
} as const;
|
|
14
|
+
|
|
15
|
+
const CONTEXT_KEY = /^[A-Za-z_][A-Za-z0-9_]{0,63}$/;
|
|
16
|
+
const FORBIDDEN_CONTEXT_KEYS = new Set([
|
|
17
|
+
'__proto__',
|
|
18
|
+
'constructor',
|
|
19
|
+
'prototype',
|
|
20
|
+
]);
|
|
21
|
+
const utf8 = new TextEncoder();
|
|
22
|
+
|
|
23
|
+
/** Validate and return a detached, key-sorted JSON-safe context object. */
|
|
24
|
+
export function normalizePlayLogContext(value: unknown): PlayLogContext | null {
|
|
25
|
+
if (value === undefined || value === null) return null;
|
|
26
|
+
if (
|
|
27
|
+
!value ||
|
|
28
|
+
typeof value !== 'object' ||
|
|
29
|
+
Array.isArray(value) ||
|
|
30
|
+
![Object.prototype, null].includes(Object.getPrototypeOf(value))
|
|
31
|
+
) {
|
|
32
|
+
throw new TypeError('ctx.log context must be a flat key/value object.');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const ownKeys = Reflect.ownKeys(value);
|
|
36
|
+
if (ownKeys.some((key) => typeof key !== 'string')) {
|
|
37
|
+
throw new TypeError('ctx.log context keys must be strings.');
|
|
38
|
+
}
|
|
39
|
+
const entries: Array<[string, unknown]> = [];
|
|
40
|
+
for (const key of ownKeys as string[]) {
|
|
41
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
42
|
+
if (
|
|
43
|
+
!descriptor?.enumerable ||
|
|
44
|
+
!Object.prototype.hasOwnProperty.call(descriptor, 'value')
|
|
45
|
+
) {
|
|
46
|
+
throw new TypeError(
|
|
47
|
+
'ctx.log context fields must be enumerable data properties.',
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
entries.push([key, descriptor.value]);
|
|
51
|
+
}
|
|
52
|
+
if (entries.length > PLAY_LOG_CONTEXT_LIMITS.fields) {
|
|
53
|
+
throw new TypeError(
|
|
54
|
+
`ctx.log context supports at most ${PLAY_LOG_CONTEXT_LIMITS.fields} fields.`,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const normalized: Record<string, PlayLogContextValue> = {};
|
|
59
|
+
for (const [key, entry] of entries.sort(([left], [right]) =>
|
|
60
|
+
left < right ? -1 : left > right ? 1 : 0,
|
|
61
|
+
)) {
|
|
62
|
+
if (
|
|
63
|
+
!CONTEXT_KEY.test(key) ||
|
|
64
|
+
utf8.encode(key).byteLength > PLAY_LOG_CONTEXT_LIMITS.keyBytes ||
|
|
65
|
+
FORBIDDEN_CONTEXT_KEYS.has(key)
|
|
66
|
+
) {
|
|
67
|
+
throw new TypeError(
|
|
68
|
+
`ctx.log context key ${JSON.stringify(key)} is not supported.`,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
if (typeof entry === 'string') {
|
|
72
|
+
if (
|
|
73
|
+
utf8.encode(entry).byteLength > PLAY_LOG_CONTEXT_LIMITS.stringValueBytes
|
|
74
|
+
) {
|
|
75
|
+
throw new TypeError(
|
|
76
|
+
`ctx.log context value for ${key} exceeds ${PLAY_LOG_CONTEXT_LIMITS.stringValueBytes} UTF-8 bytes.`,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
normalized[key] = entry;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (typeof entry === 'boolean') {
|
|
83
|
+
normalized[key] = entry;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (typeof entry === 'number' && Number.isFinite(entry)) {
|
|
87
|
+
normalized[key] = entry;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
throw new TypeError(
|
|
91
|
+
`ctx.log context value for ${key} must be a string, finite number, or boolean.`,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (entries.length === 0) return null;
|
|
96
|
+
if (
|
|
97
|
+
utf8.encode(JSON.stringify(normalized)).byteLength >
|
|
98
|
+
PLAY_LOG_CONTEXT_LIMITS.totalBytes
|
|
99
|
+
) {
|
|
100
|
+
throw new TypeError(
|
|
101
|
+
`ctx.log context exceeds ${PLAY_LOG_CONTEXT_LIMITS.totalBytes} UTF-8 bytes.`,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
return normalized;
|
|
105
|
+
}
|
|
@@ -491,7 +491,14 @@ export function buildRunDatasetPreview(
|
|
|
491
491
|
const previewRows: unknown[] = [];
|
|
492
492
|
let truncated = rows.length > RUN_RESPONSE_PREVIEW_LIMITS.maxRows;
|
|
493
493
|
for (const row of rows.slice(0, RUN_RESPONSE_PREVIEW_LIMITS.maxRows)) {
|
|
494
|
-
|
|
494
|
+
// Debug columns remain persisted/exportable; exclude them before budgeting.
|
|
495
|
+
const visibleRow =
|
|
496
|
+
row && typeof row === 'object' && !Array.isArray(row)
|
|
497
|
+
? Object.fromEntries(
|
|
498
|
+
Object.entries(row).filter(([name]) => !name.startsWith('_')),
|
|
499
|
+
)
|
|
500
|
+
: row;
|
|
501
|
+
const compacted = compactRunOutputPreviewValue(visibleRow, 0);
|
|
495
502
|
truncated ||= compacted.truncated;
|
|
496
503
|
const nextRows = [...previewRows, compacted.value];
|
|
497
504
|
if (jsonSize(nextRows) > RUN_RESPONSE_PREVIEW_LIMITS.maxBytes) {
|
|
@@ -14,6 +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
18
|
import type { PreloadedRuntimeDbSession } from './db-session';
|
|
18
19
|
import type { PacingRule } from './governor/rate-state-backend';
|
|
19
20
|
import type { GovernanceSnapshot } from './governor/governor';
|
|
@@ -319,6 +320,8 @@ export type PlayRunnerLogEvent = {
|
|
|
319
320
|
at: string;
|
|
320
321
|
source: PlayLiveEventSource;
|
|
321
322
|
line: string;
|
|
323
|
+
/** Search-only context; omitted before the compatible Convex projection. */
|
|
324
|
+
context?: PlayLogContext;
|
|
322
325
|
};
|
|
323
326
|
|
|
324
327
|
export type PlayRunnerEvent =
|
|
@@ -408,6 +411,8 @@ export type PlayRunnerResult =
|
|
|
408
411
|
outputRowCount?: number;
|
|
409
412
|
rowOutcomes?: PlayRunnerRowOutcomeSummary;
|
|
410
413
|
logs: string[];
|
|
414
|
+
/** Search-only context aligned to logs; omitted when every entry is null. */
|
|
415
|
+
logContexts?: Array<PlayLogContext | null>;
|
|
411
416
|
stats: Record<string, unknown>;
|
|
412
417
|
steps: PlayStep[];
|
|
413
418
|
checkpoint: PlayCheckpoint;
|
|
@@ -421,6 +426,8 @@ export type PlayRunnerResult =
|
|
|
421
426
|
status: 'suspended';
|
|
422
427
|
suspension: PlayExecutionSuspension;
|
|
423
428
|
logs: string[];
|
|
429
|
+
/** Search-only context aligned to logs; omitted when every entry is null. */
|
|
430
|
+
logContexts?: Array<PlayLogContext | null>;
|
|
424
431
|
stats: Record<string, unknown>;
|
|
425
432
|
steps: PlayStep[];
|
|
426
433
|
checkpoint: PlayCheckpoint;
|
|
@@ -435,6 +442,8 @@ export type PlayRunnerResult =
|
|
|
435
442
|
error: string;
|
|
436
443
|
errors?: PlayRunFailureDetails[];
|
|
437
444
|
logs: string[];
|
|
445
|
+
/** Search-only context aligned to logs; omitted when every entry is null. */
|
|
446
|
+
logContexts?: Array<PlayLogContext | null>;
|
|
438
447
|
stats: Record<string, unknown>;
|
|
439
448
|
steps: PlayStep[];
|
|
440
449
|
checkpoint?: PlayCheckpoint | null;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PlayRunLedgerIngressEvent } from './run-ledger';
|
|
2
|
+
import type { PlayLogContext } from './log-context';
|
|
2
3
|
import type { RunLedgerProjectionChunk } from './run-ledger-projection-contract';
|
|
3
4
|
|
|
4
5
|
const APP_RUNTIME_LOG_EVENT_MAX_JSON_BYTES = 128 * 1024;
|
|
@@ -26,6 +27,25 @@ function splitRunEventForAppRuntime(
|
|
|
26
27
|
? rawLines.filter((line): line is string => typeof line === 'string')
|
|
27
28
|
: [];
|
|
28
29
|
if (eventType !== 'log.appended' || lines.length === 0) return [event];
|
|
30
|
+
const payloadOwnsLineContexts =
|
|
31
|
+
payload !== null && Object.hasOwn(payload, 'lineContexts');
|
|
32
|
+
const eventOwnsLineContexts = Object.hasOwn(event, 'lineContexts');
|
|
33
|
+
if (payloadOwnsLineContexts && eventOwnsLineContexts) {
|
|
34
|
+
throw new TypeError('Run log contexts must have one event owner.');
|
|
35
|
+
}
|
|
36
|
+
const rawLineContexts = payloadOwnsLineContexts
|
|
37
|
+
? payload?.lineContexts
|
|
38
|
+
: event.lineContexts;
|
|
39
|
+
const hasLineContexts = payloadOwnsLineContexts || eventOwnsLineContexts;
|
|
40
|
+
if (
|
|
41
|
+
hasLineContexts &&
|
|
42
|
+
(!Array.isArray(rawLineContexts) || rawLineContexts.length !== lines.length)
|
|
43
|
+
) {
|
|
44
|
+
throw new TypeError('Run log contexts must align with their lines.');
|
|
45
|
+
}
|
|
46
|
+
const lineContexts = hasLineContexts
|
|
47
|
+
? (rawLineContexts as Array<PlayLogContext | null>)
|
|
48
|
+
: null;
|
|
29
49
|
const payloadChannelOffset =
|
|
30
50
|
typeof payload?.channelOffset === 'number'
|
|
31
51
|
? payload.channelOffset
|
|
@@ -33,7 +53,11 @@ function splitRunEventForAppRuntime(
|
|
|
33
53
|
const eventChannelOffset =
|
|
34
54
|
typeof event.channelOffset === 'number' ? event.channelOffset : undefined;
|
|
35
55
|
const split: PlayRunLedgerIngressEvent[] = [];
|
|
36
|
-
const {
|
|
56
|
+
const {
|
|
57
|
+
lines: _outerLines,
|
|
58
|
+
lineContexts: _outerLineContexts,
|
|
59
|
+
...eventWithoutOuterLines
|
|
60
|
+
} = event;
|
|
37
61
|
let index = 0;
|
|
38
62
|
while (index < lines.length) {
|
|
39
63
|
const start = index;
|
|
@@ -52,14 +76,25 @@ function splitRunEventForAppRuntime(
|
|
|
52
76
|
index += 1;
|
|
53
77
|
}
|
|
54
78
|
const batchLines = lines.slice(start, index);
|
|
79
|
+
const batchLineContexts = lineContexts?.slice(start, index);
|
|
55
80
|
split.push(
|
|
56
81
|
payload
|
|
57
82
|
? {
|
|
58
83
|
...eventWithoutOuterLines,
|
|
84
|
+
...(eventOwnsLineContexts && batchLineContexts
|
|
85
|
+
? { lineContexts: batchLineContexts }
|
|
86
|
+
: {}),
|
|
59
87
|
...(payloadOwnsLines ? {} : { lines: batchLines }),
|
|
60
88
|
payload: {
|
|
61
|
-
...
|
|
89
|
+
...Object.fromEntries(
|
|
90
|
+
Object.entries(payload).filter(
|
|
91
|
+
([key]) => key !== 'lineContexts',
|
|
92
|
+
),
|
|
93
|
+
),
|
|
62
94
|
...(payloadOwnsLines ? { lines: batchLines } : {}),
|
|
95
|
+
...(payloadOwnsLineContexts && batchLineContexts
|
|
96
|
+
? { lineContexts: batchLineContexts }
|
|
97
|
+
: {}),
|
|
63
98
|
...(payloadChannelOffset === undefined
|
|
64
99
|
? {}
|
|
65
100
|
: { channelOffset: payloadChannelOffset + start }),
|
|
@@ -69,8 +104,9 @@ function splitRunEventForAppRuntime(
|
|
|
69
104
|
: { channelOffset: eventChannelOffset + start }),
|
|
70
105
|
}
|
|
71
106
|
: {
|
|
72
|
-
...
|
|
107
|
+
...eventWithoutOuterLines,
|
|
73
108
|
lines: batchLines,
|
|
109
|
+
...(batchLineContexts ? { lineContexts: batchLineContexts } : {}),
|
|
74
110
|
...(eventChannelOffset === undefined
|
|
75
111
|
? {}
|
|
76
112
|
: { channelOffset: eventChannelOffset + start }),
|
|
@@ -81,7 +117,7 @@ function splitRunEventForAppRuntime(
|
|
|
81
117
|
}
|
|
82
118
|
|
|
83
119
|
/**
|
|
84
|
-
* Reproduce the exact event and byte boundaries used by the App Runtime API.
|
|
120
|
+
* Reproduce the exact event, KV and byte boundaries used by the App Runtime API.
|
|
85
121
|
* The outbox lease verifier uses this same function to validate each chunk
|
|
86
122
|
* against its canonical event prefix before accepting a projection.
|
|
87
123
|
*/
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
type PlayDatasetBornFrom,
|
|
22
22
|
} from './cell-provenance';
|
|
23
23
|
import { isLedgerTerminalResultRef } from './output-size-limits';
|
|
24
|
+
import type { PlayLogContext } from './log-context';
|
|
24
25
|
|
|
25
26
|
export type PlayRunLedgerStatus =
|
|
26
27
|
| 'queued'
|
|
@@ -294,6 +295,8 @@ export type PlayRunLedgerEvent =
|
|
|
294
295
|
| (PlayRunLedgerBaseEvent & {
|
|
295
296
|
type: 'log.appended';
|
|
296
297
|
lines: string[];
|
|
298
|
+
/** Search-only context aligned to `lines`; stripped before Convex. */
|
|
299
|
+
lineContexts?: Array<PlayLogContext | null>;
|
|
297
300
|
/**
|
|
298
301
|
* Absolute per-run sequence (1-based) of `lines[0]`. Assigned by Run
|
|
299
302
|
* Log Stream ingestion in Convex only; producers never set it.
|
|
@@ -402,6 +405,8 @@ export type PlayRunLedgerStatusPatch = {
|
|
|
402
405
|
runtimeBackend?: string | null;
|
|
403
406
|
lastCheckpointAt?: number | null;
|
|
404
407
|
liveLogs?: readonly string[] | null;
|
|
408
|
+
/** Search-only context aligned to `liveLogs`; stripped before Convex. */
|
|
409
|
+
liveLogContexts?: readonly (PlayLogContext | null)[] | null;
|
|
405
410
|
/**
|
|
406
411
|
* Positional cursor for `liveLogs[0]` on this producer's log channel (the
|
|
407
412
|
* count of lines emitted before it). Computed by the producer-side cursor
|
|
@@ -2064,9 +2069,20 @@ function terminalEventTypeForStatus(
|
|
|
2064
2069
|
*/
|
|
2065
2070
|
export function slicePositionalLogLines(input: {
|
|
2066
2071
|
bufferLines: readonly string[];
|
|
2072
|
+
bufferContexts?: readonly (PlayLogContext | null)[];
|
|
2067
2073
|
bufferTotalCount: number;
|
|
2068
2074
|
sentCount: number;
|
|
2069
|
-
}): {
|
|
2075
|
+
}): {
|
|
2076
|
+
lines: string[];
|
|
2077
|
+
channelOffset: number;
|
|
2078
|
+
lineContexts?: Array<PlayLogContext | null>;
|
|
2079
|
+
} | null {
|
|
2080
|
+
if (
|
|
2081
|
+
input.bufferContexts !== undefined &&
|
|
2082
|
+
input.bufferContexts.length !== input.bufferLines.length
|
|
2083
|
+
) {
|
|
2084
|
+
throw new TypeError('Run log contexts must align with the retained lines.');
|
|
2085
|
+
}
|
|
2070
2086
|
const total = Math.max(input.bufferTotalCount, input.bufferLines.length);
|
|
2071
2087
|
const sent = Math.max(0, input.sentCount);
|
|
2072
2088
|
if (total <= sent) {
|
|
@@ -2078,7 +2094,13 @@ export function slicePositionalLogLines(input: {
|
|
|
2078
2094
|
if (lines.length === 0) {
|
|
2079
2095
|
return null;
|
|
2080
2096
|
}
|
|
2081
|
-
|
|
2097
|
+
const start = sendFromOffset - bufferFirstOffset;
|
|
2098
|
+
const lineContexts = input.bufferContexts?.slice(start);
|
|
2099
|
+
return {
|
|
2100
|
+
lines: [...lines],
|
|
2101
|
+
channelOffset: sendFromOffset,
|
|
2102
|
+
...(lineContexts?.some(Boolean) ? { lineContexts: [...lineContexts] } : {}),
|
|
2103
|
+
};
|
|
2082
2104
|
}
|
|
2083
2105
|
|
|
2084
2106
|
function terminalLogReplayChannelOffset(input: {
|
|
@@ -2105,6 +2127,7 @@ function terminalLogReplayChannelOffset(input: {
|
|
|
2105
2127
|
export function buildPlayRunLedgerEventsFromLogLines(input: {
|
|
2106
2128
|
runId: string;
|
|
2107
2129
|
lines: readonly string[];
|
|
2130
|
+
lineContexts?: readonly (PlayLogContext | null)[];
|
|
2108
2131
|
source?: PlayRunLedgerEventSource;
|
|
2109
2132
|
occurredAt?: number;
|
|
2110
2133
|
channelOffset?: number | null;
|
|
@@ -2120,9 +2143,19 @@ export function buildPlayRunLedgerEventsFromLogLines(input: {
|
|
|
2120
2143
|
Number.isFinite(input.channelOffset);
|
|
2121
2144
|
// Positional batches must keep one entry per emitted line — dropping a
|
|
2122
2145
|
// blank would shift every later line off its channel position.
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2146
|
+
if (
|
|
2147
|
+
input.lineContexts !== undefined &&
|
|
2148
|
+
input.lineContexts.length !== input.lines.length
|
|
2149
|
+
) {
|
|
2150
|
+
throw new TypeError('Run log contexts must align with their lines.');
|
|
2151
|
+
}
|
|
2152
|
+
const entries = input.lines
|
|
2153
|
+
.map((line, index) => ({
|
|
2154
|
+
line: positional ? line.trim() || '(blank log line)' : line.trim(),
|
|
2155
|
+
context: input.lineContexts?.[index] ?? null,
|
|
2156
|
+
}))
|
|
2157
|
+
.filter((entry) => positional || entry.line.length > 0);
|
|
2158
|
+
const newLines = entries.map((entry) => entry.line);
|
|
2126
2159
|
if (newLines.length === 0 || input.includeLogAppend === false) {
|
|
2127
2160
|
return [];
|
|
2128
2161
|
}
|
|
@@ -2138,6 +2171,15 @@ export function buildPlayRunLedgerEventsFromLogLines(input: {
|
|
|
2138
2171
|
source,
|
|
2139
2172
|
occurredAt,
|
|
2140
2173
|
lines: newLines.slice(index, index + MAX_LEDGER_LOG_LINES_PER_EVENT),
|
|
2174
|
+
...(entries
|
|
2175
|
+
.slice(index, index + MAX_LEDGER_LOG_LINES_PER_EVENT)
|
|
2176
|
+
.some((entry) => entry.context)
|
|
2177
|
+
? {
|
|
2178
|
+
lineContexts: entries
|
|
2179
|
+
.slice(index, index + MAX_LEDGER_LOG_LINES_PER_EVENT)
|
|
2180
|
+
.map((entry) => entry.context),
|
|
2181
|
+
}
|
|
2182
|
+
: {}),
|
|
2141
2183
|
...(positional ? { channelOffset: input.channelOffset! + index } : {}),
|
|
2142
2184
|
...(positional &&
|
|
2143
2185
|
typeof input.producerAttempt === 'number' &&
|
|
@@ -2165,6 +2207,7 @@ export function buildPlayRunLedgerEventsFromLogLines(input: {
|
|
|
2165
2207
|
export function buildTerminalLogReplayEvents(input: {
|
|
2166
2208
|
runId: string;
|
|
2167
2209
|
lines: readonly string[];
|
|
2210
|
+
lineContexts?: readonly (PlayLogContext | null)[];
|
|
2168
2211
|
source?: PlayRunLedgerEventSource;
|
|
2169
2212
|
occurredAt?: number;
|
|
2170
2213
|
liveLogTotalCount?: number;
|
|
@@ -2178,6 +2221,7 @@ export function buildTerminalLogReplayEvents(input: {
|
|
|
2178
2221
|
return buildPlayRunLedgerEventsFromLogLines({
|
|
2179
2222
|
runId: input.runId,
|
|
2180
2223
|
lines: input.lines,
|
|
2224
|
+
lineContexts: input.lineContexts,
|
|
2181
2225
|
source: 'coordinator',
|
|
2182
2226
|
occurredAt: input.occurredAt,
|
|
2183
2227
|
channelOffset,
|
|
@@ -2218,6 +2262,7 @@ export function buildPlayRunLedgerEventsFromStatusPatch(input: {
|
|
|
2218
2262
|
? buildPlayRunLedgerEventsFromLogLines({
|
|
2219
2263
|
runId: patch.runId,
|
|
2220
2264
|
lines: patch.liveLogs,
|
|
2265
|
+
lineContexts: patch.liveLogContexts ?? undefined,
|
|
2221
2266
|
source,
|
|
2222
2267
|
occurredAt: checkpointAt,
|
|
2223
2268
|
channelOffset: patch.liveLogsChannelOffset ?? null,
|
|
@@ -117,5 +117,7 @@ export function isRecoverableDatasetRowStatus(status: unknown): boolean {
|
|
|
117
117
|
export function datasetSummaryRowModeForRunStatus(
|
|
118
118
|
status: unknown,
|
|
119
119
|
): 'output' | 'all' {
|
|
120
|
-
|
|
120
|
+
// Completed is a run lifecycle, not proof that every row succeeded. Terminal
|
|
121
|
+
// summaries must count the same persisted successful AND failed rows as export.
|
|
122
|
+
return isTerminalPlayRunLifecycleStatus(status) ? 'all' : 'output';
|
|
121
123
|
}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
import type { PlayLogContext } from './log-context';
|
|
2
|
+
import { normalizePlayLogContext } from './log-context';
|
|
3
|
+
|
|
1
4
|
/** Limits apply before logs enter runner memory or a transport request. */
|
|
2
5
|
export const RUNNER_LOG_MAX_CHARS = 2_000;
|
|
3
6
|
export const RUNNER_LOG_MAX_BYTES = 5 * 1024 * 1024;
|
|
4
7
|
export const RUNNER_LOG_MAX_LINES = 25_000;
|
|
8
|
+
/** Keep structured metadata from multiplying the per-run text-log budget. */
|
|
9
|
+
export const RUNNER_LOG_CONTEXT_MAX_BYTES = 128 * 1024;
|
|
5
10
|
const TERMINAL_RESERVE_BYTES = 64 * 1024;
|
|
6
11
|
const TERMINAL_RESERVE_LINES = 32;
|
|
7
12
|
const encoder = new TextEncoder();
|
|
@@ -20,13 +25,24 @@ export function boundRunLogLine(line: string): string {
|
|
|
20
25
|
*/
|
|
21
26
|
export class RunLogBuffer {
|
|
22
27
|
readonly lines: string[] = [];
|
|
28
|
+
readonly contexts: Array<PlayLogContext | null> = [];
|
|
23
29
|
private bytes = 0;
|
|
30
|
+
private contextBytes = 0;
|
|
31
|
+
private omittedContexts = 0;
|
|
24
32
|
private omitted = 0;
|
|
25
33
|
private marked = false;
|
|
26
34
|
|
|
27
|
-
append(
|
|
35
|
+
append(
|
|
36
|
+
raw: string,
|
|
37
|
+
terminal = false,
|
|
38
|
+
context?: PlayLogContext | null,
|
|
39
|
+
): string[] {
|
|
28
40
|
const line = boundRunLogLine(raw);
|
|
29
41
|
const bytes = encoder.encode(line).byteLength;
|
|
42
|
+
const normalizedContext = normalizePlayLogContext(context);
|
|
43
|
+
const contextBytes = normalizedContext
|
|
44
|
+
? encoder.encode(JSON.stringify(normalizedContext)).byteLength
|
|
45
|
+
: 0;
|
|
30
46
|
const byteLimit =
|
|
31
47
|
RUNNER_LOG_MAX_BYTES - (terminal ? 0 : TERMINAL_RESERVE_BYTES);
|
|
32
48
|
const lineLimit =
|
|
@@ -41,13 +57,24 @@ export class RunLogBuffer {
|
|
|
41
57
|
);
|
|
42
58
|
}
|
|
43
59
|
this.lines.push(line);
|
|
60
|
+
const retainContext =
|
|
61
|
+
normalizedContext !== null &&
|
|
62
|
+
this.contextBytes + contextBytes <= RUNNER_LOG_CONTEXT_MAX_BYTES;
|
|
63
|
+
this.contexts.push(retainContext ? normalizedContext : null);
|
|
44
64
|
this.bytes += bytes;
|
|
65
|
+
if (retainContext) {
|
|
66
|
+
this.contextBytes += contextBytes;
|
|
67
|
+
} else if (normalizedContext !== null) {
|
|
68
|
+
this.omittedContexts += 1;
|
|
69
|
+
}
|
|
45
70
|
return [line];
|
|
46
71
|
}
|
|
47
72
|
|
|
48
73
|
snapshot() {
|
|
49
74
|
return {
|
|
50
75
|
retainedBytes: this.bytes,
|
|
76
|
+
retainedContextBytes: this.contextBytes,
|
|
77
|
+
omittedContexts: this.omittedContexts,
|
|
51
78
|
retainedLines: this.lines.length,
|
|
52
79
|
omittedLines: this.omitted,
|
|
53
80
|
};
|
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
DURABLE_INVOCATION_FENCE_ATTESTATION_VALUE,
|
|
36
36
|
} from '../durable-invocation-fence';
|
|
37
37
|
import type { PlayRunInputPayload } from '../play-input';
|
|
38
|
+
import type { PlayLogContext } from '../log-context';
|
|
38
39
|
import { playRunInputObjectView } from '../play-input';
|
|
39
40
|
import {
|
|
40
41
|
normalizePlayRunFailure,
|
|
@@ -1856,6 +1857,13 @@ function terminalLogsWithPhaseSummary(ctx: {
|
|
|
1856
1857
|
];
|
|
1857
1858
|
}
|
|
1858
1859
|
|
|
1860
|
+
function terminalLogContextsWithPhaseSummary(ctx: {
|
|
1861
|
+
getLogContexts(): Array<PlayLogContext | null>;
|
|
1862
|
+
}): Array<PlayLogContext | null> | undefined {
|
|
1863
|
+
const contexts = ctx.getLogContexts();
|
|
1864
|
+
return contexts.some(Boolean) ? [...contexts, null, null] : undefined;
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1859
1867
|
function rowOutcomesFromCheckpoint(checkpoint: {
|
|
1860
1868
|
mapFrames?: Record<
|
|
1861
1869
|
string,
|
|
@@ -2422,7 +2430,7 @@ async function run(
|
|
|
2422
2430
|
});
|
|
2423
2431
|
}
|
|
2424
2432
|
: undefined,
|
|
2425
|
-
onLog: (line) => {
|
|
2433
|
+
onLog: (line, context) => {
|
|
2426
2434
|
const timestampMatch = line.match(/^\[([^\]]+)\]/);
|
|
2427
2435
|
const timestamp = timestampMatch?.[1];
|
|
2428
2436
|
const at =
|
|
@@ -2435,6 +2443,7 @@ async function run(
|
|
|
2435
2443
|
at,
|
|
2436
2444
|
source: 'play',
|
|
2437
2445
|
line,
|
|
2446
|
+
...(context ? { context } : {}),
|
|
2438
2447
|
},
|
|
2439
2448
|
options,
|
|
2440
2449
|
);
|
|
@@ -3060,6 +3069,7 @@ async function run(
|
|
|
3060
3069
|
error: runtimeLimitExceededCause(runtimeLimitSeconds),
|
|
3061
3070
|
errors: [runtimeLimitExceededFailure(runtimeLimitSeconds)],
|
|
3062
3071
|
logs: terminalLogsWithPhaseSummary(ctx),
|
|
3072
|
+
logContexts: terminalLogContextsWithPhaseSummary(ctx),
|
|
3063
3073
|
stats: runtimeStats(),
|
|
3064
3074
|
steps: ctx.getSteps(),
|
|
3065
3075
|
tableNamespace: null,
|
|
@@ -3303,6 +3313,7 @@ async function run(
|
|
|
3303
3313
|
: {}),
|
|
3304
3314
|
outputRowCount,
|
|
3305
3315
|
logs,
|
|
3316
|
+
logContexts: terminalLogContextsWithPhaseSummary(ctx),
|
|
3306
3317
|
...(runtimeDiagnostics.length > 0 ? { runtimeDiagnostics } : {}),
|
|
3307
3318
|
...(rowOutcomes ? { rowOutcomes } : {}),
|
|
3308
3319
|
stats: runtimeStats(),
|
|
@@ -3357,6 +3368,7 @@ async function run(
|
|
|
3357
3368
|
status: 'suspended',
|
|
3358
3369
|
suspension: error.suspension,
|
|
3359
3370
|
logs: terminalLogsWithPhaseSummary(ctx),
|
|
3371
|
+
logContexts: terminalLogContextsWithPhaseSummary(ctx),
|
|
3360
3372
|
stats: runtimeStats(),
|
|
3361
3373
|
steps: ctx.getSteps(),
|
|
3362
3374
|
checkpoint: ctx.getCheckpoint(),
|
|
@@ -3392,6 +3404,7 @@ async function run(
|
|
|
3392
3404
|
error: formatRunnerFailure(error),
|
|
3393
3405
|
errors: [failure],
|
|
3394
3406
|
logs: terminalLogsWithPhaseSummary(ctx),
|
|
3407
|
+
logContexts: terminalLogContextsWithPhaseSummary(ctx),
|
|
3395
3408
|
...(rowOutcomes ? { rowOutcomes } : {}),
|
|
3396
3409
|
stats: runtimeStats(),
|
|
3397
3410
|
steps: ctx.getSteps(),
|