deepline 0.3.289 → 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.
- 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 +41 -11
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +317 -63
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +10 -2
- package/dist/bundling-sources/shared_libs/play-runtime/fixture-behavior.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/rate-state-backend.ts +3 -0
- package/dist/bundling-sources/shared_libs/play-runtime/launch-identity.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/log-context.ts +36 -0
- package/dist/bundling-sources/shared_libs/play-runtime/log-provenance.ts +19 -1
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/worker-terminal-projection.ts +6 -5
- package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-batching-registry.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +12 -1
- package/dist/bundling-sources/shared_libs/play-runtime/run-event-catalog.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger-event-transport.ts +74 -33
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +73 -3
- package/dist/bundling-sources/shared_libs/play-runtime/run-log-buffer.ts +39 -2
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +72 -9
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-progress-push.ts +2 -3
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-terminal-push.ts +77 -10
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-pool-options.ts +145 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +17 -9
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-shared.ts +8 -139
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-pool.ts +19 -5
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-progress.ts +176 -18
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-state.ts +7 -5
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +1404 -478
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/receipt-gateway.ts +55 -25
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-dispatch-authority.ts +97 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-outbox-recovery.ts +34 -15
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-event-search.ts +558 -57
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-receipt-value-storage.ts +10 -1
- package/dist/bundling-sources/shared_libs/play-runtime/step-progress.ts +6 -1
- package/dist/bundling-sources/shared_libs/play-runtime/test-admission-batching.ts +103 -0
- 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/play-runtime/zerobounce-batching.ts +75 -28
- package/dist/bundling-sources/shared_libs/plays/tool-result-types.ts +2 -0
- package/dist/cli/index.js +195 -582
- package/dist/cli/index.mjs +195 -582
- 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 +14 -5
- package/dist/index.mjs +14 -5
- package/dist/install-integrity.json +7 -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
|
@@ -17,6 +17,9 @@ export const DEFAULT_ADVISORY_PROVIDER_MAX_RPS = 100;
|
|
|
17
17
|
/** Cold-start rate for an unknown provider bucket shared across a fleet key. */
|
|
18
18
|
export const DEFAULT_UNKNOWN_PROVIDER_COLD_START_RPS = 10;
|
|
19
19
|
|
|
20
|
+
/** Learned-rate floor, independent of the unknown provider's starting guess. */
|
|
21
|
+
export const DEFAULT_UNKNOWN_PROVIDER_MIN_RPS = 2;
|
|
22
|
+
|
|
20
23
|
/** AIMD state cooldown cap; provider Retry-After holds remain uncapped. */
|
|
21
24
|
export const PROVIDER_AIMD_COOLDOWN_CAP_MS = 45 * 60 * 1_000;
|
|
22
25
|
|
|
@@ -52,6 +52,7 @@ export const PLAY_LAUNCH_ENVELOPE_FIELD_ROLES: {
|
|
|
52
52
|
playId: 'detail',
|
|
53
53
|
schedulerSchema: 'detail',
|
|
54
54
|
requiresRunCreatedProjection: 'detail',
|
|
55
|
+
runtimeLogIdentity: 'detail',
|
|
55
56
|
runtimeArtifact: 'detail',
|
|
56
57
|
runtimeProtocolVersion: 'detail',
|
|
57
58
|
staticPipeline: 'detail',
|
|
@@ -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,
|
|
@@ -117,6 +117,23 @@ export const LOG_PROVENANCE_POLICY: Record<
|
|
|
117
117
|
receipt: { watch: false, ui: false, debug: true },
|
|
118
118
|
};
|
|
119
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Shared retention rule for the detached runner's positional log channel.
|
|
122
|
+
* Sender offsets and gateway persistence must count the same events. Host
|
|
123
|
+
* lifecycle output stays in worker logs; bounded performance markers also
|
|
124
|
+
* reach the durable stream for dispatcher throughput diagnostics.
|
|
125
|
+
*/
|
|
126
|
+
export function isDurableDirectRunnerLog(event: {
|
|
127
|
+
source?: unknown;
|
|
128
|
+
line?: unknown;
|
|
129
|
+
}): boolean {
|
|
130
|
+
return (
|
|
131
|
+
typeof event.line === 'string' &&
|
|
132
|
+
event.line.length > 0 &&
|
|
133
|
+
(event.source !== 'worker' || event.line.startsWith('[perf-trace] '))
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
120
137
|
/** True when a line of the given provenance should render on the surface. */
|
|
121
138
|
export function logProvenanceReaches(
|
|
122
139
|
provenance: LogProvenance,
|
|
@@ -324,7 +341,8 @@ export function classifyLegacyLogLine(line: string): LogProvenance {
|
|
|
324
341
|
|
|
325
342
|
// Scheduler / sandbox / worker internals.
|
|
326
343
|
if (
|
|
327
|
-
/^\[perf\]
|
|
344
|
+
/^\[perf\]/i.test(message) ||
|
|
345
|
+
/^\[runtime\.(?:phase_summary|logs|log_transport)\]/i.test(message) ||
|
|
328
346
|
/\[worker\] picked up run\b/.test(message) ||
|
|
329
347
|
/\[worker\] heartbeat\b/.test(message) ||
|
|
330
348
|
/\[worker\] progress completedRows=\d+ totalRows=\d+ rowUpdates=\d+/.test(
|
|
@@ -39,10 +39,11 @@ export type PlayRunAttemptWorkerTerminalProjectionPorts = {
|
|
|
39
39
|
};
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* winner only; it never emits a second projection
|
|
42
|
+
* Request delivery of the already committed terminal before awaited
|
|
43
|
+
* reconciliation or cleanup can delay it. Progress finalization still follows
|
|
44
|
+
* durable-truth reconciliation and failed-attempt lease release. A stale
|
|
45
|
+
* terminal fence observes the winner only; it never emits a second projection
|
|
46
|
+
* sequence.
|
|
46
47
|
*/
|
|
47
48
|
export function createPlayRunAttemptWorkerTerminalProjection(
|
|
48
49
|
ports: PlayRunAttemptWorkerTerminalProjectionPorts,
|
|
@@ -50,6 +51,7 @@ export function createPlayRunAttemptWorkerTerminalProjection(
|
|
|
50
51
|
return {
|
|
51
52
|
observeFenceLost: ports.observeFenceLost,
|
|
52
53
|
async projectCommitted(input) {
|
|
54
|
+
ports.requestTerminalProjection({ terminal: input.terminal });
|
|
53
55
|
await ports.reconcileTerminalTruth(input);
|
|
54
56
|
if (input.terminal.status === 'failed') {
|
|
55
57
|
await ports.releaseFailedAttempt({
|
|
@@ -58,7 +60,6 @@ export function createPlayRunAttemptWorkerTerminalProjection(
|
|
|
58
60
|
});
|
|
59
61
|
}
|
|
60
62
|
ports.queueProgressFinalization(input);
|
|
61
|
-
ports.requestTerminalProjection({ terminal: input.terminal });
|
|
62
63
|
},
|
|
63
64
|
};
|
|
64
65
|
}
|
|
@@ -6,6 +6,7 @@ import { fullenrichBatchStrategies } from './fullenrich-batching';
|
|
|
6
6
|
import { opensosdataBatchStrategies } from './opensosdata-batching';
|
|
7
7
|
import { zerobounceBatchStrategies } from './zerobounce-batching';
|
|
8
8
|
import { testAsyncBatchStrategies } from './test-async-batching';
|
|
9
|
+
import { testAdmissionBatchStrategies } from './test-admission-batching';
|
|
9
10
|
|
|
10
11
|
export const PLAY_RUNTIME_BATCH_OPERATION_REGISTRY: Record<
|
|
11
12
|
string,
|
|
@@ -17,6 +18,7 @@ export const PLAY_RUNTIME_BATCH_OPERATION_REGISTRY: Record<
|
|
|
17
18
|
...fullenrichBatchStrategies,
|
|
18
19
|
...opensosdataBatchStrategies,
|
|
19
20
|
...testAsyncBatchStrategies,
|
|
21
|
+
...testAdmissionBatchStrategies,
|
|
20
22
|
...zerobounceBatchStrategies,
|
|
21
23
|
};
|
|
22
24
|
|
|
@@ -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';
|
|
@@ -185,6 +185,8 @@ export type PlayRunnerBudgetChargeResult = {
|
|
|
185
185
|
};
|
|
186
186
|
|
|
187
187
|
export interface PlayRunnerContextConfig extends RetainedPipelineCarrier {
|
|
188
|
+
/** Reader-first logging cutover, frozen at admission; absent stays legacy. */
|
|
189
|
+
runtimeLogIdentity?: 'origin';
|
|
188
190
|
executorToken?: string;
|
|
189
191
|
baseUrl?: string;
|
|
190
192
|
/**
|
|
@@ -320,8 +322,11 @@ export type PlayRunnerLogEvent = {
|
|
|
320
322
|
at: string;
|
|
321
323
|
source: PlayLiveEventSource;
|
|
322
324
|
line: string;
|
|
325
|
+
/** Origin identity, preserved across live and terminal transports. */
|
|
326
|
+
entryId?: string;
|
|
323
327
|
/** Search-only context; omitted before the compatible Convex projection. */
|
|
324
328
|
context?: PlayLogContext;
|
|
329
|
+
metadata?: PlayLogMetadata;
|
|
325
330
|
};
|
|
326
331
|
|
|
327
332
|
export type PlayRunnerEvent =
|
|
@@ -411,8 +416,10 @@ export type PlayRunnerResult =
|
|
|
411
416
|
outputRowCount?: number;
|
|
412
417
|
rowOutcomes?: PlayRunnerRowOutcomeSummary;
|
|
413
418
|
logs: string[];
|
|
419
|
+
logIds?: string[];
|
|
414
420
|
/** Search-only context aligned to logs; omitted when every entry is null. */
|
|
415
421
|
logContexts?: Array<PlayLogContext | null>;
|
|
422
|
+
logMetadata?: Array<PlayLogMetadata | null>;
|
|
416
423
|
stats: Record<string, unknown>;
|
|
417
424
|
steps: PlayStep[];
|
|
418
425
|
checkpoint: PlayCheckpoint;
|
|
@@ -426,8 +433,10 @@ export type PlayRunnerResult =
|
|
|
426
433
|
status: 'suspended';
|
|
427
434
|
suspension: PlayExecutionSuspension;
|
|
428
435
|
logs: string[];
|
|
436
|
+
logIds?: string[];
|
|
429
437
|
/** Search-only context aligned to logs; omitted when every entry is null. */
|
|
430
438
|
logContexts?: Array<PlayLogContext | null>;
|
|
439
|
+
logMetadata?: Array<PlayLogMetadata | null>;
|
|
431
440
|
stats: Record<string, unknown>;
|
|
432
441
|
steps: PlayStep[];
|
|
433
442
|
checkpoint: PlayCheckpoint;
|
|
@@ -442,8 +451,10 @@ export type PlayRunnerResult =
|
|
|
442
451
|
error: string;
|
|
443
452
|
errors?: PlayRunFailureDetails[];
|
|
444
453
|
logs: string[];
|
|
454
|
+
logIds?: string[];
|
|
445
455
|
/** Search-only context aligned to logs; omitted when every entry is null. */
|
|
446
456
|
logContexts?: Array<PlayLogContext | null>;
|
|
457
|
+
logMetadata?: Array<PlayLogMetadata | null>;
|
|
447
458
|
stats: Record<string, unknown>;
|
|
448
459
|
steps: PlayStep[];
|
|
449
460
|
checkpoint?: PlayCheckpoint | null;
|
|
@@ -387,6 +387,7 @@ export const RUN_SCHEDULER_OUTBOX_EVENT_TYPE = {
|
|
|
387
387
|
runCancelled: RUN_EVENT_KIND.runCancelled,
|
|
388
388
|
runEvents: 'run.events',
|
|
389
389
|
runnerLogs: 'runner.logs',
|
|
390
|
+
receiptEvent: 'receipt.event',
|
|
390
391
|
runWaitTimeout: 'run.wait_timeout',
|
|
391
392
|
} as const;
|
|
392
393
|
|
|
@@ -549,6 +550,7 @@ export const RUN_SCHEDULER_OUTBOX_EVENT_TYPE_SQL_LITERAL = {
|
|
|
549
550
|
runCancelled: sqlStringLiteral(RUN_SCHEDULER_OUTBOX_EVENT_TYPE.runCancelled),
|
|
550
551
|
runEvents: sqlStringLiteral(RUN_SCHEDULER_OUTBOX_EVENT_TYPE.runEvents),
|
|
551
552
|
runnerLogs: sqlStringLiteral(RUN_SCHEDULER_OUTBOX_EVENT_TYPE.runnerLogs),
|
|
553
|
+
receiptEvent: sqlStringLiteral(RUN_SCHEDULER_OUTBOX_EVENT_TYPE.receiptEvent),
|
|
552
554
|
runWaitTimeout: sqlStringLiteral(
|
|
553
555
|
RUN_SCHEDULER_OUTBOX_EVENT_TYPE.runWaitTimeout,
|
|
554
556
|
),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { normalizeRunLogIds } from './run-log-buffer';
|
|
1
2
|
import { RUN_EVENT_KIND } from './run-event-catalog';
|
|
2
3
|
import type { PlayRunLedgerIngressEvent } from './run-ledger';
|
|
3
|
-
import type { PlayLogContext } from './log-context';
|
|
4
4
|
import type { RunLedgerProjectionChunk } from './run-ledger-projection-contract';
|
|
5
5
|
|
|
6
6
|
const APP_RUNTIME_LOG_EVENT_MAX_JSON_BYTES = 128 * 1024;
|
|
@@ -29,25 +29,35 @@ function splitRunEventForAppRuntime(
|
|
|
29
29
|
: [];
|
|
30
30
|
if (eventType !== RUN_EVENT_KIND.logAppended || lines.length === 0)
|
|
31
31
|
return [event];
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
? payload?.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
+
});
|
|
53
|
+
const payloadOwnsLineIds =
|
|
54
|
+
payload !== null && Object.hasOwn(payload, 'lineIds');
|
|
55
|
+
if (payloadOwnsLineIds && Object.hasOwn(event, 'lineIds'))
|
|
56
|
+
throw new TypeError('Run log identities must have one event owner.');
|
|
57
|
+
const lineIds = normalizeRunLogIds(
|
|
58
|
+
payloadOwnsLineIds ? payload?.lineIds : event.lineIds,
|
|
59
|
+
lines,
|
|
60
|
+
);
|
|
51
61
|
const payloadChannelOffset =
|
|
52
62
|
typeof payload?.channelOffset === 'number'
|
|
53
63
|
? payload.channelOffset
|
|
@@ -58,6 +68,8 @@ function splitRunEventForAppRuntime(
|
|
|
58
68
|
const {
|
|
59
69
|
lines: _outerLines,
|
|
60
70
|
lineContexts: _outerLineContexts,
|
|
71
|
+
lineMetadata: _outerLineMetadata,
|
|
72
|
+
lineIds: _outerLineIds,
|
|
61
73
|
...eventWithoutOuterLines
|
|
62
74
|
} = event;
|
|
63
75
|
let index = 0;
|
|
@@ -65,37 +77,65 @@ function splitRunEventForAppRuntime(
|
|
|
65
77
|
const start = index;
|
|
66
78
|
let bytes = 2;
|
|
67
79
|
while (index < lines.length) {
|
|
68
|
-
const lineBytes =
|
|
69
|
-
JSON.stringify(lines[index]!)
|
|
70
|
-
|
|
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;
|
|
71
94
|
if (
|
|
72
95
|
index > start &&
|
|
73
|
-
bytes + lineBytes + 1 >
|
|
96
|
+
bytes + lineBytes + metadataBytes + 1 >
|
|
97
|
+
APP_RUNTIME_LOG_EVENT_MAX_JSON_BYTES
|
|
74
98
|
) {
|
|
75
99
|
break;
|
|
76
100
|
}
|
|
77
|
-
bytes += lineBytes + 1;
|
|
101
|
+
bytes += lineBytes + metadataBytes + 1;
|
|
78
102
|
index += 1;
|
|
79
103
|
}
|
|
80
104
|
const batchLines = lines.slice(start, index);
|
|
81
|
-
const
|
|
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
|
+
);
|
|
82
116
|
split.push(
|
|
83
117
|
payload
|
|
84
118
|
? {
|
|
85
119
|
...eventWithoutOuterLines,
|
|
86
|
-
...
|
|
87
|
-
? { lineContexts: batchLineContexts }
|
|
88
|
-
: {}),
|
|
120
|
+
...eventFields,
|
|
89
121
|
...(payloadOwnsLines ? {} : { lines: batchLines }),
|
|
122
|
+
...(!payloadOwnsLineIds && batchLineIds
|
|
123
|
+
? { lineIds: batchLineIds }
|
|
124
|
+
: {}),
|
|
90
125
|
payload: {
|
|
91
126
|
...Object.fromEntries(
|
|
92
127
|
Object.entries(payload).filter(
|
|
93
|
-
([key]) =>
|
|
128
|
+
([key]) =>
|
|
129
|
+
key !== 'lineIds' &&
|
|
130
|
+
!alignedFields.includes(
|
|
131
|
+
key as (typeof alignedFields)[number],
|
|
132
|
+
),
|
|
94
133
|
),
|
|
95
134
|
),
|
|
96
135
|
...(payloadOwnsLines ? { lines: batchLines } : {}),
|
|
97
|
-
...
|
|
98
|
-
|
|
136
|
+
...payloadFields,
|
|
137
|
+
...(payloadOwnsLineIds && batchLineIds
|
|
138
|
+
? { lineIds: batchLineIds }
|
|
99
139
|
: {}),
|
|
100
140
|
...(payloadChannelOffset === undefined
|
|
101
141
|
? {}
|
|
@@ -108,7 +148,8 @@ function splitRunEventForAppRuntime(
|
|
|
108
148
|
: {
|
|
109
149
|
...eventWithoutOuterLines,
|
|
110
150
|
lines: batchLines,
|
|
111
|
-
...
|
|
151
|
+
...eventFields,
|
|
152
|
+
...(batchLineIds ? { lineIds: batchLineIds } : {}),
|
|
112
153
|
...(eventChannelOffset === undefined
|
|
113
154
|
? {}
|
|
114
155
|
: { channelOffset: eventChannelOffset + start }),
|
|
@@ -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,8 @@ 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>;
|
|
326
|
+
lineIds?: Array<string | null>;
|
|
323
327
|
/**
|
|
324
328
|
* Absolute per-run sequence (1-based) of `lines[0]`. Assigned by Run
|
|
325
329
|
* Log Stream ingestion in Convex only; producers never set it.
|
|
@@ -430,6 +434,8 @@ export type PlayRunLedgerStatusPatch = {
|
|
|
430
434
|
liveLogs?: readonly string[] | null;
|
|
431
435
|
/** Search-only context aligned to `liveLogs`; stripped before Convex. */
|
|
432
436
|
liveLogContexts?: readonly (PlayLogContext | null)[] | null;
|
|
437
|
+
liveLogMetadata?: readonly (PlayLogMetadata | null)[] | null;
|
|
438
|
+
liveLogIds?: readonly (string | null)[] | null;
|
|
433
439
|
/**
|
|
434
440
|
* Positional cursor for `liveLogs[0]` on this producer's log channel (the
|
|
435
441
|
* count of lines emitted before it). Computed by the producer-side cursor
|
|
@@ -1381,6 +1387,9 @@ function normalizeStepProgress(
|
|
|
1381
1387
|
...(optionalFiniteNumber(value.supersededRows) !== undefined
|
|
1382
1388
|
? { supersededRows: optionalFiniteNumber(value.supersededRows) }
|
|
1383
1389
|
: {}),
|
|
1390
|
+
...(optionalString(value.error)
|
|
1391
|
+
? { error: optionalString(value.error) }
|
|
1392
|
+
: {}),
|
|
1384
1393
|
...(optionalString(value.message)
|
|
1385
1394
|
? { message: optionalString(value.message) }
|
|
1386
1395
|
: {}),
|
|
@@ -2327,6 +2336,7 @@ function progressSignature(
|
|
|
2327
2336
|
completedRows: progress?.completedRows ?? null,
|
|
2328
2337
|
supersededRows: progress?.supersededRows ?? null,
|
|
2329
2338
|
message: progress?.message ?? null,
|
|
2339
|
+
error: progress?.error ?? null,
|
|
2330
2340
|
artifactTableNamespace: progress?.artifactTableNamespace ?? null,
|
|
2331
2341
|
startedAt: progress?.startedAt ?? null,
|
|
2332
2342
|
completedAt: progress?.completedAt ?? null,
|
|
@@ -2369,12 +2379,16 @@ function terminalEventTypeForStatus(
|
|
|
2369
2379
|
export function slicePositionalLogLines(input: {
|
|
2370
2380
|
bufferLines: readonly string[];
|
|
2371
2381
|
bufferContexts?: readonly (PlayLogContext | null)[];
|
|
2382
|
+
bufferMetadata?: readonly (PlayLogMetadata | null)[];
|
|
2383
|
+
bufferIds?: readonly (string | null)[];
|
|
2372
2384
|
bufferTotalCount: number;
|
|
2373
2385
|
sentCount: number;
|
|
2374
2386
|
}): {
|
|
2375
2387
|
lines: string[];
|
|
2376
2388
|
channelOffset: number;
|
|
2377
2389
|
lineContexts?: Array<PlayLogContext | null>;
|
|
2390
|
+
lineMetadata?: Array<PlayLogMetadata | null>;
|
|
2391
|
+
lineIds?: Array<string | null>;
|
|
2378
2392
|
} | null {
|
|
2379
2393
|
if (
|
|
2380
2394
|
input.bufferContexts !== undefined &&
|
|
@@ -2382,6 +2396,14 @@ export function slicePositionalLogLines(input: {
|
|
|
2382
2396
|
) {
|
|
2383
2397
|
throw new TypeError('Run log contexts must align with the retained lines.');
|
|
2384
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.');
|
|
2404
|
+
if (input.bufferIds && input.bufferIds.length !== input.bufferLines.length) {
|
|
2405
|
+
throw new TypeError('Run log identities must align with retained lines.');
|
|
2406
|
+
}
|
|
2385
2407
|
const total = Math.max(input.bufferTotalCount, input.bufferLines.length);
|
|
2386
2408
|
const sent = Math.max(0, input.sentCount);
|
|
2387
2409
|
if (total <= sent) {
|
|
@@ -2395,10 +2417,13 @@ export function slicePositionalLogLines(input: {
|
|
|
2395
2417
|
}
|
|
2396
2418
|
const start = sendFromOffset - bufferFirstOffset;
|
|
2397
2419
|
const lineContexts = input.bufferContexts?.slice(start);
|
|
2420
|
+
const lineMetadata = input.bufferMetadata?.slice(start);
|
|
2398
2421
|
return {
|
|
2399
2422
|
lines: [...lines],
|
|
2400
2423
|
channelOffset: sendFromOffset,
|
|
2424
|
+
...(input.bufferIds ? { lineIds: input.bufferIds.slice(start) } : {}),
|
|
2401
2425
|
...(lineContexts?.some(Boolean) ? { lineContexts: [...lineContexts] } : {}),
|
|
2426
|
+
...(lineMetadata?.some(Boolean) ? { lineMetadata: [...lineMetadata] } : {}),
|
|
2402
2427
|
};
|
|
2403
2428
|
}
|
|
2404
2429
|
|
|
@@ -2427,6 +2452,8 @@ export function buildPlayRunLedgerEventsFromLogLines(input: {
|
|
|
2427
2452
|
runId: string;
|
|
2428
2453
|
lines: readonly string[];
|
|
2429
2454
|
lineContexts?: readonly (PlayLogContext | null)[];
|
|
2455
|
+
lineMetadata?: readonly (PlayLogMetadata | null)[];
|
|
2456
|
+
lineIds?: readonly (string | null)[];
|
|
2430
2457
|
source?: PlayRunLedgerEventSource;
|
|
2431
2458
|
occurredAt?: number;
|
|
2432
2459
|
channelOffset?: number | null;
|
|
@@ -2448,10 +2475,20 @@ export function buildPlayRunLedgerEventsFromLogLines(input: {
|
|
|
2448
2475
|
) {
|
|
2449
2476
|
throw new TypeError('Run log contexts must align with their lines.');
|
|
2450
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.');
|
|
2483
|
+
if (input.lineIds && input.lineIds.length !== input.lines.length) {
|
|
2484
|
+
throw new TypeError('Run log identities must align with their lines.');
|
|
2485
|
+
}
|
|
2451
2486
|
const entries = input.lines
|
|
2452
2487
|
.map((line, index) => ({
|
|
2453
2488
|
line: positional ? line.trim() || '(blank log line)' : line.trim(),
|
|
2454
2489
|
context: input.lineContexts?.[index] ?? null,
|
|
2490
|
+
metadata: input.lineMetadata?.[index] ?? null,
|
|
2491
|
+
entryId: input.lineIds?.[index] ?? null,
|
|
2455
2492
|
}))
|
|
2456
2493
|
.filter((entry) => positional || entry.line.length > 0);
|
|
2457
2494
|
const newLines = entries.map((entry) => entry.line);
|
|
@@ -2470,6 +2507,13 @@ export function buildPlayRunLedgerEventsFromLogLines(input: {
|
|
|
2470
2507
|
source,
|
|
2471
2508
|
occurredAt,
|
|
2472
2509
|
lines: newLines.slice(index, index + MAX_LEDGER_LOG_LINES_PER_EVENT),
|
|
2510
|
+
...(input.lineIds
|
|
2511
|
+
? {
|
|
2512
|
+
lineIds: entries
|
|
2513
|
+
.slice(index, index + MAX_LEDGER_LOG_LINES_PER_EVENT)
|
|
2514
|
+
.map((entry) => entry.entryId),
|
|
2515
|
+
}
|
|
2516
|
+
: {}),
|
|
2473
2517
|
...(entries
|
|
2474
2518
|
.slice(index, index + MAX_LEDGER_LOG_LINES_PER_EVENT)
|
|
2475
2519
|
.some((entry) => entry.context)
|
|
@@ -2479,6 +2523,15 @@ export function buildPlayRunLedgerEventsFromLogLines(input: {
|
|
|
2479
2523
|
.map((entry) => entry.context),
|
|
2480
2524
|
}
|
|
2481
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
|
+
: {}),
|
|
2482
2535
|
...(positional ? { channelOffset: input.channelOffset! + index } : {}),
|
|
2483
2536
|
...(positional &&
|
|
2484
2537
|
typeof input.producerAttempt === 'number' &&
|
|
@@ -2507,6 +2560,8 @@ export function buildTerminalLogReplayEvents(input: {
|
|
|
2507
2560
|
runId: string;
|
|
2508
2561
|
lines: readonly string[];
|
|
2509
2562
|
lineContexts?: readonly (PlayLogContext | null)[];
|
|
2563
|
+
lineMetadata?: readonly (PlayLogMetadata | null)[];
|
|
2564
|
+
lineIds?: readonly (string | null)[];
|
|
2510
2565
|
source?: PlayRunLedgerEventSource;
|
|
2511
2566
|
occurredAt?: number;
|
|
2512
2567
|
liveLogTotalCount?: number;
|
|
@@ -2521,6 +2576,8 @@ export function buildTerminalLogReplayEvents(input: {
|
|
|
2521
2576
|
runId: input.runId,
|
|
2522
2577
|
lines: input.lines,
|
|
2523
2578
|
lineContexts: input.lineContexts,
|
|
2579
|
+
lineMetadata: input.lineMetadata,
|
|
2580
|
+
lineIds: input.lineIds,
|
|
2524
2581
|
source: 'coordinator',
|
|
2525
2582
|
occurredAt: input.occurredAt,
|
|
2526
2583
|
channelOffset,
|
|
@@ -2562,6 +2619,8 @@ export function buildPlayRunLedgerEventsFromStatusPatch(input: {
|
|
|
2562
2619
|
runId: patch.runId,
|
|
2563
2620
|
lines: patch.liveLogs,
|
|
2564
2621
|
lineContexts: patch.liveLogContexts ?? undefined,
|
|
2622
|
+
lineMetadata: patch.liveLogMetadata ?? undefined,
|
|
2623
|
+
lineIds: patch.liveLogIds ?? undefined,
|
|
2565
2624
|
source,
|
|
2566
2625
|
occurredAt: checkpointAt,
|
|
2567
2626
|
channelOffset: patch.liveLogsChannelOffset ?? null,
|
|
@@ -2643,6 +2702,7 @@ export function buildPlayRunLedgerEventsFromStatusPatch(input: {
|
|
|
2643
2702
|
? { supersededRows: progress.supersededRows }
|
|
2644
2703
|
: {}),
|
|
2645
2704
|
...(progress.message !== undefined ? { message: progress.message } : {}),
|
|
2705
|
+
...(progress.error !== undefined ? { error: progress.error } : {}),
|
|
2646
2706
|
...(progress.artifactTableNamespace !== undefined
|
|
2647
2707
|
? { artifactTableNamespace: progress.artifactTableNamespace }
|
|
2648
2708
|
: {}),
|
|
@@ -2669,6 +2729,7 @@ export function buildPlayRunLedgerEventsFromStatusPatch(input: {
|
|
|
2669
2729
|
occurredAt: progress.updatedAt ?? checkpointAt,
|
|
2670
2730
|
stepId,
|
|
2671
2731
|
status:
|
|
2732
|
+
typeof progress.error === 'string' ||
|
|
2672
2733
|
typeof progress.nodeIo?.error === 'string'
|
|
2673
2734
|
? 'failed'
|
|
2674
2735
|
: typeof progress.completedAt === 'number'
|
|
@@ -2680,9 +2741,18 @@ export function buildPlayRunLedgerEventsFromStatusPatch(input: {
|
|
|
2680
2741
|
});
|
|
2681
2742
|
}
|
|
2682
2743
|
|
|
2683
|
-
|
|
2744
|
+
const stepError = progress.error ?? progress.nodeIo?.error;
|
|
2745
|
+
if (
|
|
2746
|
+
progress.completedAt &&
|
|
2747
|
+
(!previousStep?.completedAt ||
|
|
2748
|
+
(typeof stepError === 'string' && previousStep.status !== 'failed'))
|
|
2749
|
+
) {
|
|
2684
2750
|
events.push({
|
|
2685
|
-
type:
|
|
2751
|
+
type:
|
|
2752
|
+
typeof stepError === 'string'
|
|
2753
|
+
? RUN_EVENT_KIND.stepFailed
|
|
2754
|
+
: RUN_EVENT_KIND.stepCompleted,
|
|
2755
|
+
...(typeof stepError === 'string' ? { error: stepError } : {}),
|
|
2686
2756
|
runId: patch.runId,
|
|
2687
2757
|
source,
|
|
2688
2758
|
occurredAt: progress.completedAt,
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import type { PlayLogContext } from './log-context';
|
|
2
|
-
import {
|
|
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;
|
|
@@ -25,7 +28,10 @@ export function boundRunLogLine(line: string): string {
|
|
|
25
28
|
*/
|
|
26
29
|
export class RunLogBuffer {
|
|
27
30
|
readonly lines: string[] = [];
|
|
31
|
+
readonly ids: string[] = [];
|
|
32
|
+
private readonly origin = crypto.randomUUID();
|
|
28
33
|
readonly contexts: Array<PlayLogContext | null> = [];
|
|
34
|
+
readonly metadata: Array<PlayLogMetadata | null> = [];
|
|
29
35
|
private bytes = 0;
|
|
30
36
|
private contextBytes = 0;
|
|
31
37
|
private omittedContexts = 0;
|
|
@@ -36,10 +42,12 @@ export class RunLogBuffer {
|
|
|
36
42
|
raw: string,
|
|
37
43
|
terminal = false,
|
|
38
44
|
context?: PlayLogContext | null,
|
|
45
|
+
metadata?: PlayLogMetadata | null,
|
|
39
46
|
): string[] {
|
|
40
47
|
const line = boundRunLogLine(raw);
|
|
41
48
|
const bytes = encoder.encode(line).byteLength;
|
|
42
49
|
const normalizedContext = normalizePlayLogContext(context);
|
|
50
|
+
const normalizedMetadata = normalizePlayLogMetadata(metadata);
|
|
43
51
|
const contextBytes = normalizedContext
|
|
44
52
|
? encoder.encode(JSON.stringify(normalizedContext)).byteLength
|
|
45
53
|
: 0;
|
|
@@ -56,7 +64,9 @@ export class RunLogBuffer {
|
|
|
56
64
|
true,
|
|
57
65
|
);
|
|
58
66
|
}
|
|
67
|
+
this.ids.push(`${this.origin}:${this.lines.length}`);
|
|
59
68
|
this.lines.push(line);
|
|
69
|
+
this.metadata.push(normalizedMetadata);
|
|
60
70
|
const retainContext =
|
|
61
71
|
normalizedContext !== null &&
|
|
62
72
|
this.contextBytes + contextBytes <= RUNNER_LOG_CONTEXT_MAX_BYTES;
|
|
@@ -80,3 +90,30 @@ export class RunLogBuffer {
|
|
|
80
90
|
};
|
|
81
91
|
}
|
|
82
92
|
}
|
|
93
|
+
|
|
94
|
+
/** Optional origin identities: old producers omit them; never infer them from text. */
|
|
95
|
+
export function normalizeRunLogIds(
|
|
96
|
+
value: unknown,
|
|
97
|
+
lines: readonly unknown[],
|
|
98
|
+
): Array<string | null> | undefined {
|
|
99
|
+
if (value === undefined || value === null) return undefined;
|
|
100
|
+
if (
|
|
101
|
+
!Array.isArray(value) ||
|
|
102
|
+
value.length !== lines.length ||
|
|
103
|
+
value.some(
|
|
104
|
+
(id) =>
|
|
105
|
+
id !== null &&
|
|
106
|
+
(typeof id !== 'string' || !/^[A-Za-z0-9:_-]{1,128}$/.test(id)),
|
|
107
|
+
)
|
|
108
|
+
) {
|
|
109
|
+
throw new TypeError(
|
|
110
|
+
'Run log identities must be bounded strings aligned with their lines.',
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
const ids = value.filter((id): id is string => id !== null);
|
|
114
|
+
if (new Set(ids).size !== ids.length)
|
|
115
|
+
throw new TypeError(
|
|
116
|
+
'Run log identities must identify distinct occurrences.',
|
|
117
|
+
);
|
|
118
|
+
return value;
|
|
119
|
+
}
|