@relayflows/sdk 2.0.42 → 2.0.44
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/adapters/base.d.ts +12 -1
- package/dist/adapters/base.d.ts.map +1 -1
- package/dist/adapters/base.js +11 -1
- package/dist/adapters/base.js.map +1 -1
- package/dist/adapters/claude.d.ts.map +1 -1
- package/dist/adapters/claude.js +8 -4
- package/dist/adapters/claude.js.map +1 -1
- package/dist/adapters/codex.d.ts.map +1 -1
- package/dist/adapters/codex.js +15 -6
- package/dist/adapters/codex.js.map +1 -1
- package/dist/agent-resume.d.ts +8 -0
- package/dist/agent-resume.d.ts.map +1 -0
- package/dist/agent-resume.js +37 -0
- package/dist/agent-resume.js.map +1 -0
- package/dist/agent-transcript.d.ts.map +1 -1
- package/dist/agent-transcript.js +5 -3
- package/dist/agent-transcript.js.map +1 -1
- package/dist/authored-flow-error.d.ts +10 -2
- package/dist/authored-flow-error.d.ts.map +1 -1
- package/dist/authored-flow-error.js.map +1 -1
- package/dist/authored-flow-executor.d.ts.map +1 -1
- package/dist/authored-flow-executor.js +16 -2
- package/dist/authored-flow-executor.js.map +1 -1
- package/dist/authored-flow-loader.d.ts +6 -0
- package/dist/authored-flow-loader.d.ts.map +1 -1
- package/dist/authored-flow-loader.js +18 -6
- package/dist/authored-flow-loader.js.map +1 -1
- package/dist/authored-helper-effect.js +3 -2
- package/dist/authored-helper-effect.js.map +1 -1
- package/dist/authored-human.d.ts +2 -0
- package/dist/authored-human.d.ts.map +1 -1
- package/dist/authored-human.js +4 -0
- package/dist/authored-human.js.map +1 -1
- package/dist/authored-memory.d.ts +47 -2
- package/dist/authored-memory.d.ts.map +1 -1
- package/dist/authored-memory.js +394 -15
- package/dist/authored-memory.js.map +1 -1
- package/dist/authored-node-entry.js +6 -1
- package/dist/authored-node-entry.js.map +1 -1
- package/dist/authored-node-runner.d.ts +1 -1
- package/dist/authored-node-runner.d.ts.map +1 -1
- package/dist/authored-node-runner.js +6 -3
- package/dist/authored-node-runner.js.map +1 -1
- package/dist/authored-root-readback.d.ts +21 -0
- package/dist/authored-root-readback.d.ts.map +1 -0
- package/dist/authored-root-readback.js +90 -0
- package/dist/authored-root-readback.js.map +1 -0
- package/dist/authored-root.d.ts.map +1 -1
- package/dist/authored-root.js +53 -74
- package/dist/authored-root.js.map +1 -1
- package/dist/authored-step-output.d.ts +2 -0
- package/dist/authored-step-output.d.ts.map +1 -1
- package/dist/authored-step-output.js +1 -0
- package/dist/authored-step-output.js.map +1 -1
- package/dist/authored-worker-step.d.ts.map +1 -1
- package/dist/authored-worker-step.js +26 -4
- package/dist/authored-worker-step.js.map +1 -1
- package/dist/cli/check-helper-body.d.ts +2 -2
- package/dist/cli/check-helper-body.d.ts.map +1 -1
- package/dist/cli/check-helper-body.js +7 -2
- package/dist/cli/check-helper-body.js.map +1 -1
- package/dist/cli/check-helper-surface.d.ts +8 -0
- package/dist/cli/check-helper-surface.d.ts.map +1 -0
- package/dist/cli/check-helper-surface.js +37 -0
- package/dist/cli/check-helper-surface.js.map +1 -0
- package/dist/cli/check-triggers.d.ts +4 -3
- package/dist/cli/check-triggers.d.ts.map +1 -1
- package/dist/cli/check-triggers.js +7 -3
- package/dist/cli/check-triggers.js.map +1 -1
- package/dist/cli/check.d.ts +7 -1
- package/dist/cli/check.d.ts.map +1 -1
- package/dist/cli/check.js +35 -8
- package/dist/cli/check.js.map +1 -1
- package/dist/cli/cloud-answer.d.ts +9 -0
- package/dist/cli/cloud-answer.d.ts.map +1 -0
- package/dist/cli/cloud-answer.js +39 -0
- package/dist/cli/cloud-answer.js.map +1 -0
- package/dist/cli/cloud-connect-cli.d.ts +2 -1
- package/dist/cli/cloud-connect-cli.d.ts.map +1 -1
- package/dist/cli/cloud-connect-cli.js +10 -2
- package/dist/cli/cloud-connect-cli.js.map +1 -1
- package/dist/cli/cloud-human.d.ts +7 -0
- package/dist/cli/cloud-human.d.ts.map +1 -0
- package/dist/cli/cloud-human.js +22 -0
- package/dist/cli/cloud-human.js.map +1 -0
- package/dist/cli/cloud-live.d.ts +2 -2
- package/dist/cli/cloud-live.d.ts.map +1 -1
- package/dist/cli/cloud-live.js +9 -2
- package/dist/cli/cloud-live.js.map +1 -1
- package/dist/cli/cloud-mirror-session.d.ts.map +1 -1
- package/dist/cli/cloud-mirror-session.js +3 -2
- package/dist/cli/cloud-mirror-session.js.map +1 -1
- package/dist/cli/cloud-run.d.ts +1 -1
- package/dist/cli/cloud-run.d.ts.map +1 -1
- package/dist/cli/cloud-run.js +12 -0
- package/dist/cli/cloud-run.js.map +1 -1
- package/dist/cli/detached-record.d.ts +22 -0
- package/dist/cli/detached-record.d.ts.map +1 -0
- package/dist/cli/detached-record.js +57 -0
- package/dist/cli/detached-record.js.map +1 -0
- package/dist/cli/detached-run.d.ts +28 -0
- package/dist/cli/detached-run.d.ts.map +1 -0
- package/dist/cli/detached-run.js +122 -0
- package/dist/cli/detached-run.js.map +1 -0
- package/dist/cli/diagnostic-fold.d.ts +19 -0
- package/dist/cli/diagnostic-fold.d.ts.map +1 -0
- package/dist/cli/diagnostic-fold.js +19 -0
- package/dist/cli/diagnostic-fold.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/eval.d.ts +22 -0
- package/dist/cli/eval.d.ts.map +1 -0
- package/dist/cli/eval.js +131 -0
- package/dist/cli/eval.js.map +1 -0
- package/dist/cli/journal-timeout.d.ts +12 -0
- package/dist/cli/journal-timeout.d.ts.map +1 -0
- package/dist/cli/journal-timeout.js +42 -0
- package/dist/cli/journal-timeout.js.map +1 -0
- package/dist/cli/local-agent-remedy.d.ts.map +1 -1
- package/dist/cli/local-agent-remedy.js +5 -4
- package/dist/cli/local-agent-remedy.js.map +1 -1
- package/dist/cli/observer-session.d.ts +1 -0
- package/dist/cli/observer-session.d.ts.map +1 -1
- package/dist/cli/observer-session.js +8 -1
- package/dist/cli/observer-session.js.map +1 -1
- package/dist/cli/reuse.d.ts +8 -0
- package/dist/cli/reuse.d.ts.map +1 -1
- package/dist/cli/reuse.js +24 -0
- package/dist/cli/reuse.js.map +1 -1
- package/dist/cli/run.d.ts +8 -2
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +72 -56
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/running-step.d.ts +9 -0
- package/dist/cli/running-step.d.ts.map +1 -0
- package/dist/cli/running-step.js +156 -0
- package/dist/cli/running-step.js.map +1 -0
- package/dist/cli/status.d.ts.map +1 -1
- package/dist/cli/status.js +12 -8
- package/dist/cli/status.js.map +1 -1
- package/dist/cli-adapter.d.ts +3 -1
- package/dist/cli-adapter.d.ts.map +1 -1
- package/dist/cli-adapter.js +6 -2
- package/dist/cli-adapter.js.map +1 -1
- package/dist/cli-commands.d.ts +77 -11
- package/dist/cli-commands.d.ts.map +1 -1
- package/dist/cli-commands.js +41 -7
- package/dist/cli-commands.js.map +1 -1
- package/dist/cli-watch.d.ts +6 -2
- package/dist/cli-watch.d.ts.map +1 -1
- package/dist/cli-watch.js +7 -6
- package/dist/cli-watch.js.map +1 -1
- package/dist/cli.d.ts +22 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +154 -50
- package/dist/cli.js.map +1 -1
- package/dist/cloud-answer.d.ts +35 -0
- package/dist/cloud-answer.d.ts.map +1 -0
- package/dist/cloud-answer.js +194 -0
- package/dist/cloud-answer.js.map +1 -0
- package/dist/cloud-auth-store.d.ts +46 -0
- package/dist/cloud-auth-store.d.ts.map +1 -0
- package/dist/cloud-auth-store.js +171 -0
- package/dist/cloud-auth-store.js.map +1 -0
- package/dist/cloud-http.d.ts +7 -16
- package/dist/cloud-http.d.ts.map +1 -1
- package/dist/cloud-http.js +59 -40
- package/dist/cloud-http.js.map +1 -1
- package/dist/cloud-human-wait.d.ts +10 -0
- package/dist/cloud-human-wait.d.ts.map +1 -0
- package/dist/cloud-human-wait.js +17 -0
- package/dist/cloud-human-wait.js.map +1 -0
- package/dist/cloud-mirror-transport.d.ts.map +1 -1
- package/dist/cloud-mirror-transport.js +1 -0
- package/dist/cloud-mirror-transport.js.map +1 -1
- package/dist/cloud-run-record.d.ts +7 -2
- package/dist/cloud-run-record.d.ts.map +1 -1
- package/dist/cloud-run-record.js +13 -5
- package/dist/cloud-run-record.js.map +1 -1
- package/dist/cloud-run.d.ts +9 -0
- package/dist/cloud-run.d.ts.map +1 -1
- package/dist/cloud-run.js +36 -2
- package/dist/cloud-run.js.map +1 -1
- package/dist/communication/local.d.ts.map +1 -1
- package/dist/communication/local.js +6 -1
- package/dist/communication/local.js.map +1 -1
- package/dist/communication/worker.d.ts.map +1 -1
- package/dist/communication/worker.js +12 -2
- package/dist/communication/worker.js.map +1 -1
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +5 -3
- package/dist/compile.js.map +1 -1
- package/dist/daemon-spec-validation.d.ts +31 -0
- package/dist/daemon-spec-validation.d.ts.map +1 -0
- package/dist/daemon-spec-validation.js +115 -0
- package/dist/daemon-spec-validation.js.map +1 -0
- package/dist/failure-kinds.d.ts +6 -4
- package/dist/failure-kinds.d.ts.map +1 -1
- package/dist/failure-kinds.js +17 -1
- package/dist/failure-kinds.js.map +1 -1
- package/dist/flow-eval-cloud.d.ts +25 -0
- package/dist/flow-eval-cloud.d.ts.map +1 -0
- package/dist/flow-eval-cloud.js +108 -0
- package/dist/flow-eval-cloud.js.map +1 -0
- package/dist/flow-eval-error.d.ts +7 -0
- package/dist/flow-eval-error.d.ts.map +1 -0
- package/dist/flow-eval-error.js +10 -0
- package/dist/flow-eval-error.js.map +1 -0
- package/dist/flow-eval-gate.d.ts +15 -0
- package/dist/flow-eval-gate.d.ts.map +1 -0
- package/dist/flow-eval-gate.js +102 -0
- package/dist/flow-eval-gate.js.map +1 -0
- package/dist/flow-eval-local.d.ts +54 -0
- package/dist/flow-eval-local.d.ts.map +1 -0
- package/dist/flow-eval-local.js +220 -0
- package/dist/flow-eval-local.js.map +1 -0
- package/dist/flow-eval-outcome.d.ts +22 -0
- package/dist/flow-eval-outcome.d.ts.map +1 -0
- package/dist/flow-eval-outcome.js +37 -0
- package/dist/flow-eval-outcome.js.map +1 -0
- package/dist/flow-eval-report.d.ts +106 -0
- package/dist/flow-eval-report.d.ts.map +1 -0
- package/dist/flow-eval-report.js +4 -0
- package/dist/flow-eval-report.js.map +1 -0
- package/dist/flow-eval-suite.d.ts +46 -0
- package/dist/flow-eval-suite.d.ts.map +1 -0
- package/dist/flow-eval-suite.js +144 -0
- package/dist/flow-eval-suite.js.map +1 -0
- package/dist/flow-eval-version.d.ts +59 -0
- package/dist/flow-eval-version.d.ts.map +1 -0
- package/dist/flow-eval-version.js +303 -0
- package/dist/flow-eval-version.js.map +1 -0
- package/dist/flow-eval.d.ts +41 -0
- package/dist/flow-eval.d.ts.map +1 -0
- package/dist/flow-eval.js +201 -0
- package/dist/flow-eval.js.map +1 -0
- package/dist/helper-operation-use.d.ts +18 -0
- package/dist/helper-operation-use.d.ts.map +1 -0
- package/dist/helper-operation-use.js +336 -0
- package/dist/helper-operation-use.js.map +1 -0
- package/dist/helper-preflight.d.ts.map +1 -1
- package/dist/helper-preflight.js +16 -2
- package/dist/helper-preflight.js.map +1 -1
- package/dist/helper-reference.d.ts +33 -0
- package/dist/helper-reference.d.ts.map +1 -1
- package/dist/helper-reference.js +5 -5
- package/dist/helper-reference.js.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/journal-budgeted-reads.d.ts +41 -0
- package/dist/journal-budgeted-reads.d.ts.map +1 -0
- package/dist/journal-budgeted-reads.js +142 -0
- package/dist/journal-budgeted-reads.js.map +1 -0
- package/dist/journal-client.d.ts +34 -29
- package/dist/journal-client.d.ts.map +1 -1
- package/dist/journal-client.js +45 -151
- package/dist/journal-client.js.map +1 -1
- package/dist/journal-connection.d.ts +42 -0
- package/dist/journal-connection.d.ts.map +1 -0
- package/dist/journal-connection.js +209 -0
- package/dist/journal-connection.js.map +1 -0
- package/dist/journal-read-policy.d.ts +38 -0
- package/dist/journal-read-policy.d.ts.map +1 -0
- package/dist/journal-read-policy.js +126 -0
- package/dist/journal-read-policy.js.map +1 -0
- package/dist/named-gate-lowering.d.ts.map +1 -1
- package/dist/named-gate-lowering.js +2 -6
- package/dist/named-gate-lowering.js.map +1 -1
- package/dist/re2js-embedded.d.ts +3 -0
- package/dist/re2js-embedded.d.ts.map +1 -0
- package/dist/re2js-embedded.js +4 -0
- package/dist/re2js-embedded.js.map +1 -0
- package/dist/spec.d.ts +10 -0
- 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/validate.d.ts.map +1 -1
- package/dist/validate.js +15 -0
- package/dist/validate.js.map +1 -1
- package/dist/worker-cli.d.ts +3 -1
- package/dist/worker-cli.d.ts.map +1 -1
- package/dist/worker-cli.js +15 -5
- package/dist/worker-cli.js.map +1 -1
- package/dist/worker-lease.d.ts.map +1 -1
- package/dist/worker-lease.js +11 -3
- package/dist/worker-lease.js.map +1 -1
- package/dist/worker.js +1 -1
- package/dist/worker.js.map +1 -1
- package/package.json +5 -3
- package/src/adapters/base.ts +16 -1
- package/src/adapters/claude.ts +8 -4
- package/src/adapters/codex.ts +15 -6
- package/src/agent-resume.ts +36 -0
- package/src/agent-transcript.ts +5 -3
- package/src/authored-flow-error.ts +9 -0
- package/src/authored-flow-executor.ts +18 -1
- package/src/authored-flow-loader.ts +27 -10
- package/src/authored-helper-effect.ts +3 -2
- package/src/authored-human.ts +5 -0
- package/src/authored-memory.ts +445 -15
- package/src/authored-node-entry.ts +6 -1
- package/src/authored-node-runner.ts +6 -3
- package/src/authored-root-readback.ts +100 -0
- package/src/authored-root.ts +58 -85
- package/src/authored-step-output.ts +3 -0
- package/src/authored-worker-step.ts +29 -4
- package/src/cli/check-helper-body.ts +9 -3
- package/src/cli/check-helper-surface.ts +43 -0
- package/src/cli/check-triggers.ts +9 -4
- package/src/cli/check.ts +37 -7
- package/src/cli/cloud-answer.ts +40 -0
- package/src/cli/cloud-connect-cli.ts +9 -2
- package/src/cli/cloud-human.ts +24 -0
- package/src/cli/cloud-live.ts +10 -5
- package/src/cli/cloud-mirror-session.ts +3 -2
- package/src/cli/cloud-run.ts +10 -1
- package/src/cli/detached-record.ts +71 -0
- package/src/cli/detached-run.ts +131 -0
- package/src/cli/diagnostic-fold.ts +28 -0
- package/src/cli/direct-run.ts +14 -1
- package/src/cli/eval.ts +141 -0
- package/src/cli/journal-timeout.ts +49 -0
- package/src/cli/local-agent-remedy.ts +6 -4
- package/src/cli/observer-session.ts +7 -1
- package/src/cli/reuse.ts +25 -0
- package/src/cli/run.ts +75 -67
- package/src/cli/running-step.ts +140 -0
- package/src/cli/status.ts +19 -13
- package/src/cli-adapter.ts +7 -1
- package/src/cli-commands.ts +43 -7
- package/src/cli-watch.ts +8 -5
- package/src/cli.ts +158 -51
- package/src/cloud-answer.ts +202 -0
- package/src/cloud-auth-store.ts +185 -0
- package/src/cloud-http.ts +61 -50
- package/src/cloud-human-wait.ts +23 -0
- package/src/cloud-mirror-transport.ts +1 -0
- package/src/cloud-run-record.ts +14 -5
- package/src/cloud-run.ts +46 -2
- package/src/communication/local.ts +6 -1
- package/src/communication/worker.ts +11 -3
- package/src/compile.ts +5 -3
- package/src/daemon-spec-validation.ts +126 -0
- package/src/failure-kinds.ts +17 -1
- package/src/flow-eval-cloud.ts +114 -0
- package/src/flow-eval-error.ts +10 -0
- package/src/flow-eval-gate.ts +114 -0
- package/src/flow-eval-local.ts +236 -0
- package/src/flow-eval-outcome.ts +43 -0
- package/src/flow-eval-report.ts +102 -0
- package/src/flow-eval-suite.ts +169 -0
- package/src/flow-eval-version.ts +316 -0
- package/src/flow-eval.ts +234 -0
- package/src/helper-operation-use.ts +285 -0
- package/src/helper-preflight.ts +17 -2
- package/src/helper-reference.ts +6 -6
- package/src/index.ts +15 -1
- package/src/journal-budgeted-reads.ts +134 -0
- package/src/journal-client.ts +59 -166
- package/src/journal-connection.ts +222 -0
- package/src/journal-read-policy.ts +123 -0
- package/src/named-gate-lowering.ts +2 -6
- package/src/re2js-embedded.ts +3 -0
- package/src/spec.ts +10 -0
- package/src/step-fields.ts +1 -1
- package/src/validate.ts +14 -0
- package/src/worker-cli.ts +14 -3
- package/src/worker-lease.ts +11 -3
- package/src/worker.ts +1 -1
package/src/authored-root.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isReadInterruptionError } from './journal-read-policy.js';
|
|
1
2
|
import { loadPinnedAuthoredSource } from './authored-source-authority.js';
|
|
2
3
|
import { assertAuthoredRuntimeAvailable, runAuthoredInNode } from './authored-node-runner.js';
|
|
3
4
|
import { createHash, randomUUID } from 'node:crypto';
|
|
@@ -5,7 +6,6 @@ import { readFile } from 'node:fs/promises';
|
|
|
5
6
|
import { canonicalize } from './canonical.js';
|
|
6
7
|
import { compileSpec, toKernelSpec } from './compile.js';
|
|
7
8
|
import { executeAuthoredFlow, type AuthoredFlowExecutionResult, type AuthoredFlowSuspendedResult } from './authored-flow-executor.js';
|
|
8
|
-
import { isDurableCompletionDetail, isLoweredCompletion } from './authored-completion.js';
|
|
9
9
|
import { AUTHORED_ROOT_KIND } from './authored-verdict.js';
|
|
10
10
|
import {
|
|
11
11
|
loadAuthoredFlow,
|
|
@@ -19,9 +19,9 @@ import type { RunLifecycleOptions } from './cli/run.js';
|
|
|
19
19
|
import { isLeaseLost, withWorkerLease } from './worker-lease.js';
|
|
20
20
|
import { AuthoredFlowExecutionError, AuthoredHumanParked } from './authored-flow-error.js';
|
|
21
21
|
import { readOpenHumanWaits, resumeCommand } from './authored-human.js';
|
|
22
|
-
import { isSurfaceCompletionReason } from './authored-step-output.js';
|
|
23
22
|
import { readSubscriptionPark } from './authored-subscription-park.js';
|
|
24
23
|
import { localAgentCredentialEnvironment } from './local-agent-environment.js';
|
|
24
|
+
import { completedResult, rootKernelStep, rootStepIfPresent } from './authored-root-readback.js';
|
|
25
25
|
|
|
26
26
|
export type DurableAuthoredFlowResult =
|
|
27
27
|
| (AuthoredFlowExecutionResult & { readonly rootRunId: string })
|
|
@@ -117,9 +117,10 @@ export async function executeDurableAuthoredFlow(
|
|
|
117
117
|
});
|
|
118
118
|
const outcome = await journal.runStart(spec, undefined, admissionKey);
|
|
119
119
|
options.onAdmitted?.(outcome.run_id);
|
|
120
|
+
options.lifecycle?.onRunReceipt?.({ runId: outcome.run_id, flow: definition.name });
|
|
120
121
|
if (outcome.status === 'completed') {
|
|
121
122
|
dispatchWait.cancel();
|
|
122
|
-
return await
|
|
123
|
+
return await completedResult(journal, outcome, outcome.run_id);
|
|
123
124
|
}
|
|
124
125
|
assertRootCanDispatch(outcome);
|
|
125
126
|
options.lifecycle?.onRunStarted?.({ runId: outcome.run_id, flow: definition.name });
|
|
@@ -166,9 +167,10 @@ export async function resumeDurableAuthoredFlow(
|
|
|
166
167
|
workspace: [], streams: [{ stream, read_offset: 0 }],
|
|
167
168
|
});
|
|
168
169
|
const outcome = await journal.runResume(rootRunId);
|
|
170
|
+
options.lifecycle?.onRunReceipt?.({ runId: rootRunId, flow: metadata.flowName });
|
|
169
171
|
if (outcome.status === 'completed') {
|
|
170
172
|
dispatchWait.cancel();
|
|
171
|
-
return await
|
|
173
|
+
return await completedResult(journal, outcome, rootRunId);
|
|
172
174
|
}
|
|
173
175
|
assertRootCanDispatch(outcome);
|
|
174
176
|
await assertNoOpenHumanWait(journal, outcome);
|
|
@@ -199,7 +201,7 @@ export async function readAuthoredRootMetadata(
|
|
|
199
201
|
journal: JournalClient,
|
|
200
202
|
runId: string,
|
|
201
203
|
): Promise<AuthoredRootMetadata | undefined> {
|
|
202
|
-
const step = await
|
|
204
|
+
const step = await rootStepIfPresent(journal, runId);
|
|
203
205
|
if (step === undefined || step.id !== 'authored-root' || !hasRootStream(step)) return undefined;
|
|
204
206
|
if (typeof step.instruction !== 'string') {
|
|
205
207
|
throw new Error('authored root journal has malformed authority metadata');
|
|
@@ -320,38 +322,43 @@ async function driveRoot(
|
|
|
320
322
|
): Promise<DurableAuthoredFlowResult> {
|
|
321
323
|
try {
|
|
322
324
|
const result = await withWorkerLease(peer, dispatch, async rootSignal => {
|
|
323
|
-
|
|
324
|
-
const
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
:
|
|
353
|
-
|
|
354
|
-
|
|
325
|
+
// A lost root lease must also stop body reads still inside their budget.
|
|
326
|
+
const releaseReads = journal.scopeReads?.(rootSignal) ?? (() => {});
|
|
327
|
+
try {
|
|
328
|
+
const callerSignal = options.lifecycle?.signal;
|
|
329
|
+
const signal = callerSignal === undefined ? rootSignal : AbortSignal.any([callerSignal, rootSignal]);
|
|
330
|
+
if (process.versions['bun'] !== undefined) {
|
|
331
|
+
// Awaited, so the read scope below covers the child's whole run.
|
|
332
|
+
return await runAuthoredInNode(metadata, journal.socketPath, dispatch.run_id, {
|
|
333
|
+
dataDir: options.dataDir, localAgentStream: options.localAgentStream,
|
|
334
|
+
...(options.agentEnvironment === undefined ? {} : {
|
|
335
|
+
agentEnvironment: localAgentCredentialEnvironment(options.agentEnvironment),
|
|
336
|
+
}),
|
|
337
|
+
...(options.workerCapacity === undefined ? {} : { workerCapacity: options.workerCapacity }),
|
|
338
|
+
...options.lifecycle, signal,
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
return await executeAuthoredFlow(
|
|
342
|
+
loaded.handle,
|
|
343
|
+
journal,
|
|
344
|
+
metadata.inputPresent ? metadata.input : undefined,
|
|
345
|
+
{
|
|
346
|
+
getDefinition: loaded.getDefinition,
|
|
347
|
+
dataDir: options.dataDir,
|
|
348
|
+
flowPath: metadata.flowPath,
|
|
349
|
+
localAgentStream: options.localAgentStream,
|
|
350
|
+
...(options.agentEnvironment === undefined ? {} : { agentEnvironment: options.agentEnvironment }),
|
|
351
|
+
...(options.workerCapacity === undefined ? {} : { workerCapacity: options.workerCapacity }),
|
|
352
|
+
rootRunId: dispatch.run_id,
|
|
353
|
+
extensions: loaded.extensions,
|
|
354
|
+
flowGraph: loaded.graph,
|
|
355
|
+
...options.lifecycle,
|
|
356
|
+
signal: callerSignal === undefined
|
|
357
|
+
? rootSignal
|
|
358
|
+
: AbortSignal.any([callerSignal, rootSignal]),
|
|
359
|
+
},
|
|
360
|
+
);
|
|
361
|
+
} finally { releaseReads(); }
|
|
355
362
|
});
|
|
356
363
|
await peer.stepComplete(
|
|
357
364
|
dispatch.run_id, dispatch.step_id, dispatch.attempt,
|
|
@@ -368,6 +375,20 @@ async function driveRoot(
|
|
|
368
375
|
// A lost lease is owned by the kernel's retry path, not by this attempt's
|
|
369
376
|
// terminalization or suspension handling.
|
|
370
377
|
if (isLeaseLost(error)) throw error;
|
|
378
|
+
// A caller cancel is the caller's, not a body failure: reads aborted by it
|
|
379
|
+
// (including the Bun verifier's) leave the root resumable, never terminal.
|
|
380
|
+
const signal = options.lifecycle?.signal;
|
|
381
|
+
if (aborted(signal) && (error === signal!.reason || (error instanceof Error && error.name === 'AbortError'))) {
|
|
382
|
+
if (error instanceof Error) (error as Error & { rootRunId?: string }).rootRunId = dispatch.run_id;
|
|
383
|
+
throw error;
|
|
384
|
+
}
|
|
385
|
+
if (isReadInterruptionError(error)
|
|
386
|
+
|| (error instanceof AuthoredFlowExecutionError && error.code === 'daemon_unresponsive')) {
|
|
387
|
+
const parked = new AuthoredFlowExecutionError('daemon_unresponsive',
|
|
388
|
+
`${error.message}. The run remains resumable. Continue with: ${resumeCommand(dispatch.run_id, options.dataDir, options.localAgentStream !== undefined)}.`);
|
|
389
|
+
parked.rootRunId = dispatch.run_id;
|
|
390
|
+
throw parked;
|
|
391
|
+
}
|
|
371
392
|
if (error instanceof AuthoredFlowExecutionError
|
|
372
393
|
&& error.code === 'subscription_suspended'
|
|
373
394
|
&& error.suspension !== undefined) {
|
|
@@ -450,16 +471,6 @@ function rootSpec(metadata: AuthoredRootMetadata, stream: string) {
|
|
|
450
471
|
}));
|
|
451
472
|
}
|
|
452
473
|
|
|
453
|
-
async function rootKernelStep(journal: JournalClient, runId: string): Promise<Record<string, unknown>> {
|
|
454
|
-
const entries = (await journal.journalRead(runId, 1)).entries as Array<Record<string, unknown>>;
|
|
455
|
-
const spawned = entries.find(entry => entry.entry_type === 'run.spawned');
|
|
456
|
-
const payload = spawned?.payload as { spec?: { steps?: unknown[] } } | undefined;
|
|
457
|
-
const step = payload?.spec?.steps?.[0];
|
|
458
|
-
if (typeof step !== 'object' || step === null || Array.isArray(step)) {
|
|
459
|
-
throw new Error('authored root journal has no root step');
|
|
460
|
-
}
|
|
461
|
-
return step as Record<string, unknown>;
|
|
462
|
-
}
|
|
463
474
|
|
|
464
475
|
function rootStreamFromMetadata(step: Record<string, unknown>): string {
|
|
465
476
|
const surfaces = step.surfaces as { streams?: Array<{ stream?: unknown }> } | undefined;
|
|
@@ -543,44 +554,6 @@ function nextRootDispatch(peer: JournalClient, timeoutMs = 30_000): {
|
|
|
543
554
|
return { promise, cancel };
|
|
544
555
|
}
|
|
545
556
|
|
|
546
|
-
async function completedRootResult(
|
|
547
|
-
journal: JournalClient,
|
|
548
|
-
rootRunId: string,
|
|
549
|
-
): Promise<AuthoredFlowExecutionResult & { readonly rootRunId: string }> {
|
|
550
|
-
const entries = (await journal.journalRead(rootRunId, 1)).entries as Array<Record<string, unknown>>;
|
|
551
|
-
const completed = entries.slice().reverse().find(entry => entry.entry_type === 'step.completed'
|
|
552
|
-
&& entry.step_id === 'authored-root'
|
|
553
|
-
&& (entry.payload as { completionReason?: unknown } | undefined)?.completionReason === 'success');
|
|
554
|
-
const output = (completed?.payload as { output?: unknown } | undefined)?.output;
|
|
555
|
-
if (!isCompletedRootOutput(output)) {
|
|
556
|
-
throw new Error('completed authored root has no durable result');
|
|
557
|
-
}
|
|
558
|
-
// The stored detail, never a freshly computed one: redaction reads the
|
|
559
|
-
// CURRENT environment, so recomputing here would let a completed run report
|
|
560
|
-
// something its journal does not hold.
|
|
561
|
-
return Object.freeze({
|
|
562
|
-
name: output.name,
|
|
563
|
-
completionReason: output.completionReason,
|
|
564
|
-
...(output.completionDetail === undefined ? {} : { completionDetail: output.completionDetail }),
|
|
565
|
-
journalSteps: Object.freeze(output.journalSteps.map(step => Object.freeze({ ...step }))),
|
|
566
|
-
rootRunId,
|
|
567
|
-
});
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
function isCompletedRootOutput(value: unknown): value is Omit<AuthoredFlowExecutionResult, 'rootRunId'> {
|
|
571
|
-
if (typeof value !== 'object' || value === null || Array.isArray(value)) return false;
|
|
572
|
-
const output = value as Partial<AuthoredFlowExecutionResult>;
|
|
573
|
-
return typeof output.name === 'string'
|
|
574
|
-
&& isLoweredCompletion(output.completionReason)
|
|
575
|
-
// A malformed detail fails the whole readback rather than being dropped:
|
|
576
|
-
// silently discarding it would report the verdict without the evidence
|
|
577
|
-
// the journal says it was recorded with.
|
|
578
|
-
&& (output.completionDetail === undefined || isDurableCompletionDetail(output.completionDetail))
|
|
579
|
-
&& Array.isArray(output.journalSteps)
|
|
580
|
-
&& output.journalSteps.every(step => typeof step === 'object' && step !== null
|
|
581
|
-
&& typeof step.id === 'string' && typeof step.runId === 'string'
|
|
582
|
-
&& isSurfaceCompletionReason(step.completionReason));
|
|
583
|
-
}
|
|
584
557
|
|
|
585
558
|
function assertRootCanDispatch(outcome: RunOutcome): void {
|
|
586
559
|
// The kernel reports `parked` after handing an agent lease to its worker;
|
|
@@ -23,6 +23,8 @@ export interface AuthoredStepContext {
|
|
|
23
23
|
readonly stepEdges?: (step: string) => AuthoredStepEdges | undefined;
|
|
24
24
|
/** Cancels the wait for an adopted child run that is not yet terminal. */
|
|
25
25
|
readonly signal?: AbortSignal;
|
|
26
|
+
/** Sees the accepted completion's payload (its evidence, not only its output). */
|
|
27
|
+
readonly onCompleted?: (payload: Readonly<Record<string, unknown>>) => void;
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
/**
|
|
@@ -149,6 +151,7 @@ export async function readCompletedStepOutput(
|
|
|
149
151
|
await recordAuthoredChild(journal, context.rootRunId, {
|
|
150
152
|
step: stepId, runId, state: 'completed', completionReason: reason, ...edges,
|
|
151
153
|
});
|
|
154
|
+
context.onCompleted?.(completed.payload as unknown as Readonly<Record<string, unknown>>);
|
|
152
155
|
return completed.payload.output;
|
|
153
156
|
}
|
|
154
157
|
|
|
@@ -9,6 +9,7 @@ import { classifyOutcome, type RunLifecycleOptions, type RunReport } from './cli
|
|
|
9
9
|
import type { PreflightDiagnostic } from './preflight.js';
|
|
10
10
|
import { AuthoredFlowExecutionError } from './authored-flow-error.js';
|
|
11
11
|
import { agentCwdDeclarationError, agentCwdTransportError } from './agent-cwd.js';
|
|
12
|
+
import { agentResumeDeclarationError, agentResumeTransportError } from './agent-resume.js';
|
|
12
13
|
import type { JournalClient } from './journal-client.js';
|
|
13
14
|
import { SPEC_SCHEMA_VERSION, type FlowSpec, type PermissionsSpec, type StepSpec } from './spec.js';
|
|
14
15
|
import { isSurfaceCompletionReason, readCompletedStepOutput, readRecordedOutcome, readSuccessfulOutput, type AuthoredStepContext, type RecordedRunOutcome } from './authored-step-output.js';
|
|
@@ -46,7 +47,7 @@ export function authoredWorkerRunner(
|
|
|
46
47
|
...(stepEdges === undefined ? {} : { stepEdges }),
|
|
47
48
|
...(waitOptions.signal === undefined ? {} : { signal: waitOptions.signal }),
|
|
48
49
|
};
|
|
49
|
-
async function run(step: StepSpec): Promise<unknown> {
|
|
50
|
+
async function run(step: StepSpec, onCompleted?: AuthoredStepContext['onCompleted']): Promise<unknown> {
|
|
50
51
|
const id = step.id;
|
|
51
52
|
const authoring: FlowSpec = { version: SPEC_SCHEMA_VERSION, name: `${definition.name}/${id}`, steps: [step], ...(headerBudget === undefined ? {} : { budget: parseBudget(headerBudget) }) };
|
|
52
53
|
// The kernel never resolves a `cli` on its own — every declarative
|
|
@@ -152,7 +153,8 @@ export function authoredWorkerRunner(
|
|
|
152
153
|
details,
|
|
153
154
|
);
|
|
154
155
|
}
|
|
155
|
-
return readCompletedStepOutput(journal, outcome.run_id, id, journalSteps,
|
|
156
|
+
return readCompletedStepOutput(journal, outcome.run_id, id, journalSteps,
|
|
157
|
+
onCompleted === undefined ? context : { ...context, onCompleted }, execution.report.status);
|
|
156
158
|
};
|
|
157
159
|
const admissionKey = authoredChildAdmissionKey(rootRunId, id);
|
|
158
160
|
const admit = async () => budget === undefined
|
|
@@ -250,6 +252,11 @@ export function authoredWorkerRunner(
|
|
|
250
252
|
`f.agent options.recoveryMode must be 'reset', 'inspect', or 'manual' (got ${JSON.stringify(options.recoveryMode)}).`,
|
|
251
253
|
);
|
|
252
254
|
}
|
|
255
|
+
const resumeProblem = options.resume === undefined ? undefined
|
|
256
|
+
: agentResumeDeclarationError(options.resume) ?? agentResumeTransportError(options.resume, options.transport);
|
|
257
|
+
if (resumeProblem !== undefined) {
|
|
258
|
+
throw new AuthoredFlowExecutionError('agent_cli_unresolved', `f.agent options.resume: ${resumeProblem}.`);
|
|
259
|
+
}
|
|
253
260
|
const cwdTransport = agentCwdTransportError(declaredCwd, options.transport);
|
|
254
261
|
if (cwdTransport !== undefined) {
|
|
255
262
|
throw new AuthoredFlowExecutionError('agent_cli_unresolved', `f.agent options.${cwdTransport}.`);
|
|
@@ -263,6 +270,7 @@ export function authoredWorkerRunner(
|
|
|
263
270
|
'f.agent timeout requires direct transport and maxIterations 1.');
|
|
264
271
|
}
|
|
265
272
|
}
|
|
273
|
+
let sessionId: string | undefined;
|
|
266
274
|
const permissions = options.permissions;
|
|
267
275
|
const permissionsSnapshot = permissions === undefined ? undefined
|
|
268
276
|
: snapshotJsonValue(permissions, 'f.agent options.permissions') as unknown as PermissionsSpec;
|
|
@@ -275,12 +283,13 @@ export function authoredWorkerRunner(
|
|
|
275
283
|
...(options.cli === undefined ? {} : { cli: options.cli }),
|
|
276
284
|
...(options.model === undefined ? {} : { model: options.model }),
|
|
277
285
|
...(declaredCwd === undefined ? {} : { cwd: declaredCwd }),
|
|
286
|
+
...(options.resume === undefined ? {} : { resume: options.resume }),
|
|
278
287
|
...(options.transport === undefined ? {} : { transport: options.transport }),
|
|
279
288
|
...(options.maxIterations === undefined ? {} : { maxIterations: options.maxIterations }),
|
|
280
289
|
...(options.transportRetries === undefined ? {} : { transportRetries: options.transportRetries }),
|
|
281
290
|
...(options.recoveryMode === undefined ? {} : { recoveryMode: options.recoveryMode }),
|
|
282
291
|
...(verification === undefined ? {} : { verification }),
|
|
283
|
-
});
|
|
292
|
+
}, (payload) => { sessionId = journaledSessionId(payload); });
|
|
284
293
|
if (typeof output !== 'object' || output === null || Array.isArray(output)) {
|
|
285
294
|
throw new AuthoredFlowExecutionError('journal_protocol_violation', `step "${id}" produced a non-object output`);
|
|
286
295
|
}
|
|
@@ -294,7 +303,10 @@ export function authoredWorkerRunner(
|
|
|
294
303
|
const journaled = 'artifacts' in output ? output.artifacts : undefined;
|
|
295
304
|
const artifacts = Array.isArray(journaled) && journaled.every(entry => typeof entry === 'string')
|
|
296
305
|
? [...journaled] : [];
|
|
297
|
-
return {
|
|
306
|
+
return {
|
|
307
|
+
completionReason: 'success', summary: typeof stdout === 'string' ? stdout : JSON.stringify(output), artifacts,
|
|
308
|
+
...(sessionId === undefined ? {} : { sessionId }),
|
|
309
|
+
};
|
|
298
310
|
},
|
|
299
311
|
async llm(id: string, prompt: string, options?: LlmOptions, verification?: NamedGate): Promise<unknown> {
|
|
300
312
|
if (options !== undefined && (typeof options !== 'object' || options === null || options.output === undefined)) {
|
|
@@ -411,3 +423,16 @@ function stepDetails(
|
|
|
411
423
|
};
|
|
412
424
|
return Object.keys(details).length === 0 ? undefined : details;
|
|
413
425
|
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* The CLI session the worker journaled for an agent attempt
|
|
429
|
+
* (`trajectory_tail.transcript.result.session_id`). Evidence, so it is read on
|
|
430
|
+
* the JSON-answer path too, and offered only when it is a session id a later
|
|
431
|
+
* `resume` would accept (a bounded label never is one).
|
|
432
|
+
*/
|
|
433
|
+
function journaledSessionId(payload: Readonly<Record<string, unknown>>): string | undefined {
|
|
434
|
+
const field = (value: unknown, key: string): unknown =>
|
|
435
|
+
value !== null && typeof value === 'object' && !Array.isArray(value) ? (value as Record<string, unknown>)[key] : undefined;
|
|
436
|
+
const id = field(field(field(payload.trajectory_tail, 'transcript'), 'result'), 'session_id');
|
|
437
|
+
return typeof id === 'string' && agentResumeDeclarationError(id) === undefined ? id : undefined;
|
|
438
|
+
}
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import { loadAuthoredFlow } from '../authored-flow-loader.js';
|
|
2
2
|
import { PluginError } from '../plugin-manifest.js';
|
|
3
3
|
import { checkSlackHelpers } from '../slack-preflight.js';
|
|
4
|
-
import { inputFailureReport, type CheckExecution } from './check.js';
|
|
4
|
+
import { inputFailureReport, type CheckInvocation, type CheckExecution } from './check.js';
|
|
5
|
+
|
|
6
|
+
import { helperCredentialDiagnostics } from './check-helper-surface.js';
|
|
5
7
|
|
|
6
8
|
/** Imports the definition, but never executes arbitrary authored body code. */
|
|
7
|
-
export async function checkHelperBody(path: string): Promise<CheckExecution> {
|
|
9
|
+
export async function checkHelperBody(path: string, invocation: CheckInvocation = {}): Promise<CheckExecution> {
|
|
8
10
|
try {
|
|
9
11
|
const { handle, getDefinition } = await loadAuthoredFlow(path);
|
|
10
|
-
|
|
12
|
+
const report = checkSlackHelpers(getDefinition(handle));
|
|
13
|
+
const diagnostics = invocation.warnUnresolvedHelperCredential === true
|
|
14
|
+
? helperCredentialDiagnostics(report.diagnostics).diagnostics : report.diagnostics;
|
|
15
|
+
return { report: { ...report, path, diagnostics,
|
|
16
|
+
ok: !diagnostics.some(diagnostic => diagnostic.severity === 'refusal') } };
|
|
11
17
|
} catch (error) {
|
|
12
18
|
if (error instanceof PluginError) {
|
|
13
19
|
return { report: inputFailureReport({ kind: error.code, message: error.message }, path) };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { helperProviders } from '@relayflows/surface/runtime';
|
|
2
|
+
import type { PreflightDiagnostic } from '../preflight.js';
|
|
3
|
+
import type { CheckWarningDiagnostic } from './check.js';
|
|
4
|
+
|
|
5
|
+
const mountKinds = new Set([
|
|
6
|
+
'helper_slack.credential_missing', 'helper_slack.mount_required',
|
|
7
|
+
'helper_provider.mount_required', 'helper_mount_required',
|
|
8
|
+
]);
|
|
9
|
+
|
|
10
|
+
/** Restate local mount failures for inspection only; execution stays strict. */
|
|
11
|
+
export function helperCredentialDiagnostics(diagnostics: readonly PreflightDiagnostic[]): {
|
|
12
|
+
diagnostics: Array<PreflightDiagnostic | CheckWarningDiagnostic>;
|
|
13
|
+
downgraded: boolean;
|
|
14
|
+
} {
|
|
15
|
+
const result: Array<PreflightDiagnostic | CheckWarningDiagnostic> = [];
|
|
16
|
+
const seen = new Set<string>();
|
|
17
|
+
for (const diagnostic of diagnostics) {
|
|
18
|
+
const helper = diagnostic.severity === 'refusal' && mountKinds.has(diagnostic.kind)
|
|
19
|
+
? helperProviders.find(({ namespace, provider }) =>
|
|
20
|
+
diagnostic.message.startsWith(`f.${namespace} requires `)
|
|
21
|
+
|| diagnostic.message.startsWith(`${provider} helper requires `))
|
|
22
|
+
: undefined;
|
|
23
|
+
// Unknown diagnostic shapes remain refusals rather than silently passing.
|
|
24
|
+
if (helper === undefined) {
|
|
25
|
+
result.push(diagnostic);
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (seen.has(helper.provider)) continue;
|
|
29
|
+
seen.add(helper.provider);
|
|
30
|
+
// YAML execution (helper_mount_required) honours a mock only for Slack
|
|
31
|
+
// (yaml-helper-effect.ts helperReady); every other YAML helper needs a mount.
|
|
32
|
+
const mockSupported = diagnostic.kind !== 'helper_mount_required' || helper.provider === 'slack';
|
|
33
|
+
result.push({
|
|
34
|
+
severity: 'warning', kind: 'helper_credential_unresolved',
|
|
35
|
+
message: `f.${helper.namespace} needs a ${helper.provider} mount, which is not available locally. `
|
|
36
|
+
+ 'flows schedule / flows deploy / flows run --cloud check the integration against your workspace at submit and refuse if Cloud cannot connect it. '
|
|
37
|
+
+ `A local flows run needs a relayfile ${helper.provider} mount (a ${helper.provider}/ directory under RELAYFILE_MOUNT_PATH)`
|
|
38
|
+
+ (mockSupported ? ` or ${helper.mockEnv}=1` : '')
|
|
39
|
+
+ `, and refuses with [${diagnostic.kind}] without one.`,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return { diagnostics: result, downgraded: seen.size > 0 };
|
|
43
|
+
}
|
|
@@ -7,16 +7,19 @@ import { scheduleLowering } from '../schedule-trigger.js';
|
|
|
7
7
|
import { checkSlackHelpers } from '../slack-preflight.js';
|
|
8
8
|
import { flowRequirements, mergeFlowExtensionRequirements } from '../flow-requirements.js';
|
|
9
9
|
import { PluginError } from '../plugin-manifest.js';
|
|
10
|
-
import { inputFailureReport, readProjectConfig, type CheckReport } from './check.js';
|
|
10
|
+
import { inputFailureReport, readProjectConfig, type CheckInvocation, type CheckReport } from './check.js';
|
|
11
|
+
|
|
12
|
+
import { helperCredentialDiagnostics } from './check-helper-surface.js';
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* Inspect an authored flow without running a handler or contacting the daemon.
|
|
14
16
|
* Combines the two authored-only preflight paths that share a loaded flow: the
|
|
15
17
|
* webhook-trigger check (E) and the f.slack helper check (B). Skipping either
|
|
16
18
|
* turned this into a silent trapdoor -- a `.flow.ts` using `f.slack.post`
|
|
17
|
-
* without a
|
|
19
|
+
* without a mount would silently pass inspection. Check may opt into a
|
|
20
|
+
* warning; local run keeps the default refusal before daemon attachment.
|
|
18
21
|
*/
|
|
19
|
-
export async function checkAuthoredTriggers(path: string): Promise<{
|
|
22
|
+
export async function checkAuthoredTriggers(path: string, invocation: CheckInvocation = {}): Promise<{
|
|
20
23
|
report: CheckReport;
|
|
21
24
|
loaded?: LoadedAuthoredFlow;
|
|
22
25
|
}> {
|
|
@@ -33,8 +36,10 @@ export async function checkAuthoredTriggers(path: string): Promise<{
|
|
|
33
36
|
// refused there, on the first real event.
|
|
34
37
|
const providerDiagnostics = preflightProviderTriggers(triggers);
|
|
35
38
|
const helperReport = checkSlackHelpers(definition);
|
|
39
|
+
const helperDiagnostics = invocation.warnUnresolvedHelperCredential === true
|
|
40
|
+
? helperCredentialDiagnostics(helperReport.diagnostics).diagnostics : helperReport.diagnostics;
|
|
36
41
|
const diagnostics = [
|
|
37
|
-
...triggerDiagnostics, ...providerDiagnostics, ...
|
|
42
|
+
...triggerDiagnostics, ...providerDiagnostics, ...helperDiagnostics,
|
|
38
43
|
];
|
|
39
44
|
// A schedule is inspectable data: print what it lowers to, and say plainly
|
|
40
45
|
// when the local runner cannot drive it. Neither is a refusal — Cloud can.
|
package/src/cli/check.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { validateSpecWithDaemon, type DaemonValidation, type DaemonValidationMode, type DaemonValidationDeps } from '../daemon-spec-validation.js';
|
|
1
2
|
import { communicationInstruction } from '../communication/spec.js';
|
|
2
3
|
import { rememberResolvedCliIdentities } from '../resolved-cli-identity.js';
|
|
3
4
|
import { checkCommunicationEnvironment } from '../communication/preflight.js';
|
|
@@ -5,6 +6,7 @@ import { accessSync, constants, readFileSync } from 'node:fs';
|
|
|
5
6
|
import { dirname, isAbsolute, join, parse as parsePath, resolve } from 'node:path';
|
|
6
7
|
import { parse as parseYaml } from 'yaml';
|
|
7
8
|
import { CompileError, compileSpec, kernelToAuthoring } from '../compile.js';
|
|
9
|
+
import { helperCredentialDiagnostics } from './check-helper-surface.js';
|
|
8
10
|
import { agentWorkerDiagnostics } from './check-worker-surface.js';
|
|
9
11
|
import { helperReady } from '../yaml-helper-effect.js';
|
|
10
12
|
import { flowRequirements, type FlowRequirements } from '../flow-requirements.js';
|
|
@@ -44,6 +46,7 @@ export interface ProjectConfig {
|
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
export interface CheckReport {
|
|
49
|
+
validation?: DaemonValidation;
|
|
47
50
|
mcpTools?: Readonly<Record<string, readonly string[]>>;
|
|
48
51
|
ok: boolean;
|
|
49
52
|
path?: string;
|
|
@@ -125,6 +128,10 @@ type CliProbeOutcomeMap = Map<string, CliProbeOutcome>;
|
|
|
125
128
|
* anything that depends on how the flow is about to be invoked opts in here.
|
|
126
129
|
*/
|
|
127
130
|
export interface CheckInvocation {
|
|
131
|
+
/** Only flows check opts into reporting missing local helper mounts as warnings. */
|
|
132
|
+
warnUnresolvedHelperCredential?: boolean;
|
|
133
|
+
daemonValidation?: DaemonValidationMode;
|
|
134
|
+
daemonValidationDeps?: DaemonValidationDeps;
|
|
128
135
|
/** Environment used only by provider executable/auth/model probes. */
|
|
129
136
|
environment?: NodeJS.ProcessEnv;
|
|
130
137
|
/**
|
|
@@ -159,7 +166,7 @@ export function checkFlow(path: string, invocation: CheckInvocation = {}): Check
|
|
|
159
166
|
execution = checkAuthoredFlow(readFlow(source, absolutePath), path, undefined, invocation);
|
|
160
167
|
} catch (error) {
|
|
161
168
|
if (!(error instanceof CheckFailure)) throw error;
|
|
162
|
-
execution = { report: inputFailureReport(error, path) };
|
|
169
|
+
execution = { report: inputFailureReport(error, path, invocation) };
|
|
163
170
|
}
|
|
164
171
|
// The editor-schema hint is a documentation nudge, emitted for every
|
|
165
172
|
// .flow.yaml without a first-line yaml-language-server comment.
|
|
@@ -172,14 +179,17 @@ export function checkFlow(path: string, invocation: CheckInvocation = {}): Check
|
|
|
172
179
|
const failure = error instanceof CheckFailure
|
|
173
180
|
? error
|
|
174
181
|
: new CheckFailure('invalid_spec', `Flow "${path}" could not be checked as a Relayflow spec.`);
|
|
175
|
-
return { report: inputFailureReport(failure, path) };
|
|
182
|
+
return { report: inputFailureReport(failure, path, invocation) };
|
|
176
183
|
}
|
|
177
184
|
}
|
|
178
185
|
|
|
179
186
|
/** Requirements never turn a preflight refusal into an unrelated exception. */
|
|
180
187
|
function safeRequirements(authoring: FlowSpec, projectCli: string | undefined): FlowRequirements | undefined {
|
|
181
188
|
try {
|
|
182
|
-
|
|
189
|
+
let requirementsSpec = authoring;
|
|
190
|
+
try { requirementsSpec = compileSpec(authoring); }
|
|
191
|
+
catch { /* Preserve requirements from invalid authoring; preflight owns its refusal. */ }
|
|
192
|
+
return flowRequirements(requirementsSpec, projectCli === undefined ? {} : { projectCli });
|
|
183
193
|
} catch {
|
|
184
194
|
return undefined;
|
|
185
195
|
}
|
|
@@ -220,7 +230,14 @@ export function checkAuthoredFlow(
|
|
|
220
230
|
...(config.modelRegistryPath !== undefined ? { modelRegistryPath: config.modelRegistryPath } : {}),
|
|
221
231
|
probes,
|
|
222
232
|
});
|
|
223
|
-
|
|
233
|
+
// check may restate a missing local helper mount as a warning. When that is
|
|
234
|
+
// the only refusal, still compile the flow so the communication checks
|
|
235
|
+
// below run: they must not depend on whether a helper is mounted. The
|
|
236
|
+
// compiled flow is returned only when preflight itself admitted it.
|
|
237
|
+
const inspectOnly = !result.ok && invocation.warnUnresolvedHelperCredential === true
|
|
238
|
+
&& !helperCredentialDiagnostics(result.diagnostics).diagnostics
|
|
239
|
+
.some(diagnostic => diagnostic.severity === 'refusal');
|
|
240
|
+
const flow = result.ok || inspectOnly
|
|
224
241
|
? bindResolvedCliPaths(
|
|
225
242
|
compileSpec(authoring),
|
|
226
243
|
result.resolutions,
|
|
@@ -246,14 +263,25 @@ export function checkAuthoredFlow(
|
|
|
246
263
|
const workerSurface = invocation.warnUnresolvedAgentWorker === true
|
|
247
264
|
? agentWorkerDiagnostics(authoring)
|
|
248
265
|
: [];
|
|
266
|
+
// A flow inspected past a missing helper mount is still a compiled spec the
|
|
267
|
+
// daemon must judge; skipping it would let a downgraded warning pass the gate.
|
|
268
|
+
const daemon = invocation.daemonValidation === undefined ? undefined
|
|
269
|
+
: (result.ok || inspectOnly) && flow !== undefined
|
|
270
|
+
? validateSpecWithDaemon(flow, invocation.daemonValidation, invocation.daemonValidationDeps)
|
|
271
|
+
: { validation: { mode: 'local' as const, reason: 'not_reached' as const }, diagnostics: [] };
|
|
272
|
+
if (daemon?.diagnostics.some(d => d.severity === 'refusal')) result.ok = false;
|
|
273
|
+
const diagnostics = invocation.warnUnresolvedHelperCredential === true
|
|
274
|
+
? helperCredentialDiagnostics(result.diagnostics).diagnostics : result.diagnostics;
|
|
249
275
|
return {
|
|
250
276
|
report: {
|
|
251
|
-
|
|
277
|
+
...(daemon === undefined ? {} : { validation: daemon.validation }),
|
|
278
|
+
ok: !diagnostics.some(diagnostic => diagnostic.severity === 'refusal')
|
|
279
|
+
&& !(daemon?.diagnostics.some(diagnostic => diagnostic.severity === 'refusal') ?? false),
|
|
252
280
|
path,
|
|
253
281
|
...(config.path !== undefined ? { projectConfigPath: config.path } : {}),
|
|
254
282
|
gates: result.gates,
|
|
255
283
|
resolutions: result.resolutions,
|
|
256
|
-
diagnostics: [...
|
|
284
|
+
diagnostics: [...diagnostics, ...workerSurface, ...(daemon?.diagnostics ?? [])],
|
|
257
285
|
requirements: safeRequirements(authoring, config.cli),
|
|
258
286
|
},
|
|
259
287
|
...(result.ok && flow !== undefined ? { flow } : {}),
|
|
@@ -262,7 +290,7 @@ export function checkAuthoredFlow(
|
|
|
262
290
|
const failure = error instanceof CheckFailure
|
|
263
291
|
? error
|
|
264
292
|
: new CheckFailure('invalid_spec', `Flow "${path}" could not be checked as a Relayflow spec.`);
|
|
265
|
-
return { report: inputFailureReport(failure, path) };
|
|
293
|
+
return { report: inputFailureReport(failure, path, invocation) };
|
|
266
294
|
}
|
|
267
295
|
}
|
|
268
296
|
|
|
@@ -335,8 +363,10 @@ export async function checkBuildableFlow(path: string): Promise<CheckExecution>
|
|
|
335
363
|
export function inputFailureReport(
|
|
336
364
|
failure: { kind: CheckFailureKind; message: string },
|
|
337
365
|
path?: string,
|
|
366
|
+
invocation: CheckInvocation = {},
|
|
338
367
|
): CheckReport {
|
|
339
368
|
return {
|
|
369
|
+
...(invocation.daemonValidation === undefined ? {} : { validation: { mode: 'local' as const, reason: 'not_reached' as const } }),
|
|
340
370
|
ok: false,
|
|
341
371
|
...(path !== undefined ? { path } : {}),
|
|
342
372
|
gates: [],
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { cloudAnswerCommand } from '../authored-human.js';
|
|
2
|
+
import { shellWord } from '../shell-word.js';
|
|
3
|
+
import { answerCloudFlow, CloudAnswerError } from '../cloud-answer.js';
|
|
4
|
+
import { CloudFlowError } from '../cloud-http.js';
|
|
5
|
+
import { redact } from '../redact.js';
|
|
6
|
+
import { safe } from './cloud-format.js';
|
|
7
|
+
import type { CliIo } from '../cli.js';
|
|
8
|
+
|
|
9
|
+
export async function runCloudAnswerCli(
|
|
10
|
+
args: { runId: string; answer: boolean; note?: string; source?: string; json: boolean },
|
|
11
|
+
io: CliIo,
|
|
12
|
+
signal: AbortSignal,
|
|
13
|
+
): Promise<0 | 1 | 2> {
|
|
14
|
+
try {
|
|
15
|
+
const receipt = await answerCloudFlow(args.runId, args.answer, { ...args, signal });
|
|
16
|
+
if (args.json) io.stdout(JSON.stringify({ ok: true, ...receipt }));
|
|
17
|
+
else {
|
|
18
|
+
io.stdout(`ANSWERED ${receipt.resumedFrom} ${receipt.waitId} ${receipt.answer ? 'yes' : 'no'}`);
|
|
19
|
+
io.stdout(`RESUMED ${receipt.runId}${receipt.resumedByCloud ? ' (by Cloud)' : ''}`);
|
|
20
|
+
}
|
|
21
|
+
return 0;
|
|
22
|
+
} catch (error) {
|
|
23
|
+
const partial = error instanceof CloudAnswerError;
|
|
24
|
+
const cause = partial ? error.cause : error;
|
|
25
|
+
const code = partial ? 'cloud_answer_incomplete' : cause instanceof CloudFlowError ? cause.code : 'cloud_answer_failed';
|
|
26
|
+
const detail = partial && cause instanceof Error ? ` ${cause.message}` : '';
|
|
27
|
+
const message = safe(redact((error instanceof Error ? error.message : 'Cloud answer failed.') + detail, process.env));
|
|
28
|
+
const retry = partial ? cloudAnswerCommand(args.runId).replace(/yes\|no$/u, args.answer ? 'yes' : 'no')
|
|
29
|
+
+ (args.source === undefined ? '' : ` --source ${shellWord(args.source)}`)
|
|
30
|
+
+ (args.note === undefined ? '' : ` --note ${shellWord(args.note)}`) : undefined;
|
|
31
|
+
const next = retry === undefined ? undefined : safe(redact(retry, process.env));
|
|
32
|
+
if (args.json) io.stdout(JSON.stringify({ ok: false, code, message, runId: args.runId,
|
|
33
|
+
...(partial ? { answerRecorded: error.answerRecorded, next } : {}) }));
|
|
34
|
+
else {
|
|
35
|
+
io.stderr(`${code}: ${message}`);
|
|
36
|
+
if (next) io.stderr(`After checking Cloud status, retry with: ${next}`);
|
|
37
|
+
}
|
|
38
|
+
return !partial && cause instanceof CloudFlowError && ['configuration', 'invalid_input', 'unsupported_source'].includes(cause.code) ? 2 : 1;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -3,6 +3,7 @@ import { dirname, resolve } from 'node:path';
|
|
|
3
3
|
import { createInterface } from 'node:readline';
|
|
4
4
|
import { parse as parseYaml } from 'yaml';
|
|
5
5
|
import { loadAuthoredFlow } from '../authored-flow-loader.js';
|
|
6
|
+
import { isAuthoredFlowPath } from '../direct-input.js';
|
|
6
7
|
import { ensureIntegrationsConnected, type ConnectPrompt, type ConnectionsOutcome } from '../cloud-connect.js';
|
|
7
8
|
import { CloudFlowError, cloudRequest, isCloudRecord, type CloudConnectionOptions } from '../cloud-http.js';
|
|
8
9
|
import { CompileError, compileSpec, kernelToAuthoring } from '../compile.js';
|
|
@@ -35,7 +36,9 @@ export async function flowRequirementsForPath(
|
|
|
35
36
|
const projectCli = context.projectCli ?? projectCliFor(path);
|
|
36
37
|
const withCli = { ...context, ...(projectCli === undefined ? {} : { projectCli }) };
|
|
37
38
|
try {
|
|
38
|
-
|
|
39
|
+
// Every authored extension flows check accepts; skipping one would let a
|
|
40
|
+
// Cloud submission go out without its integration check.
|
|
41
|
+
if (isAuthoredFlowPath(path)) {
|
|
39
42
|
const loaded = await loadAuthoredFlow(path);
|
|
40
43
|
return flowRequirements(loaded.getDefinition(loaded.handle), withCli);
|
|
41
44
|
}
|
|
@@ -77,11 +80,15 @@ export async function currentWorkspaceId(options: CloudConnectionOptions): Promi
|
|
|
77
80
|
* contacted when the flow requires no integration, so a flow with no
|
|
78
81
|
* helpers, sources or repository submits exactly as before; the derived
|
|
79
82
|
* requirements still come back so a later harness refusal can name its remedy.
|
|
80
|
-
* `undefined`
|
|
83
|
+
* `undefined` when the source does not load or is an authored extension Cloud
|
|
84
|
+
* does not submit — the submission says why.
|
|
81
85
|
*/
|
|
82
86
|
export async function ensureFlowConnections(
|
|
83
87
|
input: FlowConnectionsInput, options: CloudConnectionOptions = {},
|
|
84
88
|
): Promise<{ requirements: FlowRequirements; outcome: ConnectionsOutcome } | undefined> {
|
|
89
|
+
// Cloud submission accepts only .flow.ts among authored sources (cloud-run.ts
|
|
90
|
+
// prepareCloudSubmission); never prompt or connect for one it will refuse.
|
|
91
|
+
if (isAuthoredFlowPath(input.path) && !/\.flow\.ts$/iu.test(input.path)) return undefined;
|
|
85
92
|
const requirements = await flowRequirementsForPath(input.path, input);
|
|
86
93
|
if (requirements === undefined) return undefined;
|
|
87
94
|
if (requirements.integrations.length === 0) return { requirements, outcome: { ready: [], connected: [] } };
|