deepline 0.1.91 → 0.1.94
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/cli/index.js +4012 -705
- package/dist/cli/index.mjs +4028 -714
- package/dist/index.d.mts +232 -108
- package/dist/index.d.ts +232 -108
- package/dist/index.js +1145 -99
- package/dist/index.mjs +1134 -99
- package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +87 -20
- package/dist/repo/apps/play-runner-workers/src/entry.ts +75 -22
- package/dist/repo/sdk/src/client.ts +412 -40
- package/dist/repo/sdk/src/index.ts +1 -0
- package/dist/repo/sdk/src/play.ts +51 -0
- package/dist/repo/sdk/src/release.ts +2 -2
- package/dist/repo/sdk/src/runs/observe-transport.ts +481 -0
- package/dist/repo/sdk/src/stream-reconnect.ts +44 -0
- package/dist/repo/sdk/src/types.ts +10 -3
- package/dist/repo/shared_libs/play-runtime/email-status.ts +10 -36
- package/dist/repo/shared_libs/play-runtime/extractor-targets.ts +3 -3
- package/dist/repo/shared_libs/play-runtime/live-events.ts +217 -0
- package/dist/repo/shared_libs/play-runtime/run-ledger.ts +1074 -0
- package/dist/repo/shared_libs/play-runtime/run-snapshot-stream.ts +581 -0
- package/dist/repo/shared_libs/play-runtime/tool-result.ts +44 -0
- package/dist/repo/shared_libs/plays/secret-guardrails.ts +22 -11
- package/package.json +5 -2
|
@@ -3211,8 +3211,22 @@ function formatTailLogPart(value: unknown): string {
|
|
|
3211
3211
|
}
|
|
3212
3212
|
}
|
|
3213
3213
|
|
|
3214
|
+
// Operator-diagnostic console lines that carry the [deepline-run:] prefix but
|
|
3215
|
+
// are not user-facing run output. The console scrape fans run-prefixed lines
|
|
3216
|
+
// back into the run's durable Run Log Stream ('system' channel), so harness/
|
|
3217
|
+
// coordinator plumbing noise is filtered at ingestion, never at read time.
|
|
3218
|
+
// User play log lines (runner-event echoes) intentionally pass through.
|
|
3219
|
+
const OPERATOR_NOISE_LOG_PATTERNS: readonly RegExp[] = [
|
|
3220
|
+
/\[perf-trace\]/,
|
|
3221
|
+
/\[harness-probe\]/,
|
|
3222
|
+
/TenantWorkflow\.run entered/,
|
|
3223
|
+
/TenantWorkflow\.run threw/,
|
|
3224
|
+
/failed to forward runner perf trace/,
|
|
3225
|
+
/failed to forward TenantWorkflow\.run error/,
|
|
3226
|
+
];
|
|
3227
|
+
|
|
3214
3228
|
function parseRunLogLine(line: string): { runId: string; line: string } | null {
|
|
3215
|
-
if (
|
|
3229
|
+
if (OPERATOR_NOISE_LOG_PATTERNS.some((pattern) => pattern.test(line))) {
|
|
3216
3230
|
return null;
|
|
3217
3231
|
}
|
|
3218
3232
|
const prefixed = line.match(RUN_LOG_PREFIX_RE);
|
|
@@ -3661,26 +3675,79 @@ async function handleWorkflowRoute(input: {
|
|
|
3661
3675
|
}
|
|
3662
3676
|
try {
|
|
3663
3677
|
if (action === 'cancel') {
|
|
3664
|
-
if (
|
|
3665
|
-
|
|
3678
|
+
if (instance) {
|
|
3679
|
+
try {
|
|
3680
|
+
await instance.terminate();
|
|
3681
|
+
} catch (error) {
|
|
3682
|
+
const message =
|
|
3683
|
+
error instanceof Error ? error.message : String(error);
|
|
3684
|
+
// Tolerate four classes of error here:
|
|
3685
|
+
// - already-terminal (complete / errored / terminated)
|
|
3686
|
+
// - "Cannot terminate instance since its on a finite state"
|
|
3687
|
+
// (the runtime's wording for "already finished")
|
|
3688
|
+
// - "not implemented" (wrangler dev local mode doesn't support
|
|
3689
|
+
// instance.terminate() yet — silently no-op there)
|
|
3690
|
+
// - "not found" (instance never existed)
|
|
3691
|
+
if (
|
|
3692
|
+
!/complete|terminated|errored|finite state|cannot[ _]terminate|not[ _]implemented|not[ _]found|404/i.test(
|
|
3693
|
+
message,
|
|
3694
|
+
)
|
|
3695
|
+
) {
|
|
3696
|
+
throw error;
|
|
3697
|
+
}
|
|
3698
|
+
}
|
|
3666
3699
|
}
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3700
|
+
// terminate() kills the dynamic worker before its run() wrapper can
|
|
3701
|
+
// write terminal state (the only place completed/failed land), so
|
|
3702
|
+
// without this write /tail reports 'running' forever and any
|
|
3703
|
+
// start-stream watcher hangs after a cancel. Land the cancelled
|
|
3704
|
+
// terminal state here — terminal-set appends a 'terminal' run event
|
|
3705
|
+
// and wakes the dedup DO's long-poll waiters, which unblocks tails.
|
|
3706
|
+
//
|
|
3707
|
+
// Idempotency: first-wins from this side — if the run already went
|
|
3708
|
+
// terminal (completed/failed/cancelled) we keep that state. The DO
|
|
3709
|
+
// stores the cached terminal state under a single storage key
|
|
3710
|
+
// (last-wins on raw writes), but the run-event log is append-only
|
|
3711
|
+
// and /tail truncates at the FIRST terminal event, so a racing
|
|
3712
|
+
// completed/failed write from a dying worker can at worst replace
|
|
3713
|
+
// the cached key with another terminal status — it can never
|
|
3714
|
+
// resurrect 'running'.
|
|
3715
|
+
const existingTerminal = await readCoordinatorTerminalState(
|
|
3716
|
+
env,
|
|
3717
|
+
runId,
|
|
3718
|
+
).catch((error: unknown) => {
|
|
3719
|
+
// Tolerated: better to risk a harmless terminal-over-terminal
|
|
3720
|
+
// overwrite than to skip the cancelled write and hang watchers.
|
|
3721
|
+
console.warn('[coordinator] terminal state read before cancel failed', {
|
|
3722
|
+
runId,
|
|
3723
|
+
error: error instanceof Error ? error.message : String(error),
|
|
3724
|
+
});
|
|
3725
|
+
return null;
|
|
3726
|
+
});
|
|
3727
|
+
if (!existingTerminal) {
|
|
3728
|
+
try {
|
|
3729
|
+
await writeCoordinatorTerminalState(env, {
|
|
3730
|
+
runId,
|
|
3731
|
+
status: 'cancelled',
|
|
3732
|
+
error: 'Run cancelled',
|
|
3733
|
+
});
|
|
3734
|
+
} catch (error) {
|
|
3735
|
+
// Fail loudly: the workflow was terminated but watchers would
|
|
3736
|
+
// hang on 'running' forever without the terminal event.
|
|
3737
|
+
const message =
|
|
3738
|
+
error instanceof Error ? error.message : String(error);
|
|
3739
|
+
console.error('[coordinator] cancel terminal state write failed', {
|
|
3740
|
+
runId,
|
|
3741
|
+
error: message,
|
|
3742
|
+
});
|
|
3743
|
+
return Response.json(
|
|
3744
|
+
{
|
|
3745
|
+
runId,
|
|
3746
|
+
status: 'error',
|
|
3747
|
+
error: `workflow terminated but cancelled terminal state write failed: ${message}`,
|
|
3748
|
+
},
|
|
3749
|
+
{ status: 500 },
|
|
3750
|
+
);
|
|
3684
3751
|
}
|
|
3685
3752
|
}
|
|
3686
3753
|
return Response.json({ runId, status: 'cancelled' });
|
|
@@ -1206,7 +1206,10 @@ async function waitForSyntheticIntegrationEvent(
|
|
|
1206
1206
|
{
|
|
1207
1207
|
type: 'log.appended',
|
|
1208
1208
|
runId: req.runId,
|
|
1209
|
-
|
|
1209
|
+
// 'system' (windowed text-dedupe channel), NOT 'worker': this line is
|
|
1210
|
+
// emitted outside the harness log buffer, so it has no positional
|
|
1211
|
+
// channelOffset and must not pollute the worker channel cursor.
|
|
1212
|
+
source: 'system',
|
|
1210
1213
|
occurredAt: nowMs(),
|
|
1211
1214
|
lines: [
|
|
1212
1215
|
`Waiting for integration_event:${eventKey} for up to ${timeoutMs}ms.`,
|
|
@@ -1334,20 +1337,35 @@ async function callToolDirect(
|
|
|
1334
1337
|
|
|
1335
1338
|
function toolMetadataFallback(toolId: string): ToolResultMetadataInput {
|
|
1336
1339
|
if (toolId === 'test_rate_limit') {
|
|
1340
|
+
// Batched members resolve metadata through this fallback because the lean
|
|
1341
|
+
// worker does not bundle the catalog. It MUST mirror the same
|
|
1342
|
+
// `email_status: emailStatus({...})` contract registered in
|
|
1343
|
+
// src/lib/integrations/test/index.ts so batched results normalize to the
|
|
1344
|
+
// same rich email_status OBJECT (status from statusMap, catch_all as a
|
|
1345
|
+
// signal) that the single-execute real-metadata path produces. The legacy
|
|
1346
|
+
// `transforms:['emailStatus']` + mx_security_gateway→'catch_all' string
|
|
1347
|
+
// override coarsened email_status into a bare string and predates the
|
|
1348
|
+
// emailStatus object contract (#1466).
|
|
1337
1349
|
return {
|
|
1338
1350
|
toolId,
|
|
1339
1351
|
extractors: {
|
|
1340
1352
|
email_status: {
|
|
1341
1353
|
paths: ['email_status'],
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1354
|
+
emailStatus: {
|
|
1355
|
+
provider: 'test',
|
|
1356
|
+
rawStatus: ['email_status'],
|
|
1357
|
+
catchAll: ['mx_security_gateway'],
|
|
1358
|
+
statusMap: {
|
|
1359
|
+
valid: { status: 'valid', verdict: 'send', verified: true },
|
|
1360
|
+
invalid: { status: 'invalid', verdict: 'drop', verified: false },
|
|
1361
|
+
catch_all: {
|
|
1362
|
+
status: 'catch_all',
|
|
1363
|
+
verdict: 'verify_next',
|
|
1364
|
+
verified: false,
|
|
1365
|
+
},
|
|
1366
|
+
unknown: { status: 'unknown', verdict: 'hold', verified: false },
|
|
1349
1367
|
},
|
|
1350
|
-
|
|
1368
|
+
},
|
|
1351
1369
|
},
|
|
1352
1370
|
},
|
|
1353
1371
|
targetGetters: {
|
|
@@ -5402,6 +5420,14 @@ async function executeRunRequest(
|
|
|
5402
5420
|
const abortSignal = abortController.signal;
|
|
5403
5421
|
let runLogBuffer: string[] = [];
|
|
5404
5422
|
let pendingRunLogLines: string[] = [];
|
|
5423
|
+
// Monotonic count of every line ever appended to this run's worker log
|
|
5424
|
+
// channel. runLogBuffer/pendingRunLogLines are rotating tails of those
|
|
5425
|
+
// lines (RUN_LOG_BUFFER_LIMIT is the coordinator transport cache only), so
|
|
5426
|
+
// each log.appended batch can carry the absolute channelOffset of its first
|
|
5427
|
+
// line: totalEmittedLogLines - pendingRunLogLines.length. Run Log Stream
|
|
5428
|
+
// ingestion skips re-sent prefixes positionally (exactly-once, repeated
|
|
5429
|
+
// identical lines preserved) instead of text-deduping.
|
|
5430
|
+
let totalEmittedLogLines = 0;
|
|
5405
5431
|
let stepProgressByNodeId: LiveNodeProgressMap = {};
|
|
5406
5432
|
let dirtyProgressNodeIds = new Set<string>();
|
|
5407
5433
|
let pendingLedgerEvents: PlayRunLedgerEvent[] = [
|
|
@@ -5424,6 +5450,7 @@ async function executeRunRequest(
|
|
|
5424
5450
|
const appendRunLogLine = (line: string) => {
|
|
5425
5451
|
const trimmed = redactSecretsFromLogString(line.trim());
|
|
5426
5452
|
if (!trimmed) return;
|
|
5453
|
+
totalEmittedLogLines += 1;
|
|
5427
5454
|
runLogBuffer = [...runLogBuffer, trimmed].slice(-RUN_LOG_BUFFER_LIMIT);
|
|
5428
5455
|
pendingRunLogLines = [...pendingRunLogLines, trimmed].slice(
|
|
5429
5456
|
-RUN_LOG_BUFFER_LIMIT,
|
|
@@ -5614,6 +5641,12 @@ async function executeRunRequest(
|
|
|
5614
5641
|
source: 'worker',
|
|
5615
5642
|
occurredAt,
|
|
5616
5643
|
lines: pendingRunLogLines,
|
|
5644
|
+
// Positional cursor: pendingRunLogLines always holds the LAST
|
|
5645
|
+
// pending lines emitted on this channel, so the offset of its first
|
|
5646
|
+
// line is total-emitted minus pending length. This also covers the
|
|
5647
|
+
// terminal full-buffer re-send (pending = runLogBuffer), which
|
|
5648
|
+
// ingestion then skips positionally instead of via text dedupe.
|
|
5649
|
+
channelOffset: totalEmittedLogLines - pendingRunLogLines.length,
|
|
5617
5650
|
});
|
|
5618
5651
|
pendingRunLogLines = [];
|
|
5619
5652
|
}
|
|
@@ -5709,6 +5742,9 @@ async function executeRunRequest(
|
|
|
5709
5742
|
): Promise<void> => {
|
|
5710
5743
|
if (!options?.persistResultDatasets) return;
|
|
5711
5744
|
const now = nowMs();
|
|
5745
|
+
// Terminal re-send of the full retained buffer. drainPendingLedgerEvents
|
|
5746
|
+
// stamps it with channelOffset = totalEmitted - buffer length, so Run Log
|
|
5747
|
+
// Stream ingestion drops the already-ingested prefix positionally.
|
|
5712
5748
|
pendingRunLogLines = runLogBuffer;
|
|
5713
5749
|
dirtyProgressNodeIds = new Set([
|
|
5714
5750
|
...dirtyProgressNodeIds,
|
|
@@ -5859,6 +5895,25 @@ async function executeRunRequest(
|
|
|
5859
5895
|
ms: nowMs() - resultDatasetStartedAt,
|
|
5860
5896
|
});
|
|
5861
5897
|
const parentSignal = startParentTerminalSignal();
|
|
5898
|
+
// Capped runs settle compute billing BEFORE declaring run.completed: a
|
|
5899
|
+
// per-run cap denial (422 billing_cap_exceeded) must fail the run as
|
|
5900
|
+
// its ONLY terminal. Flushing completed first opens a race — watchers
|
|
5901
|
+
// stream the ledger snapshot and exit on the transient completed
|
|
5902
|
+
// before the demoting run.failed lands.
|
|
5903
|
+
const capped = extractMaxCreditsPerRun(req.contractSnapshot) !== null;
|
|
5904
|
+
if (capped) {
|
|
5905
|
+
const billingStartedAt = nowMs();
|
|
5906
|
+
await finalizeWorkerComputeBilling({
|
|
5907
|
+
req,
|
|
5908
|
+
success: true,
|
|
5909
|
+
actionEstimate: 4,
|
|
5910
|
+
});
|
|
5911
|
+
recordRunnerPerfTrace({
|
|
5912
|
+
req,
|
|
5913
|
+
phase: 'runner.compute_billing_finalize',
|
|
5914
|
+
ms: nowMs() - billingStartedAt,
|
|
5915
|
+
});
|
|
5916
|
+
}
|
|
5862
5917
|
const terminalOccurredAt = nowMs();
|
|
5863
5918
|
const terminalUpdateStartedAt = nowMs();
|
|
5864
5919
|
await flushTerminalLedgerEvents({
|
|
@@ -5874,21 +5929,19 @@ async function executeRunRequest(
|
|
|
5874
5929
|
ms: nowMs() - terminalUpdateStartedAt,
|
|
5875
5930
|
});
|
|
5876
5931
|
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
success: true,
|
|
5881
|
-
actionEstimate: 4,
|
|
5882
|
-
}).then(() => {
|
|
5883
|
-
recordRunnerPerfTrace({
|
|
5932
|
+
if (!capped) {
|
|
5933
|
+
const billingStartedAt = nowMs();
|
|
5934
|
+
const billingPromise = finalizeWorkerComputeBilling({
|
|
5884
5935
|
req,
|
|
5885
|
-
|
|
5886
|
-
|
|
5936
|
+
success: true,
|
|
5937
|
+
actionEstimate: 4,
|
|
5938
|
+
}).then(() => {
|
|
5939
|
+
recordRunnerPerfTrace({
|
|
5940
|
+
req,
|
|
5941
|
+
phase: 'runner.compute_billing_finalize',
|
|
5942
|
+
ms: nowMs() - billingStartedAt,
|
|
5943
|
+
});
|
|
5887
5944
|
});
|
|
5888
|
-
});
|
|
5889
|
-
if (extractMaxCreditsPerRun(req.contractSnapshot) !== null) {
|
|
5890
|
-
await billingPromise;
|
|
5891
|
-
} else {
|
|
5892
5945
|
const nonBlockingBillingPromise = billingPromise.catch((error) => {
|
|
5893
5946
|
console.error(
|
|
5894
5947
|
`[play-harness] non-fatal compute billing finalize failed runId=${req.runId}: ${
|