@relayflows/sdk 2.0.30 → 2.0.32
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/agent-artifacts.d.ts.map +1 -1
- package/dist/agent-artifacts.js +2 -9
- package/dist/agent-artifacts.js.map +1 -1
- package/dist/agent-cwd.d.ts +69 -0
- package/dist/agent-cwd.d.ts.map +1 -0
- package/dist/agent-cwd.js +140 -0
- package/dist/agent-cwd.js.map +1 -0
- package/dist/artifact-scan-policy.d.ts +35 -0
- package/dist/artifact-scan-policy.d.ts.map +1 -0
- package/dist/artifact-scan-policy.js +49 -0
- package/dist/artifact-scan-policy.js.map +1 -0
- package/dist/authored-completion-record.d.ts +35 -0
- package/dist/authored-completion-record.d.ts.map +1 -0
- package/dist/authored-completion-record.js +92 -0
- package/dist/authored-completion-record.js.map +1 -0
- package/dist/authored-completion.d.ts +99 -0
- package/dist/authored-completion.d.ts.map +1 -0
- package/dist/authored-completion.js +184 -0
- package/dist/authored-completion.js.map +1 -0
- package/dist/authored-flow-error.d.ts +13 -1
- package/dist/authored-flow-error.d.ts.map +1 -1
- package/dist/authored-flow-error.js +12 -0
- package/dist/authored-flow-error.js.map +1 -1
- package/dist/authored-flow-executor.d.ts +7 -30
- package/dist/authored-flow-executor.d.ts.map +1 -1
- package/dist/authored-flow-executor.js +54 -47
- package/dist/authored-flow-executor.js.map +1 -1
- package/dist/authored-human.d.ts.map +1 -1
- package/dist/authored-human.js +1 -3
- package/dist/authored-human.js.map +1 -1
- package/dist/authored-node-entry.js +4 -1
- package/dist/authored-node-entry.js.map +1 -1
- package/dist/authored-node-runner.d.ts +9 -1
- package/dist/authored-node-runner.d.ts.map +1 -1
- package/dist/authored-node-runner.js +92 -17
- package/dist/authored-node-runner.js.map +1 -1
- package/dist/authored-preflight.d.ts +4 -0
- package/dist/authored-preflight.d.ts.map +1 -0
- package/dist/authored-preflight.js +50 -0
- package/dist/authored-preflight.js.map +1 -0
- package/dist/authored-root.d.ts +5 -0
- package/dist/authored-root.d.ts.map +1 -1
- package/dist/authored-root.js +20 -2
- package/dist/authored-root.js.map +1 -1
- package/dist/authored-step-output.d.ts +35 -10
- package/dist/authored-step-output.d.ts.map +1 -1
- package/dist/authored-step-output.js +108 -40
- package/dist/authored-step-output.js.map +1 -1
- package/dist/authored-verdict.d.ts +28 -0
- package/dist/authored-verdict.d.ts.map +1 -0
- package/dist/authored-verdict.js +79 -0
- package/dist/authored-verdict.js.map +1 -0
- package/dist/authored-worker-step.d.ts +5 -2
- package/dist/authored-worker-step.d.ts.map +1 -1
- package/dist/authored-worker-step.js +51 -13
- package/dist/authored-worker-step.js.map +1 -1
- package/dist/cli/check-worker-surface.d.ts +27 -0
- package/dist/cli/check-worker-surface.d.ts.map +1 -0
- package/dist/cli/check-worker-surface.js +63 -0
- package/dist/cli/check-worker-surface.js.map +1 -0
- package/dist/cli/check.d.ts +28 -3
- package/dist/cli/check.d.ts.map +1 -1
- package/dist/cli/check.js +27 -140
- package/dist/cli/check.js.map +1 -1
- package/dist/cli/cli-probe.d.ts +20 -0
- package/dist/cli/cli-probe.d.ts.map +1 -0
- package/dist/cli/cli-probe.js +166 -0
- package/dist/cli/cli-probe.js.map +1 -0
- package/dist/cli/cloud-format.d.ts +9 -0
- package/dist/cli/cloud-format.d.ts.map +1 -0
- package/dist/cli/cloud-format.js +77 -0
- package/dist/cli/cloud-format.js.map +1 -0
- package/dist/cli/cloud-live.d.ts +39 -0
- package/dist/cli/cloud-live.d.ts.map +1 -0
- package/dist/cli/cloud-live.js +325 -0
- package/dist/cli/cloud-live.js.map +1 -0
- package/dist/cli/cloud-mirror-session.d.ts +97 -0
- package/dist/cli/cloud-mirror-session.d.ts.map +1 -0
- package/dist/cli/cloud-mirror-session.js +264 -0
- package/dist/cli/cloud-mirror-session.js.map +1 -0
- package/dist/cli/cloud-read.d.ts +3 -7
- package/dist/cli/cloud-read.d.ts.map +1 -1
- package/dist/cli/cloud-read.js +14 -266
- package/dist/cli/cloud-read.js.map +1 -1
- package/dist/cli/cloud-refusal.d.ts +39 -0
- package/dist/cli/cloud-refusal.d.ts.map +1 -0
- package/dist/cli/cloud-refusal.js +95 -0
- package/dist/cli/cloud-refusal.js.map +1 -0
- package/dist/cli/cloud-status-view.d.ts +7 -0
- package/dist/cli/cloud-status-view.d.ts.map +1 -0
- package/dist/cli/cloud-status-view.js +214 -0
- package/dist/cli/cloud-status-view.js.map +1 -0
- package/dist/cli/direct-run.d.ts.map +1 -1
- package/dist/cli/direct-run.js +16 -2
- package/dist/cli/direct-run.js.map +1 -1
- package/dist/cli/hn-monitor.d.ts +0 -13
- package/dist/cli/hn-monitor.d.ts.map +1 -1
- package/dist/cli/hn-monitor.js +2 -1
- package/dist/cli/hn-monitor.js.map +1 -1
- package/dist/cli/local-agent-remedy.d.ts +80 -0
- package/dist/cli/local-agent-remedy.d.ts.map +1 -0
- package/dist/cli/local-agent-remedy.js +116 -0
- package/dist/cli/local-agent-remedy.js.map +1 -0
- package/dist/cli/observer-session.d.ts +41 -0
- package/dist/cli/observer-session.d.ts.map +1 -0
- package/dist/cli/observer-session.js +78 -0
- package/dist/cli/observer-session.js.map +1 -0
- package/dist/cli/run.d.ts +30 -2
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +186 -24
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/status.d.ts +9 -0
- package/dist/cli/status.d.ts.map +1 -1
- package/dist/cli/status.js +31 -2
- package/dist/cli/status.js.map +1 -1
- package/dist/cli/step-evidence.d.ts +90 -0
- package/dist/cli/step-evidence.d.ts.map +1 -0
- package/dist/cli/step-evidence.js +259 -0
- package/dist/cli/step-evidence.js.map +1 -0
- package/dist/cli/step-failure.d.ts +14 -4
- package/dist/cli/step-failure.d.ts.map +1 -1
- package/dist/cli/step-failure.js +41 -76
- package/dist/cli/step-failure.js.map +1 -1
- package/dist/cli-commands.d.ts +12 -0
- package/dist/cli-commands.d.ts.map +1 -1
- package/dist/cli-commands.js +3 -0
- package/dist/cli-commands.js.map +1 -1
- package/dist/cli.d.ts +17 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +134 -57
- package/dist/cli.js.map +1 -1
- package/dist/cloud-deploy.d.ts +1 -1
- package/dist/cloud-deploy.d.ts.map +1 -1
- package/dist/cloud-deploy.js +24 -8
- package/dist/cloud-deploy.js.map +1 -1
- package/dist/cloud-mirror-ledger.d.ts +40 -0
- package/dist/cloud-mirror-ledger.d.ts.map +1 -0
- package/dist/cloud-mirror-ledger.js +142 -0
- package/dist/cloud-mirror-ledger.js.map +1 -0
- package/dist/cloud-mirror-step.d.ts +168 -0
- package/dist/cloud-mirror-step.d.ts.map +1 -0
- package/dist/cloud-mirror-step.js +504 -0
- package/dist/cloud-mirror-step.js.map +1 -0
- package/dist/cloud-mirror-transport.d.ts +94 -0
- package/dist/cloud-mirror-transport.d.ts.map +1 -0
- package/dist/cloud-mirror-transport.js +163 -0
- package/dist/cloud-mirror-transport.js.map +1 -0
- package/dist/cloud-mirror.d.ts +132 -0
- package/dist/cloud-mirror.d.ts.map +1 -0
- package/dist/cloud-mirror.js +556 -0
- package/dist/cloud-mirror.js.map +1 -0
- package/dist/cloud-read.d.ts +15 -0
- package/dist/cloud-read.d.ts.map +1 -1
- package/dist/cloud-read.js +20 -1
- package/dist/cloud-read.js.map +1 -1
- package/dist/cloud-run-record.d.ts +27 -0
- package/dist/cloud-run-record.d.ts.map +1 -0
- package/dist/cloud-run-record.js +56 -0
- package/dist/cloud-run-record.js.map +1 -0
- package/dist/cloud-run.d.ts +2 -9
- package/dist/cloud-run.d.ts.map +1 -1
- package/dist/cloud-run.js +3 -20
- package/dist/cloud-run.js.map +1 -1
- package/dist/communication/local.d.ts.map +1 -1
- package/dist/communication/local.js +2 -1
- package/dist/communication/local.js.map +1 -1
- package/dist/communication/worker.d.ts +1 -1
- package/dist/communication/worker.d.ts.map +1 -1
- package/dist/communication/worker.js +13 -5
- package/dist/communication/worker.js.map +1 -1
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +28 -6
- package/dist/compile.js.map +1 -1
- package/dist/direct-input.d.ts.map +1 -1
- package/dist/direct-input.js +21 -1
- package/dist/direct-input.js.map +1 -1
- package/dist/failure-kinds.d.ts +96 -6
- package/dist/failure-kinds.d.ts.map +1 -1
- package/dist/failure-kinds.js +29 -2
- package/dist/failure-kinds.js.map +1 -1
- package/dist/flow-requirements.d.ts.map +1 -1
- package/dist/flow-requirements.js +24 -9
- package/dist/flow-requirements.js.map +1 -1
- package/dist/gate-contract.d.ts +7 -0
- package/dist/gate-contract.d.ts.map +1 -1
- package/dist/gate-contract.js +6 -1
- package/dist/gate-contract.js.map +1 -1
- package/dist/helper-preflight.d.ts.map +1 -1
- package/dist/helper-preflight.js +10 -3
- package/dist/helper-preflight.js.map +1 -1
- package/dist/helper-reference.d.ts +25 -0
- package/dist/helper-reference.d.ts.map +1 -0
- package/dist/helper-reference.js +129 -0
- package/dist/helper-reference.js.map +1 -0
- package/dist/hosted-extension-runtime.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/journal-client.d.ts +3 -1
- package/dist/journal-client.d.ts.map +1 -1
- package/dist/journal-client.js +17 -2
- package/dist/journal-client.js.map +1 -1
- package/dist/journal-projection.d.ts +16 -0
- package/dist/journal-projection.d.ts.map +1 -0
- package/dist/journal-projection.js +108 -0
- package/dist/journal-projection.js.map +1 -0
- package/dist/llm-worker.js +1 -1
- package/dist/llm-worker.js.map +1 -1
- package/dist/local-agent.d.ts +1 -1
- package/dist/local-agent.d.ts.map +1 -1
- package/dist/local-agent.js +4 -3
- package/dist/local-agent.js.map +1 -1
- package/dist/named-gate-lowering.js +35 -5
- package/dist/named-gate-lowering.js.map +1 -1
- package/dist/named-gate-preflight.d.ts +27 -0
- package/dist/named-gate-preflight.d.ts.map +1 -0
- package/dist/named-gate-preflight.js +47 -0
- package/dist/named-gate-preflight.js.map +1 -0
- package/dist/named-gates.d.ts +1 -1
- package/dist/named-gates.d.ts.map +1 -1
- package/dist/named-gates.js +3 -0
- package/dist/named-gates.js.map +1 -1
- package/dist/observer-link.d.ts +7 -0
- package/dist/observer-link.d.ts.map +1 -1
- package/dist/observer-link.js +3 -1
- package/dist/observer-link.js.map +1 -1
- package/dist/package-version.d.ts +10 -0
- package/dist/package-version.d.ts.map +1 -0
- package/dist/package-version.js +22 -0
- package/dist/package-version.js.map +1 -0
- package/dist/preflight.d.ts +14 -0
- package/dist/preflight.d.ts.map +1 -1
- package/dist/preflight.js +25 -3
- package/dist/preflight.js.map +1 -1
- package/dist/protocol.d.ts +2 -0
- package/dist/protocol.d.ts.map +1 -1
- package/dist/redact.d.ts +42 -0
- package/dist/redact.d.ts.map +1 -1
- package/dist/redact.js +128 -0
- package/dist/redact.js.map +1 -1
- package/dist/relay-cli.d.ts.map +1 -1
- package/dist/relay-cli.js +1 -21
- package/dist/relay-cli.js.map +1 -1
- package/dist/run-projection.d.ts +83 -0
- package/dist/run-projection.d.ts.map +1 -0
- package/dist/run-projection.js +199 -0
- package/dist/run-projection.js.map +1 -0
- package/dist/shell-word.d.ts +19 -0
- package/dist/shell-word.d.ts.map +1 -0
- package/dist/shell-word.js +23 -0
- package/dist/shell-word.js.map +1 -0
- package/dist/spec.d.ts +52 -3
- package/dist/spec.d.ts.map +1 -1
- package/dist/spec.js.map +1 -1
- package/dist/step-fields.d.ts +1 -1
- package/dist/step-fields.js +1 -1
- package/dist/step-fields.js.map +1 -1
- package/dist/steps-green.d.ts +27 -0
- package/dist/steps-green.d.ts.map +1 -0
- package/dist/steps-green.js +164 -0
- package/dist/steps-green.js.map +1 -0
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +39 -8
- package/dist/validate.js.map +1 -1
- package/dist/worker-lease.d.ts +8 -1
- package/dist/worker-lease.d.ts.map +1 -1
- package/dist/worker-lease.js +34 -4
- package/dist/worker-lease.js.map +1 -1
- package/dist/worker.d.ts +8 -0
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +17 -9
- package/dist/worker.js.map +1 -1
- package/package.json +3 -7
- package/src/agent-artifacts.ts +2 -10
- package/src/agent-cwd.ts +139 -0
- package/src/artifact-scan-policy.ts +51 -0
- package/src/authored-completion-record.ts +119 -0
- package/src/authored-completion.ts +203 -0
- package/src/authored-flow-error.ts +13 -1
- package/src/authored-flow-executor.ts +91 -58
- package/src/authored-human.ts +1 -4
- package/src/authored-node-entry.ts +4 -1
- package/src/authored-node-runner.ts +96 -22
- package/src/authored-preflight.ts +46 -0
- package/src/authored-root.ts +20 -2
- package/src/authored-step-output.ts +139 -42
- package/src/authored-verdict.ts +87 -0
- package/src/authored-worker-step.ts +63 -14
- package/src/cli/check-worker-surface.ts +66 -0
- package/src/cli/check.ts +59 -166
- package/src/cli/cli-probe.ts +194 -0
- package/src/cli/cloud-format.ts +82 -0
- package/src/cli/cloud-live.ts +366 -0
- package/src/cli/cloud-mirror-session.ts +329 -0
- package/src/cli/cloud-read.ts +13 -282
- package/src/cli/cloud-refusal.ts +113 -0
- package/src/cli/cloud-status-view.ts +213 -0
- package/src/cli/direct-run.ts +19 -2
- package/src/cli/hn-monitor.ts +2 -1
- package/src/cli/local-agent-remedy.ts +160 -0
- package/src/cli/observer-session.ts +107 -0
- package/src/cli/run.ts +233 -28
- package/src/cli/status.ts +59 -3
- package/src/cli/step-evidence.ts +310 -0
- package/src/cli/step-failure.ts +57 -81
- package/src/cli-commands.ts +3 -0
- package/src/cli.ts +155 -60
- package/src/cloud-deploy.ts +26 -8
- package/src/cloud-mirror-ledger.ts +161 -0
- package/src/cloud-mirror-step.ts +618 -0
- package/src/cloud-mirror-transport.ts +261 -0
- package/src/cloud-mirror.ts +633 -0
- package/src/cloud-read.ts +25 -1
- package/src/cloud-run-record.ts +64 -0
- package/src/cloud-run.ts +5 -24
- package/src/communication/local.ts +2 -1
- package/src/communication/worker.ts +13 -5
- package/src/compile.ts +28 -6
- package/src/direct-input.ts +22 -1
- package/src/failure-kinds.ts +104 -2
- package/src/flow-requirements.ts +24 -9
- package/src/gate-contract.ts +13 -1
- package/src/helper-preflight.ts +10 -3
- package/src/helper-reference.ts +130 -0
- package/src/hosted-extension-runtime.ts +1 -1
- package/src/index.ts +2 -0
- package/src/journal-client.ts +19 -2
- package/src/journal-projection.ts +106 -0
- package/src/llm-worker.ts +1 -1
- package/src/local-agent.ts +4 -2
- package/src/named-gate-lowering.ts +35 -5
- package/src/named-gate-preflight.ts +47 -0
- package/src/named-gates.ts +3 -0
- package/src/observer-link.ts +10 -1
- package/src/package-version.ts +23 -0
- package/src/preflight.ts +38 -7
- package/src/protocol.ts +2 -0
- package/src/redact.ts +111 -0
- package/src/relay-cli.ts +1 -24
- package/src/run-projection.ts +247 -0
- package/src/shell-word.ts +24 -0
- package/src/spec.ts +54 -3
- package/src/step-fields.ts +1 -1
- package/src/steps-green.ts +168 -0
- package/src/validate.ts +35 -8
- package/src/worker-lease.ts +35 -5
- package/src/worker.ts +27 -11
package/src/cli/run.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { onWorkerFailure } from '../worker-lease.js';
|
|
1
2
|
import { communicationInstruction } from '../communication/spec.js';
|
|
2
3
|
import { checkCommunicationEnvironment, CommunicationEnvironmentError } from '../communication/preflight.js';
|
|
3
4
|
import { parseHumanRecipient } from '../human-to.js';
|
|
@@ -9,25 +10,33 @@ import { AuthoredFlowExecutionError, AuthoredHumanParked, type AuthoredHumanWait
|
|
|
9
10
|
import { answerCommand, resumeCommand } from '../authored-human.js';
|
|
10
11
|
import { join, resolve } from 'node:path';
|
|
11
12
|
import type { ProgressEvent } from '../progress.js';
|
|
13
|
+
import type { JournalEvent } from '../journal-reader.js';
|
|
12
14
|
import { toKernelSpec } from '../compile.js';
|
|
13
15
|
import { socketPathFor } from '../daemon-connection.js';
|
|
14
16
|
import { ensureDaemon, type EnsureDaemonOptions } from '../daemon-lifecycle.js';
|
|
15
17
|
import { isAuthoredFlowPath } from '../direct-input.js';
|
|
16
18
|
import { daemonRefusal } from './daemon-refusal.js';
|
|
17
|
-
import
|
|
19
|
+
import {
|
|
20
|
+
authoredInput, authoredWorkerRemedy, localAgentRemedy,
|
|
21
|
+
type AuthoredInput, type LocalAgentRemedy,
|
|
22
|
+
} from './local-agent-remedy.js';
|
|
23
|
+
import type { ParkCause, RunFailureKind, RunWarningKind, StepFailedDetails } from '../failure-kinds.js';
|
|
18
24
|
import { inspectionHint, renderInspection, renderStepEvidence, stepFailureDetails } from './step-failure.js';
|
|
19
25
|
import { JournalClient, JournalProtocolError } from '../journal-client.js';
|
|
20
26
|
import { attachLocalAgent } from '../local-agent.js';
|
|
21
27
|
import { LlmWorker } from '../llm-worker.js';
|
|
22
|
-
import { readAuthoredRootMetadata, resumeDurableAuthoredFlow } from '../authored-root.js';
|
|
28
|
+
import { readAuthoredRootMetadata, resumeDurableAuthoredFlow, type AuthoredRootMetadata } from '../authored-root.js';
|
|
23
29
|
import type {
|
|
24
30
|
RunCompletionReason,
|
|
25
31
|
RunOutcome,
|
|
26
32
|
RunStatus,
|
|
27
33
|
} from '../protocol.js';
|
|
28
34
|
import type { StepType } from '../spec.js';
|
|
35
|
+
import {
|
|
36
|
+
singleLineCompletionDetail,
|
|
37
|
+
type LoweredCompletionReason,
|
|
38
|
+
} from '../authored-completion.js';
|
|
29
39
|
import { DEFAULT_LOCAL_AGENT_CAPACITY } from '../worker-slots.js';
|
|
30
|
-
import type { LoweredCompletionReason } from '../authored-flow-executor.js';
|
|
31
40
|
import {
|
|
32
41
|
checkFlow,
|
|
33
42
|
type CheckReport,
|
|
@@ -57,9 +66,27 @@ export interface RunReport {
|
|
|
57
66
|
socketPath?: string;
|
|
58
67
|
status?: RunStatus;
|
|
59
68
|
completionReason?: RunCompletionReason;
|
|
69
|
+
/**
|
|
70
|
+
* What the body passed to `done(reason, { detail })`, normalized: redacted,
|
|
71
|
+
* trimmed, and bounded to 2,000 code points. Absent for every one-argument
|
|
72
|
+
* call, so an existing report keeps its exact shape — and absent on the
|
|
73
|
+
* paths where no authored body declared the outcome at all.
|
|
74
|
+
*/
|
|
75
|
+
completionDetail?: string;
|
|
60
76
|
completedSteps?: number;
|
|
61
77
|
reuse?: { fromRunId: string; reusedSteps: number; executedSteps: number };
|
|
62
78
|
parkedStep?: ParkedStep;
|
|
79
|
+
/**
|
|
80
|
+
* Why an exit-3 park happened, when classification established it.
|
|
81
|
+
*
|
|
82
|
+
* Carried structurally so a consumer — including this package's own authored
|
|
83
|
+
* boundaries — can tell "nothing is attached to run this step" from "the
|
|
84
|
+
* kernel is waiting for a human to recover it" without matching on the
|
|
85
|
+
* rendered message. Absent means the cause was not established, never
|
|
86
|
+
* `worker_unavailable` by default: recommending a worker for a `needs_human`
|
|
87
|
+
* park is the wrong answer, and guessing it is worse than saying nothing.
|
|
88
|
+
*/
|
|
89
|
+
parkCause?: ParkCause;
|
|
63
90
|
/** The open `f.human` question a parked authored run is waiting on. */
|
|
64
91
|
humanWait?: AuthoredHumanWait;
|
|
65
92
|
/** `flows answer` only: the answer it recorded. */
|
|
@@ -91,6 +118,10 @@ export interface RunLifecycleOptions {
|
|
|
91
118
|
onPtyReady?: (path: string) => void;
|
|
92
119
|
reuseFromRunId?: string;
|
|
93
120
|
onProgress?: (event: ProgressEvent) => void;
|
|
121
|
+
/** Stream the run's journal entries while it runs (YAML runs, via `run.start {watch}` / `run.watch`). */
|
|
122
|
+
onJournalEntry?: (entry: JournalEvent) => void;
|
|
123
|
+
/** An authored root was admitted: its id is known before its body runs. */
|
|
124
|
+
onRunStarted?: (run: { runId: string; flow: string; resumed?: boolean }) => void;
|
|
94
125
|
localAgent?: boolean;
|
|
95
126
|
/** `--agent-capacity`: the local workers' concurrency; the default is `DEFAULT_LOCAL_AGENT_CAPACITY`. */
|
|
96
127
|
agentCapacity?: number;
|
|
@@ -158,7 +189,8 @@ async function executeCheckedFlow(
|
|
|
158
189
|
const { attachCommunicationWorkers } = await import('../communication/local.js');
|
|
159
190
|
communicationWorkers = await attachCommunicationWorkers(spec, socketPath, dataDir);
|
|
160
191
|
}
|
|
161
|
-
|
|
192
|
+
if (options.onJournalEntry !== undefined) client.on('entry', options.onJournalEntry);
|
|
193
|
+
const outcome = await startWatched(client, spec, options);
|
|
162
194
|
const execution = await classifyOutcome(client, 'run', outcome, base, socketPath, { ...options, dataDir });
|
|
163
195
|
if (options.reuseFromRunId !== undefined) {
|
|
164
196
|
execution.report.reuse = await reuseSummary(client, outcome.run_id, options.reuseFromRunId);
|
|
@@ -178,10 +210,33 @@ async function executeCheckedFlow(
|
|
|
178
210
|
}
|
|
179
211
|
return protocolFailure('run', base, socketPath, communicationWorkers?.failure ?? localAgent?.failure ?? error);
|
|
180
212
|
} finally {
|
|
213
|
+
if (options.onJournalEntry !== undefined) client.off('entry', options.onJournalEntry);
|
|
181
214
|
try { await communicationWorkers?.close(); } finally { try { await localAgent?.close(); } finally { client.close(); } }
|
|
182
215
|
}
|
|
183
216
|
}
|
|
184
217
|
|
|
218
|
+
/**
|
|
219
|
+
* `run.start`, streaming the run's entries to `onJournalEntry` when one is
|
|
220
|
+
* given. A daemon that predates `watch` refuses the field while decoding,
|
|
221
|
+
* before any run exists, so starting again without it is the same request
|
|
222
|
+
* minus the observation — never a second run.
|
|
223
|
+
*/
|
|
224
|
+
async function startWatched(
|
|
225
|
+
client: JournalClient,
|
|
226
|
+
spec: ReturnType<typeof toKernelSpec>,
|
|
227
|
+
options: RunLifecycleOptions,
|
|
228
|
+
): Promise<RunOutcome> {
|
|
229
|
+
const onEntry = options.onJournalEntry;
|
|
230
|
+
if (onEntry === undefined) return client.runStart(spec, options.reuseFromRunId);
|
|
231
|
+
try {
|
|
232
|
+
return await client.runStart(spec, options.reuseFromRunId, undefined, true);
|
|
233
|
+
} catch (error) {
|
|
234
|
+
if (!(error instanceof JournalProtocolError) || error.code !== 'bad_request'
|
|
235
|
+
|| !/unknown field `watch`/.test(error.message)) throw error;
|
|
236
|
+
return await client.runStart(spec, options.reuseFromRunId);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
185
240
|
export async function resumeFlow(
|
|
186
241
|
runId: string,
|
|
187
242
|
dataDir: string,
|
|
@@ -196,16 +251,34 @@ export async function resumeFlow(
|
|
|
196
251
|
let communicationWorkers: Awaited<ReturnType<typeof import('../communication/local.js').attachCommunicationWorkers>> | undefined;
|
|
197
252
|
let authoredAgent: Awaited<ReturnType<typeof attachLocalAgent>> | undefined;
|
|
198
253
|
let authoredLlm: LlmWorker | undefined;
|
|
254
|
+
let llmFailure: unknown;
|
|
199
255
|
let authoredLlmClient: JournalClient | undefined;
|
|
256
|
+
// Hoisted so the catch below can build the authored park remedy from the same
|
|
257
|
+
// metadata this resume was admitted against — the journal is the only place
|
|
258
|
+
// the flow path and the original `--input` survive a process boundary.
|
|
259
|
+
let authoredRoot: AuthoredRootMetadata | undefined;
|
|
200
260
|
const workerCapacity = options.agentCapacity ?? DEFAULT_LOCAL_AGENT_CAPACITY;
|
|
201
261
|
try {
|
|
202
|
-
|
|
262
|
+
authoredRoot = await readAuthoredRootMetadata(client, runId);
|
|
203
263
|
if (authoredRoot !== undefined) {
|
|
264
|
+
// Both worker-surface mismatches are refusals, not protocol failures.
|
|
265
|
+
// They used to throw bare `Error`s, which landed on `protocol_error`
|
|
266
|
+
// ("RUN <id> unknown") and told nobody what to do instead; and the
|
|
267
|
+
// second one is the exact complaint this change answers — `--local-agent`
|
|
268
|
+
// accepted on a run that cannot grow one is worse than rejected, because
|
|
269
|
+
// the run parks again with a message that has not changed.
|
|
204
270
|
if (authoredRoot.localAgentStream !== undefined && !options.localAgent) {
|
|
205
|
-
|
|
271
|
+
return localAgentRefusal(base, runId, socketPath,
|
|
272
|
+
`Run "${runId}" was started with a local agent worker surface, so resuming it needs the same flag.`
|
|
273
|
+
+ ` To continue this run: ${resumeCommand(runId, dataDir, true)}.`);
|
|
206
274
|
}
|
|
207
275
|
if (authoredRoot.localAgentStream === undefined && options.localAgent) {
|
|
208
|
-
|
|
276
|
+
return localAgentRefusal(base, runId, socketPath,
|
|
277
|
+
`Run "${runId}" was started without a local agent worker surface:`
|
|
278
|
+
+ ' --local-agent is admitted at run start; start a new run.'
|
|
279
|
+
+ localAgentRemedy(authoredWorkerRemedy('worker_unavailable', false, {
|
|
280
|
+
path: authoredRoot.flowPath, input: authoredInputArgument(authoredRoot), dataDir,
|
|
281
|
+
})));
|
|
209
282
|
}
|
|
210
283
|
if (options.localAgent) {
|
|
211
284
|
authoredAgent = await attachLocalAgent(
|
|
@@ -215,6 +288,10 @@ export async function resumeFlow(
|
|
|
215
288
|
await authoredLlmClient.connect();
|
|
216
289
|
await authoredLlmClient.hello('flows-authored-resume-llm');
|
|
217
290
|
authoredLlm = new LlmWorker(authoredLlmClient, `${authoredAgent.stream}-llm`, workerCapacity);
|
|
291
|
+
authoredLlm.on('error', onWorkerFailure('resume-llm', error => {
|
|
292
|
+
llmFailure = error;
|
|
293
|
+
client.close();
|
|
294
|
+
}));
|
|
218
295
|
await authoredLlm.attach();
|
|
219
296
|
}
|
|
220
297
|
const result = await resumeDurableAuthoredFlow(runId, client, {
|
|
@@ -239,6 +316,13 @@ export async function resumeFlow(
|
|
|
239
316
|
communicationWorkers = await attachCommunicationWorkers(spec, socketPath, dataDir);
|
|
240
317
|
}
|
|
241
318
|
}
|
|
319
|
+
const onEntry = options.onJournalEntry;
|
|
320
|
+
if (onEntry !== undefined) {
|
|
321
|
+
client.on('entry', onEntry);
|
|
322
|
+
// Observation never decides a resume: a watch the daemon refuses leaves
|
|
323
|
+
// the run unprojected, and the resume below reports the run's own fate.
|
|
324
|
+
await client.runWatch(runId).catch(() => client.off('entry', onEntry));
|
|
325
|
+
}
|
|
242
326
|
let outcome = await client.runResume(runId, options.allowHumanInfluenced);
|
|
243
327
|
if (await resumeHelperEffect(client, runId, dataDir)) {
|
|
244
328
|
outcome = await client.runResume(runId, options.allowHumanInfluenced);
|
|
@@ -269,11 +353,41 @@ export async function resumeFlow(
|
|
|
269
353
|
if (error instanceof AuthoredFlowExecutionError && (error.code === 'step_failed' || error.code === 'gate_failed')) {
|
|
270
354
|
return authoredStepFailure('resume', base, socketPath, error, runId);
|
|
271
355
|
}
|
|
356
|
+
// An authored resume that parks for want of a worker is a park, reported
|
|
357
|
+
// like the run path's — and with the same remedy, because an authored root
|
|
358
|
+
// admits its worker at run start: resuming again cannot attach one, so the
|
|
359
|
+
// only honest instruction is a new run carrying the same `--input`.
|
|
360
|
+
// `runId` here is the CHILD run holding the evidence; the root this resume
|
|
361
|
+
// named stays separate, so `flows` can still collect it.
|
|
362
|
+
if (error instanceof AuthoredFlowExecutionError && (error.code === 'agent_parked' || error.code === 'llm_parked')) {
|
|
363
|
+
return {
|
|
364
|
+
exitCode: 3,
|
|
365
|
+
report: {
|
|
366
|
+
...base,
|
|
367
|
+
ok: false,
|
|
368
|
+
runId: error.runId ?? runId,
|
|
369
|
+
rootRunId: runId,
|
|
370
|
+
socketPath,
|
|
371
|
+
status: 'parked',
|
|
372
|
+
parkCause: error.parkCause,
|
|
373
|
+
diagnostics: [...base.diagnostics, {
|
|
374
|
+
severity: 'parked',
|
|
375
|
+
kind: 'run_parked',
|
|
376
|
+
message: error.message + localAgentRemedy(authoredWorkerRemedy(
|
|
377
|
+
error.parkCause, options.localAgent === true,
|
|
378
|
+
{ path: authoredRoot?.flowPath, input: authoredInputArgument(authoredRoot), dataDir },
|
|
379
|
+
)),
|
|
380
|
+
}],
|
|
381
|
+
},
|
|
382
|
+
};
|
|
383
|
+
}
|
|
272
384
|
if (error instanceof AuthoredHumanParked) {
|
|
273
385
|
return authoredHumanParked('resume', base, socketPath, error, { dataDir, localAgent: options.localAgent === true });
|
|
274
386
|
}
|
|
275
387
|
if (!(error instanceof JournalProtocolError) || error.code !== 'run_not_found') {
|
|
276
|
-
|
|
388
|
+
const cause = error instanceof AuthoredFlowExecutionError
|
|
389
|
+
? error : llmFailure ?? authoredAgent?.failure ?? error;
|
|
390
|
+
return protocolFailure('resume', base, socketPath, cause, runId);
|
|
277
391
|
}
|
|
278
392
|
return {
|
|
279
393
|
exitCode: 2,
|
|
@@ -290,6 +404,7 @@ export async function resumeFlow(
|
|
|
290
404
|
};
|
|
291
405
|
} finally {
|
|
292
406
|
try {
|
|
407
|
+
if (options.onJournalEntry !== undefined) client.off('entry', options.onJournalEntry);
|
|
293
408
|
await communicationWorkers?.close();
|
|
294
409
|
await authoredLlm?.close();
|
|
295
410
|
authoredLlmClient?.close();
|
|
@@ -300,6 +415,45 @@ export async function resumeFlow(
|
|
|
300
415
|
}
|
|
301
416
|
}
|
|
302
417
|
|
|
418
|
+
/**
|
|
419
|
+
* A `--local-agent` mismatch, refused before the resume touches the journal.
|
|
420
|
+
*
|
|
421
|
+
* Exit 2 and `ok` left false by `emptyReport`: nothing ran, so this is the same
|
|
422
|
+
* shape as every other pre-journal refusal on this surface (docs/SURFACE.md §5).
|
|
423
|
+
* It returns rather than throwing so no worker is attached on the way out.
|
|
424
|
+
*/
|
|
425
|
+
function localAgentRefusal(
|
|
426
|
+
base: RunReport, runId: string, socketPath: string, message: string,
|
|
427
|
+
): RunExecution {
|
|
428
|
+
return {
|
|
429
|
+
exitCode: 2,
|
|
430
|
+
report: { ...base, runId, socketPath,
|
|
431
|
+
diagnostics: [...base.diagnostics, { severity: 'refusal', kind: 'local_agent_unavailable', message }] },
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* The `--input` argument a new run of this authored root would have to repeat,
|
|
437
|
+
* rendered back from the journaled metadata.
|
|
438
|
+
*
|
|
439
|
+
* `absent` when the root recorded no input, and also when the recorded input is
|
|
440
|
+
* something `JSON.stringify` cannot render — the remedy formatter then says an
|
|
441
|
+
* input is required instead of printing a command that would be refused as
|
|
442
|
+
* `input_invalid`. What it must never do is substitute `{}`: that is a
|
|
443
|
+
* different invocation of the flow than the one that parked.
|
|
444
|
+
*
|
|
445
|
+
* Unlike `runDirectFlow`, this side has only the decoded value: the journal
|
|
446
|
+
* records the input, not the `--input` word that carried it, so a run started
|
|
447
|
+
* from a file comes back as inline JSON. `authoredInput` is what keeps that
|
|
448
|
+
* honest about its own size — a recorded input can be larger than a shell
|
|
449
|
+
* argument even when the file that supplied it was unremarkable.
|
|
450
|
+
*/
|
|
451
|
+
function authoredInputArgument(metadata: AuthoredRootMetadata | undefined): AuthoredInput {
|
|
452
|
+
if (metadata === undefined || !metadata.inputPresent) return { kind: 'absent' };
|
|
453
|
+
const rendered = JSON.stringify(metadata.input);
|
|
454
|
+
return authoredInput(typeof rendered === 'string' ? rendered : undefined);
|
|
455
|
+
}
|
|
456
|
+
|
|
303
457
|
/**
|
|
304
458
|
* A step that ran and failed, reported as the run failure it is.
|
|
305
459
|
*
|
|
@@ -405,15 +559,30 @@ export function authoredCompletion(
|
|
|
405
559
|
command: RunCommand,
|
|
406
560
|
base: RunReport,
|
|
407
561
|
socketPath: string,
|
|
408
|
-
result: {
|
|
562
|
+
result: {
|
|
563
|
+
name: string;
|
|
564
|
+
completionReason: LoweredCompletionReason;
|
|
565
|
+
completionDetail?: string;
|
|
566
|
+
journalSteps: readonly unknown[];
|
|
567
|
+
},
|
|
409
568
|
runId: string | undefined,
|
|
410
569
|
): RunExecution {
|
|
411
570
|
const common: RunReport = {
|
|
412
571
|
...fromBase(command, base),
|
|
413
572
|
...(runId === undefined ? {} : { runId }),
|
|
414
573
|
socketPath,
|
|
574
|
+
...(result.completionDetail === undefined ? {} : { completionDetail: result.completionDetail }),
|
|
415
575
|
completedSteps: result.journalSteps.length,
|
|
416
576
|
};
|
|
577
|
+
// Two spellings of the same fact, on purpose. `detail` is the structured
|
|
578
|
+
// one — the `StepFailedDetails` key the diagnostic already declares, here
|
|
579
|
+
// describing the AUTHOR's verdict rather than the daemon's account of a
|
|
580
|
+
// failing step — and keeps the body's own line breaks. `said` is the same
|
|
581
|
+
// text folded onto one line for the message, because Cloud renders a run's
|
|
582
|
+
// `error` through a view that elides the middle of a long one.
|
|
583
|
+
const detail = result.completionDetail;
|
|
584
|
+
const evidence = detail === undefined ? {} : { detail };
|
|
585
|
+
const said = detail === undefined ? '' : singleLineCompletionDetail(detail);
|
|
417
586
|
switch (result.completionReason) {
|
|
418
587
|
case 'success':
|
|
419
588
|
return {
|
|
@@ -427,7 +596,9 @@ export function authoredCompletion(
|
|
|
427
596
|
...common, ok: true, status: 'completed', completionReason: 'success',
|
|
428
597
|
diagnostics: [...base.diagnostics, {
|
|
429
598
|
severity: 'declined', kind: 'run_declined',
|
|
430
|
-
|
|
599
|
+
...evidence,
|
|
600
|
+
message: 'Flow deliberately chose not to act on this input.'
|
|
601
|
+
+ (detail === undefined ? '' : ` ${said}`),
|
|
431
602
|
}],
|
|
432
603
|
},
|
|
433
604
|
};
|
|
@@ -438,7 +609,9 @@ export function authoredCompletion(
|
|
|
438
609
|
...common, ok: false, status: 'parked',
|
|
439
610
|
diagnostics: [...base.diagnostics, {
|
|
440
611
|
severity: 'parked', kind: 'run_parked',
|
|
441
|
-
|
|
612
|
+
...evidence,
|
|
613
|
+
message: `Flow "${result.name}" needs_human; see the journal for accumulated blockers.`
|
|
614
|
+
+ (detail === undefined ? '' : ` ${said}`),
|
|
442
615
|
}],
|
|
443
616
|
},
|
|
444
617
|
};
|
|
@@ -454,9 +627,16 @@ export function authoredCompletion(
|
|
|
454
627
|
...common, ok: false, status: 'failed', completionReason: 'step_failed',
|
|
455
628
|
diagnostics: [...base.diagnostics, {
|
|
456
629
|
severity: 'failure', kind: 'step_failed',
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
630
|
+
...evidence,
|
|
631
|
+
// With a detail, the flow's own account REPLACES the generic
|
|
632
|
+
// sentence: "no step-level evidence to inspect" is the only thing
|
|
633
|
+
// there is to say when the body said nothing, and saying it
|
|
634
|
+
// beside a real explanation would bury the explanation.
|
|
635
|
+
message: detail === undefined
|
|
636
|
+
? `Flow "${result.name}" declared done("step_failed"): its own checks did not pass. `
|
|
637
|
+
+ 'No step failed, so there is no step-level evidence to inspect; the journal holds '
|
|
638
|
+
+ 'every step the flow ran before it decided.'
|
|
639
|
+
: `Flow "${result.name}" declared done("step_failed"): ${said}`,
|
|
460
640
|
}],
|
|
461
641
|
},
|
|
462
642
|
};
|
|
@@ -664,25 +844,14 @@ export async function classifyOutcome(
|
|
|
664
844
|
report: {
|
|
665
845
|
...report,
|
|
666
846
|
parkedStep,
|
|
847
|
+
parkCause: needsHuman ? 'needs_human' : 'worker_unavailable',
|
|
667
848
|
diagnostics: [...report.diagnostics, {
|
|
668
849
|
severity: 'parked',
|
|
669
850
|
kind: 'run_parked',
|
|
670
851
|
message: needsHuman
|
|
671
852
|
? `Run "${current.run_id}" parked at step "${parkedStep.id}" (${parkedStep.type}): waiting for human recovery after the worker attempt failed.`
|
|
672
853
|
: `Run "${current.run_id}" parked at step "${parkedStep.id}" (${parkedStep.type}): no worker is attached for step type "${parkedStep.type}".`
|
|
673
|
-
|
|
674
|
-
// Authored TS flows require `--input`; the bare command below
|
|
675
|
-
// would be refused (Cursor Bugbot flagged as LOW on flows#293).
|
|
676
|
-
// For TS we omit the hint rather than fabricate a syntactically
|
|
677
|
-
// valid but semantically wrong command — the direct-run refusal
|
|
678
|
-
// for TS already names its own missing --input.
|
|
679
|
-
+ (command === 'run'
|
|
680
|
-
&& parkedStep.type === 'agent'
|
|
681
|
-
&& !options.localAgent
|
|
682
|
-
&& base.path !== undefined
|
|
683
|
-
&& !isAuthoredFlowPath(base.path)
|
|
684
|
-
? ` To start a new run with a local agent worker: flows run --local-agent '${base.path.replace(/'/g, "'\\''")}'. Declared workspace or stream surfaces require a worker that holds their pins.`
|
|
685
|
-
: ''),
|
|
854
|
+
+ localAgentRemedy(declarativeWorkerRemedy(command, current.run_id, parkedStep, base, options)),
|
|
686
855
|
}],
|
|
687
856
|
},
|
|
688
857
|
};
|
|
@@ -692,6 +861,39 @@ export async function classifyOutcome(
|
|
|
692
861
|
), current.run_id);
|
|
693
862
|
}
|
|
694
863
|
|
|
864
|
+
/**
|
|
865
|
+
* Which remedy a worker park gets from the declarative classifier.
|
|
866
|
+
*
|
|
867
|
+
* An authored `.flow.ts` is deliberately SILENT here, and the suppression is
|
|
868
|
+
* tested first on purpose. Its remedy is a new run carrying the same `--input`,
|
|
869
|
+
* an argument this function does not have; `runDirectFlow` and `resumeFlow`
|
|
870
|
+
* render it once at the authored boundary where the input (or the journaled
|
|
871
|
+
* metadata holding it) is in hand. `classifyOutcome` also runs once per
|
|
872
|
+
* authored `f.agent` CHILD run, so answering here as well would append a
|
|
873
|
+
* remedy to every child message on the way out — and, on resume, a second
|
|
874
|
+
* contradictory one after the attached-worker branch below.
|
|
875
|
+
*
|
|
876
|
+
* `llm` parks get nothing, unchanged: the declarative verbs attach an
|
|
877
|
+
* agent-only worker (local-agent.ts advertises `['agent']`), so `--local-agent`
|
|
878
|
+
* is not a remedy for an `llm` step outside the authored path, which attaches
|
|
879
|
+
* an `LlmWorker` of its own.
|
|
880
|
+
*/
|
|
881
|
+
function declarativeWorkerRemedy(
|
|
882
|
+
command: RunCommand,
|
|
883
|
+
runId: string,
|
|
884
|
+
parkedStep: ParkedStep,
|
|
885
|
+
base: CheckReport | RunReport,
|
|
886
|
+
options: RunLifecycleOptions,
|
|
887
|
+
): LocalAgentRemedy {
|
|
888
|
+
const dataDir = options.dataDir;
|
|
889
|
+
if (parkedStep.type !== 'agent') return { kind: 'none' };
|
|
890
|
+
if (base.path !== undefined && isAuthoredFlowPath(base.path)) return { kind: 'none' };
|
|
891
|
+
if (options.localAgent === true) return { kind: 'attached' };
|
|
892
|
+
if (command === 'resume') return { kind: 'spec-resume', runId, dataDir };
|
|
893
|
+
if (command === 'run' && base.path !== undefined) return { kind: 'spec-run', path: base.path, dataDir };
|
|
894
|
+
return { kind: 'none' };
|
|
895
|
+
}
|
|
896
|
+
|
|
695
897
|
interface OutOfBandInspection {
|
|
696
898
|
status: RunStatus;
|
|
697
899
|
parkedStep?: ParkedStep;
|
|
@@ -742,6 +944,9 @@ async function inspectOutOfBandStep(
|
|
|
742
944
|
};
|
|
743
945
|
}
|
|
744
946
|
|
|
947
|
+
// Match kernel/relayflowd/src/server/client.rs: allow the lease sweep to dispatch a retry.
|
|
948
|
+
const LEASE_SWEEP_GRACE_MS = 5_000;
|
|
949
|
+
|
|
745
950
|
async function waitForRunningStep(
|
|
746
951
|
client: JournalClient,
|
|
747
952
|
runId: string,
|
|
@@ -760,7 +965,7 @@ async function waitForRunningStep(
|
|
|
760
965
|
});
|
|
761
966
|
while (true) {
|
|
762
967
|
throwIfCanceled(options.signal, runningStep.id);
|
|
763
|
-
const remainingMs = leaseDeadlineMs - Date.now();
|
|
968
|
+
const remainingMs = leaseDeadlineMs + LEASE_SWEEP_GRACE_MS - Date.now();
|
|
764
969
|
if (remainingMs <= 0) {
|
|
765
970
|
throw new Error(
|
|
766
971
|
`worker lease for step "${runningStep.id}" expired at ${leaseDeadlineMs} without completion`,
|
package/src/cli/status.ts
CHANGED
|
@@ -13,6 +13,8 @@ import { AUTHORED_STEP_STREAM, foldAuthoredStepRecords, type AuthoredStepRecord
|
|
|
13
13
|
import { canonicalize } from '../canonical.js';
|
|
14
14
|
import { DEFAULT_DATA_DIR } from '../daemon-connection.js';
|
|
15
15
|
import { JournalReadError, walkJournal, type JournalEvent } from '../journal-client.js';
|
|
16
|
+
import { singleLineCompletionDetail } from '../authored-completion.js';
|
|
17
|
+
import { authoredVerdictOf, type AuthoredVerdict } from '../authored-verdict.js';
|
|
16
18
|
import { redact } from '../redact.js';
|
|
17
19
|
import { foldRunState, RunStateError, type RunView, type StepView } from '../run-state.js';
|
|
18
20
|
import { DATA_DIR_ENV, RUN_ID_ENV, STEP_ID_ENV } from '../step-env.js';
|
|
@@ -32,11 +34,25 @@ export interface StatusArgs {
|
|
|
32
34
|
* module keeps its property of opening one file and no socket.
|
|
33
35
|
*/
|
|
34
36
|
cloud?: true;
|
|
37
|
+
/**
|
|
38
|
+
* `--watch`: redraw the hosted page until the run is terminal.
|
|
39
|
+
*
|
|
40
|
+
* Only with `--cloud`. A local `flows status` reads one journal file and
|
|
41
|
+
* returns; there is no loop for it to hang in, so `--watch` without
|
|
42
|
+
* `--cloud` is a contradiction rather than a narrower request, and is
|
|
43
|
+
* refused with the rest of the invocation. Handled in `cli/cloud-live.ts`.
|
|
44
|
+
*/
|
|
45
|
+
watch?: true;
|
|
35
46
|
}
|
|
36
47
|
|
|
37
48
|
export const DEFAULT_TAIL_LINES = 20;
|
|
38
49
|
/** A gate's `detail` may be 2,000 chars (engine/remote.rs); the view shows the head. */
|
|
39
50
|
const DETAIL_LIMIT = 1024;
|
|
51
|
+
// An authored completion detail is NOT held to `DETAIL_LIMIT`. That bound is
|
|
52
|
+
// the head of a gate render the journal holds in full; this text is already
|
|
53
|
+
// bounded to 2,000 code points at `done()`, it is the whole of what the flow
|
|
54
|
+
// said about its own verdict, and cutting it at 1,024 would drop the finding
|
|
55
|
+
// as often as not.
|
|
40
56
|
const BUSY_RETRIES = 5;
|
|
41
57
|
const BUSY_RETRY_DELAY_MS = 50;
|
|
42
58
|
|
|
@@ -64,6 +80,7 @@ export interface StatusOptions {
|
|
|
64
80
|
export function parseStatusArgs(args: readonly string[]): StatusArgs | undefined {
|
|
65
81
|
let json = false;
|
|
66
82
|
let cloud = false;
|
|
83
|
+
let watch = false;
|
|
67
84
|
let dataDir: string | undefined;
|
|
68
85
|
let tail: number | undefined;
|
|
69
86
|
const positionals: string[] = [];
|
|
@@ -75,6 +92,9 @@ export function parseStatusArgs(args: readonly string[]): StatusArgs | undefined
|
|
|
75
92
|
} else if (argument === '--cloud') {
|
|
76
93
|
if (cloud) return undefined;
|
|
77
94
|
cloud = true;
|
|
95
|
+
} else if (argument === '--watch') {
|
|
96
|
+
if (watch) return undefined;
|
|
97
|
+
watch = true;
|
|
78
98
|
} else if (argument === '--data-dir' || argument === '--tail') {
|
|
79
99
|
const value = args[++index];
|
|
80
100
|
if (value === undefined || value.length === 0 || value.startsWith('-')) return undefined;
|
|
@@ -98,9 +118,11 @@ export function parseStatusArgs(args: readonly string[]): StatusArgs | undefined
|
|
|
98
118
|
// but a contradiction, and is refused as an invocation rather than silently
|
|
99
119
|
// ignored. A hosted run id is mandatory: there is no ambient one.
|
|
100
120
|
if (cloud && (dataDir !== undefined || tail !== undefined || positionals.length === 0)) return undefined;
|
|
121
|
+
if (watch && !cloud) return undefined;
|
|
101
122
|
return {
|
|
102
123
|
command: 'status', json,
|
|
103
124
|
...(cloud ? { cloud: true as const } : {}),
|
|
125
|
+
...(watch ? { watch: true as const } : {}),
|
|
104
126
|
...(dataDir === undefined ? {} : { dataDir }),
|
|
105
127
|
...(tail === undefined ? {} : { tail }),
|
|
106
128
|
...(positionals[0] === undefined ? {} : { runId: positionals[0] }),
|
|
@@ -178,7 +200,7 @@ export async function runStatus(args: StatusArgs, io: CliIo, options: StatusOpti
|
|
|
178
200
|
}
|
|
179
201
|
}
|
|
180
202
|
|
|
181
|
-
const presented = present(view, thisStep, tails, env);
|
|
203
|
+
const presented = present(view, thisStep, tails, env, authoredVerdictOf(taken.events));
|
|
182
204
|
if (args.json) {
|
|
183
205
|
const authored = authoredSteps(taken.events, env);
|
|
184
206
|
io.stdout(canonicalize({
|
|
@@ -237,13 +259,41 @@ function authoredSteps(events: readonly JournalEvent[], env: NodeJS.ProcessEnv):
|
|
|
237
259
|
}
|
|
238
260
|
|
|
239
261
|
type PresentedStep = StepView & { tails: StepTails };
|
|
240
|
-
type Presented = Omit<RunView, 'steps'> & {
|
|
262
|
+
type Presented = Omit<RunView, 'steps'> & {
|
|
263
|
+
this_step: string | null;
|
|
264
|
+
steps: PresentedStep[];
|
|
265
|
+
/**
|
|
266
|
+
* The verdict the BODY declared, beside — never instead of — the kernel
|
|
267
|
+
* facts above. `status`, `completion_reason` and every step stay exactly
|
|
268
|
+
* what the journal recorded, because they are true: an authored
|
|
269
|
+
* `step_failed` run completes with a root step that succeeded.
|
|
270
|
+
*
|
|
271
|
+
* Present only when the body actually said why. A one-argument `done()`
|
|
272
|
+
* adds nothing a reader could not already see from `completion_reason`, and
|
|
273
|
+
* emitting a null here for every legacy and non-authored run would change a
|
|
274
|
+
* shape that nobody asked to change.
|
|
275
|
+
*/
|
|
276
|
+
authored_completion?: { reason: string; detail: string };
|
|
277
|
+
};
|
|
241
278
|
|
|
242
279
|
/** Apply the redaction and size bounds the on-disk model does not have. */
|
|
243
|
-
function present(
|
|
280
|
+
function present(
|
|
281
|
+
view: RunView,
|
|
282
|
+
thisStep: string | null,
|
|
283
|
+
tails: Map<string, StepTails>,
|
|
284
|
+
env: NodeJS.ProcessEnv,
|
|
285
|
+
authored: AuthoredVerdict | null,
|
|
286
|
+
): Presented {
|
|
244
287
|
return {
|
|
245
288
|
...view,
|
|
246
289
|
this_step: thisStep,
|
|
290
|
+
...(authored?.detail === undefined ? {} : {
|
|
291
|
+
// Redacted again on the way out. It was redacted before it was
|
|
292
|
+
// journaled, by this same redactor against a different environment;
|
|
293
|
+
// doing it here too costs nothing and keeps this module the one place
|
|
294
|
+
// that decides what reaches an agent-facing page.
|
|
295
|
+
authored_completion: { reason: authored.reason, detail: redact(authored.detail, env) },
|
|
296
|
+
}),
|
|
247
297
|
steps: view.steps.map((step) => ({
|
|
248
298
|
...step,
|
|
249
299
|
// An agent names the files it writes, and it inherits this process's
|
|
@@ -307,6 +357,12 @@ function renderText(view: Presented, partial: string[], wantTails: boolean): str
|
|
|
307
357
|
+ (view.spend.dollars_unmetered ? ' (unmetered)' : '');
|
|
308
358
|
const finished = view.completion_reason === null ? '' : ` finished ${view.completion_reason}`;
|
|
309
359
|
lines.push(`RUN ${view.run_id} ${safe(view.name)} ${view.status} started ${formatDuration(view.now_ms - view.spawned_at_ms)} ago${finished} spend ${spend}`);
|
|
360
|
+
// Labelled, so nobody reads a failure explanation as a claim that the run's
|
|
361
|
+
// kernel status is anything other than the line above says it is.
|
|
362
|
+
if (view.authored_completion !== undefined) {
|
|
363
|
+
lines.push(`authored done("${safe(view.authored_completion.reason)}"): `
|
|
364
|
+
+ safe(singleLineCompletionDetail(view.authored_completion.detail)));
|
|
365
|
+
}
|
|
310
366
|
const counts = (['done', 'running', 'pending', 'backoff', 'waiting', 'needs_human'] as const)
|
|
311
367
|
.filter((key) => view.counts[key] > 0).map((key) => `${view.counts[key]} ${key}`);
|
|
312
368
|
// The denominator is always printed, even for zero steps, so a reader can
|