@osolmaz/pi-workflows 0.16.2 → 0.16.3
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/README.md +41 -41
- package/dist/builtins/monitor.workflow.d.ts +1 -1
- package/dist/builtins/monitor.workflow.js +8 -8
- package/dist/builtins/pi-agent-group.js +3 -3
- package/dist/builtins/pi-agent-group.js.map +1 -1
- package/dist/channels/adapter-entry.js +14 -14
- package/dist/channels/adapter-entry.js.map +1 -1
- package/dist/client/client.d.ts +6 -6
- package/dist/client/client.js +27 -27
- package/dist/client/client.js.map +1 -1
- package/dist/client/protocol.d.ts +4 -1
- package/dist/client/protocol.js +17 -11
- package/dist/client/protocol.js.map +1 -1
- package/dist/client/resolver.d.ts +4 -4
- package/dist/client/view.d.ts +1 -1
- package/dist/extension/index.d.ts +1 -1
- package/dist/extension/index.js +32 -32
- package/dist/extension/index.js.map +1 -1
- package/dist/extension/recorder.d.ts +1 -1
- package/dist/extension/recorder.js +1 -1
- package/dist/extension/recorder.js.map +1 -1
- package/dist/extension/remote-recorder-store.d.ts +1 -1
- package/dist/extension/remote-recorder-store.js +2 -2
- package/dist/extension/remote-recorder-store.js.map +1 -1
- package/dist/extension/{controller-command.d.ts → resource-manager-command.d.ts} +6 -6
- package/dist/extension/{controller-command.js → resource-manager-command.js} +7 -7
- package/dist/extension/resource-manager-command.js.map +1 -0
- package/dist/extension/session-delivery.d.ts +1 -1
- package/dist/extension/session-delivery.js +2 -2
- package/dist/extension/session-delivery.js.map +1 -1
- package/dist/extension/session-view.d.ts +1 -1
- package/dist/extension/session-view.js +1 -1
- package/dist/extension/session-view.js.map +1 -1
- package/dist/extension/workflow-message-coordinator.d.ts +1 -1
- package/dist/extension/workflow-message-coordinator.js +6 -6
- package/dist/extension/workflow-message-coordinator.js.map +1 -1
- package/dist/resource-managers/conditions.d.ts +6 -0
- package/dist/{controllers → resource-managers}/conditions.js +3 -3
- package/dist/resource-managers/conditions.js.map +1 -0
- package/dist/resource-managers/definition.d.ts +6 -0
- package/dist/resource-managers/definition.js +45 -0
- package/dist/resource-managers/definition.js.map +1 -0
- package/dist/resource-managers/effects.d.ts +15 -0
- package/dist/{controllers → resource-managers}/effects.js +2 -2
- package/dist/resource-managers/effects.js.map +1 -0
- package/dist/resource-managers/errors.d.ts +12 -0
- package/dist/resource-managers/errors.js +25 -0
- package/dist/resource-managers/errors.js.map +1 -0
- package/dist/resource-managers/index.d.ts +12 -0
- package/dist/resource-managers/index.js +12 -0
- package/dist/resource-managers/index.js.map +1 -0
- package/dist/resource-managers/json.js.map +1 -0
- package/dist/resource-managers/loader.d.ts +23 -0
- package/dist/resource-managers/loader.js +75 -0
- package/dist/resource-managers/loader.js.map +1 -0
- package/dist/resource-managers/results.d.ts +5 -0
- package/dist/resource-managers/results.js.map +1 -0
- package/dist/resource-managers/runtime.d.ts +59 -0
- package/dist/{controllers/manager.js → resource-managers/runtime.js} +57 -57
- package/dist/resource-managers/runtime.js.map +1 -0
- package/dist/{controllers → resource-managers}/sqlite.d.ts +44 -44
- package/dist/{controllers → resource-managers}/sqlite.js +139 -130
- package/dist/resource-managers/sqlite.js.map +1 -0
- package/dist/{controllers → resource-managers}/store.d.ts +33 -33
- package/dist/{controllers → resource-managers}/store.js.map +1 -1
- package/dist/{controllers → resource-managers}/types.d.ts +41 -41
- package/dist/{controllers → resource-managers}/types.js.map +1 -1
- package/dist/resource-managers/workflows.d.ts +34 -0
- package/dist/{controllers → resource-managers}/workflows.js +15 -15
- package/dist/resource-managers/workflows.js.map +1 -0
- package/dist/server/channel-effects.js.map +1 -0
- package/dist/{host → server}/channel-supervisor.d.ts +6 -6
- package/dist/{host → server}/channel-supervisor.js +3 -3
- package/dist/server/channel-supervisor.js.map +1 -0
- package/dist/{host/child-worker-supervisor.d.ts → server/child-runner-supervisor.d.ts} +15 -13
- package/dist/{host/child-worker-supervisor.js → server/child-runner-supervisor.js} +6 -5
- package/dist/server/child-runner-supervisor.js.map +1 -0
- package/dist/{host → server}/processes.d.ts +3 -3
- package/dist/{host → server}/processes.js +3 -3
- package/dist/server/processes.js.map +1 -0
- package/dist/{host → server}/resolver-entry.d.ts +6 -6
- package/dist/{host → server}/resolver-entry.js +14 -14
- package/dist/server/resolver-entry.js.map +1 -0
- package/dist/server/resource-runner-entry.d.ts +2 -0
- package/dist/{host/controller-worker-entry.js → server/resource-runner-entry.js} +33 -33
- package/dist/server/resource-runner-entry.js.map +1 -0
- package/dist/server/resource-runner-protocol.d.ts +36 -0
- package/dist/server/resource-runner-protocol.js +49 -0
- package/dist/server/resource-runner-protocol.js.map +1 -0
- package/dist/server/resource-runner-supervisor.d.ts +21 -0
- package/dist/{host/worker-supervisor.js → server/resource-runner-supervisor.js} +17 -17
- package/dist/server/resource-runner-supervisor.js.map +1 -0
- package/dist/{host → server}/rpc-bridge.d.ts +3 -3
- package/dist/{host → server}/rpc-bridge.js +3 -3
- package/dist/server/rpc-bridge.js.map +1 -0
- package/dist/{host → server}/rpc-executor.d.ts +1 -1
- package/dist/{host → server}/rpc-executor.js +3 -3
- package/dist/server/rpc-executor.js.map +1 -0
- package/dist/{host/host-entry.js → server/server-entry.js} +6 -6
- package/dist/server/server-entry.js.map +1 -0
- package/dist/{host/runner.d.ts → server/server.d.ts} +39 -38
- package/dist/{host/runner.js → server/server.js} +408 -374
- package/dist/server/server.js.map +1 -0
- package/dist/{host → server}/state.d.ts +25 -25
- package/dist/{host → server}/state.js +49 -49
- package/dist/server/state.js.map +1 -0
- package/dist/{host → server}/view.d.ts +7 -7
- package/dist/{host → server}/view.js +13 -13
- package/dist/server/view.js.map +1 -0
- package/dist/server/workflow-runner-content.d.ts +6 -0
- package/dist/server/workflow-runner-content.js +96 -0
- package/dist/server/workflow-runner-content.js.map +1 -0
- package/dist/{host/worker-entry.d.ts → server/workflow-runner-entry.d.ts} +3 -3
- package/dist/{host/worker-entry.js → server/workflow-runner-entry.js} +68 -35
- package/dist/server/workflow-runner-entry.js.map +1 -0
- package/dist/server/workflow-runner-protocol.d.ts +69 -0
- package/dist/server/workflow-runner-protocol.js +176 -0
- package/dist/server/workflow-runner-protocol.js.map +1 -0
- package/dist/{host/worker-store.d.ts → server/workflow-runner-store.d.ts} +11 -11
- package/dist/{host/worker-store.js → server/workflow-runner-store.js} +8 -8
- package/dist/server/workflow-runner-store.js.map +1 -0
- package/dist/server/workflow-runner-supervisor.d.ts +22 -0
- package/dist/server/workflow-runner-supervisor.js +56 -0
- package/dist/server/workflow-runner-supervisor.js.map +1 -0
- package/dist/state/prune.d.ts +1 -1
- package/dist/state/prune.js +17 -10
- package/dist/state/prune.js.map +1 -1
- package/dist/state/schema.js +2 -2
- package/dist/state/workflow-messages.d.ts +1 -1
- package/dist/state/workflow-messages.js +1 -1
- package/dist/state/workflow-messages.js.map +1 -1
- package/dist/viewer/backup.js +1 -1
- package/dist/viewer/backup.js.map +1 -1
- package/dist/viewer/cli.d.ts +2 -2
- package/dist/viewer/cli.js +46 -32
- package/dist/viewer/cli.js.map +1 -1
- package/dist/viewer/tui.d.ts +1 -1
- package/dist/viewer/tui.js +1 -1
- package/dist/viewer/tui.js.map +1 -1
- package/dist/workflows/command-batch.js +2 -2
- package/dist/workflows/engine.js +14 -15
- package/dist/workflows/engine.js.map +1 -1
- package/dist/workflows/store.d.ts +12 -11
- package/dist/workflows/store.js +24 -18
- package/dist/workflows/store.js.map +1 -1
- package/dist/workflows/types.d.ts +2 -2
- package/docs/2026-08-20-durable-workflow-launch-plan.md +12 -12
- package/docs/2026-08-25-workflow-follow-ups.md +7 -7
- package/docs/2026-08-25-workflow-settings.md +3 -3
- package/docs/2026-08-30-out-of-process-workflow-host-plan.md +3 -3
- package/docs/2026-09-01-restore-session-delivery-controls-plan.md +5 -5
- package/docs/2026-09-01-unified-workflow-client-plan.md +6 -6
- package/docs/2026-09-02-installed-live-e2e-plan.md +3 -3
- package/docs/2026-09-02-unify-workflow-messages-plan.md +8 -8
- package/docs/2026-09-04-workflow-run-state-plan.md +6 -6
- package/docs/DEFERRED_TURNS.md +7 -7
- package/docs/DESIGN_PHILOSOPHY.md +1 -1
- package/docs/HUMAN_DECISIONS.md +11 -11
- package/docs/HUMAN_DECISION_PRESENTATIONS.md +4 -4
- package/docs/MONITOR.md +1 -1
- package/docs/RESOURCE_MANAGERS.md +221 -0
- package/docs/SQLITE_STATE.md +35 -33
- package/docs/WORKFLOW_COMPOSITION.md +1 -1
- package/docs/{WORKFLOW_HOST.md → WORKFLOW_SERVER.md} +125 -119
- package/docs/WORKFLOW_STEP_MESSAGES.md +18 -18
- package/docs/WORKFLOW_UPDATES.md +16 -16
- package/docs/development.md +15 -14
- package/docs/live-replay-protocol.md +20 -20
- package/docs/plans/2026-08-04-controller-runtime-plan.md +25 -25
- package/docs/plans/2026-08-05-always-on-workflows-plan.md +8 -8
- package/docs/plans/2026-08-10-agent-managed-monitor-workflows-plan.md +2 -2
- package/docs/plans/2026-08-13-session-addressed-workflow-notifications-plan.md +1 -1
- package/docs/plans/2026-08-16-workflow-updates-plan.md +5 -5
- package/docs/plans/2026-08-19-human-decision-gates-plan.md +1 -1
- package/docs/plans/2026-08-20-bounded-command-batches-plan.md +1 -1
- package/docs/plans/2026-08-21-deferred-turn-intents-plan.md +3 -3
- package/docs/plans/2026-08-21-sanity-check-plan.md +2 -2
- package/docs/plans/2026-08-23-assistant-agent-completion-plan.md +5 -5
- package/docs/plans/2026-08-23-sqlite-state-plan.md +25 -25
- package/docs/plans/2026-08-25-live-workflow-settings-plan.md +4 -4
- package/docs/plans/2026-08-27-workflow-terminal-restart-plan.md +3 -3
- package/docs/plans/2026-09-04-workflow-runner-resume-state-plan.md +318 -0
- package/docs/plans/piw-viewer-experience-implementation-plan.md +1 -1
- package/docs/tui-viewer.md +9 -9
- package/docs/workflows.md +72 -66
- package/examples/{controllers/pull-request.controller.ts → resource-managers/pull-request.resource-manager.ts} +12 -12
- package/herdr-plugin.toml +1 -1
- package/package.json +5 -5
- package/protocol/client.v1.schema.json +7 -7
- package/protocol/fixtures/client-v1.json +1 -1
- package/src/builtins/monitor.workflow.ts +9 -9
- package/src/builtins/pi-agent-group.ts +3 -3
- package/src/channels/adapter-entry.ts +14 -14
- package/src/client/client.ts +33 -32
- package/src/client/protocol.ts +16 -11
- package/src/client/resolver.ts +4 -4
- package/src/client/view.ts +1 -1
- package/src/extension/index.ts +44 -34
- package/src/extension/recorder.ts +1 -1
- package/src/extension/remote-recorder-store.ts +2 -2
- package/src/extension/resource-manager-command.ts +45 -0
- package/src/extension/session-delivery.ts +2 -2
- package/src/extension/session-view.ts +1 -1
- package/src/extension/workflow-message-coordinator.ts +6 -6
- package/src/{controllers → resource-managers}/conditions.ts +23 -23
- package/src/resource-managers/definition.ts +71 -0
- package/src/{controllers → resource-managers}/effects.ts +9 -9
- package/src/resource-managers/errors.ts +27 -0
- package/src/resource-managers/index.ts +88 -0
- package/src/resource-managers/loader.ts +111 -0
- package/src/{controllers → resource-managers}/results.ts +4 -4
- package/src/{controllers/manager.ts → resource-managers/runtime.ts} +95 -92
- package/src/{controllers → resource-managers}/sqlite.ts +198 -186
- package/src/{controllers → resource-managers}/store.ts +46 -38
- package/src/{controllers → resource-managers}/types.ts +50 -41
- package/src/{controllers → resource-managers}/workflows.ts +43 -41
- package/src/{host → server}/channel-supervisor.ts +8 -8
- package/src/{host/child-worker-supervisor.ts → server/child-runner-supervisor.ts} +22 -18
- package/src/{host → server}/processes.ts +3 -3
- package/src/{host → server}/resolver-entry.ts +28 -25
- package/src/{host/controller-worker-entry.ts → server/resource-runner-entry.ts} +67 -65
- package/src/server/resource-runner-protocol.ts +103 -0
- package/src/server/resource-runner-supervisor.ts +76 -0
- package/src/{host → server}/rpc-bridge.ts +3 -3
- package/src/{host → server}/rpc-executor.ts +4 -4
- package/src/{host/host-entry.ts → server/server-entry.ts} +5 -5
- package/src/{host/runner.ts → server/server.ts} +526 -461
- package/src/{host → server}/state.ts +78 -67
- package/src/{host → server}/view.ts +15 -15
- package/src/server/workflow-runner-content.ts +119 -0
- package/src/{host/worker-entry.ts → server/workflow-runner-entry.ts} +98 -57
- package/src/server/workflow-runner-protocol.ts +241 -0
- package/src/{host/worker-store.ts → server/workflow-runner-store.ts} +18 -20
- package/src/server/workflow-runner-supervisor.ts +79 -0
- package/src/state/prune.ts +27 -9
- package/src/state/schema.ts +2 -2
- package/src/state/workflow-messages.ts +1 -1
- package/src/viewer/backup.ts +1 -1
- package/src/viewer/cli.ts +50 -36
- package/src/viewer/tui.ts +1 -1
- package/src/workflows/command-batch.ts +2 -2
- package/src/workflows/engine.ts +14 -15
- package/src/workflows/store.ts +31 -28
- package/src/workflows/types.ts +2 -2
- package/dist/controllers/conditions.d.ts +0 -6
- package/dist/controllers/conditions.js.map +0 -1
- package/dist/controllers/definition.d.ts +0 -6
- package/dist/controllers/definition.js +0 -45
- package/dist/controllers/definition.js.map +0 -1
- package/dist/controllers/effects.d.ts +0 -15
- package/dist/controllers/effects.js.map +0 -1
- package/dist/controllers/errors.d.ts +0 -12
- package/dist/controllers/errors.js +0 -25
- package/dist/controllers/errors.js.map +0 -1
- package/dist/controllers/index.d.ts +0 -12
- package/dist/controllers/index.js +0 -12
- package/dist/controllers/index.js.map +0 -1
- package/dist/controllers/json.js.map +0 -1
- package/dist/controllers/loader.d.ts +0 -23
- package/dist/controllers/loader.js +0 -74
- package/dist/controllers/loader.js.map +0 -1
- package/dist/controllers/manager.d.ts +0 -59
- package/dist/controllers/manager.js.map +0 -1
- package/dist/controllers/results.d.ts +0 -5
- package/dist/controllers/results.js.map +0 -1
- package/dist/controllers/sqlite.js.map +0 -1
- package/dist/controllers/workflows.d.ts +0 -34
- package/dist/controllers/workflows.js.map +0 -1
- package/dist/extension/controller-command.js.map +0 -1
- package/dist/host/channel-effects.js.map +0 -1
- package/dist/host/channel-supervisor.js.map +0 -1
- package/dist/host/child-worker-supervisor.js.map +0 -1
- package/dist/host/controller-worker-entry.d.ts +0 -2
- package/dist/host/controller-worker-entry.js.map +0 -1
- package/dist/host/controller-worker-protocol.d.ts +0 -36
- package/dist/host/controller-worker-protocol.js +0 -49
- package/dist/host/controller-worker-protocol.js.map +0 -1
- package/dist/host/controller-worker-supervisor.d.ts +0 -21
- package/dist/host/controller-worker-supervisor.js +0 -54
- package/dist/host/controller-worker-supervisor.js.map +0 -1
- package/dist/host/host-entry.js.map +0 -1
- package/dist/host/processes.js.map +0 -1
- package/dist/host/resolver-entry.js.map +0 -1
- package/dist/host/rpc-bridge.js.map +0 -1
- package/dist/host/rpc-executor.js.map +0 -1
- package/dist/host/runner.js.map +0 -1
- package/dist/host/state.js.map +0 -1
- package/dist/host/view.js.map +0 -1
- package/dist/host/worker-entry.js.map +0 -1
- package/dist/host/worker-protocol.d.ts +0 -46
- package/dist/host/worker-protocol.js +0 -122
- package/dist/host/worker-protocol.js.map +0 -1
- package/dist/host/worker-store.js.map +0 -1
- package/dist/host/worker-supervisor.d.ts +0 -22
- package/dist/host/worker-supervisor.js.map +0 -1
- package/docs/CONTROLLERS.md +0 -217
- package/src/controllers/definition.ts +0 -65
- package/src/controllers/errors.ts +0 -27
- package/src/controllers/index.ts +0 -88
- package/src/controllers/loader.ts +0 -104
- package/src/extension/controller-command.ts +0 -45
- package/src/host/controller-worker-protocol.ts +0 -104
- package/src/host/controller-worker-supervisor.ts +0 -79
- package/src/host/worker-protocol.ts +0 -176
- package/src/host/worker-supervisor.ts +0 -74
- /package/dist/{controllers → resource-managers}/json.d.ts +0 -0
- /package/dist/{controllers → resource-managers}/json.js +0 -0
- /package/dist/{controllers → resource-managers}/results.js +0 -0
- /package/dist/{controllers → resource-managers}/store.js +0 -0
- /package/dist/{controllers → resource-managers}/types.js +0 -0
- /package/dist/{host → server}/channel-effects.d.ts +0 -0
- /package/dist/{host → server}/channel-effects.js +0 -0
- /package/dist/{host/host-entry.d.ts → server/server-entry.d.ts} +0 -0
- /package/src/{controllers → resource-managers}/json.ts +0 -0
- /package/src/{host → server}/channel-effects.ts +0 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { canonicalJson } from "../state/json.js";
|
|
5
|
+
import { ChildRunnerSupervisor, type SupervisedRunnerResult } from "./child-runner-supervisor.js";
|
|
6
|
+
import { ServerProcessRegistry, type ProcessIdentity } from "./processes.js";
|
|
7
|
+
import type { WorkflowRunnerLaunchEnvelope, RunnerOutcome } from "./state.js";
|
|
8
|
+
import {
|
|
9
|
+
MAX_WORKFLOW_RUNNER_PROTOCOL_MESSAGE_BYTES,
|
|
10
|
+
encodeRunnerLine,
|
|
11
|
+
parseRunnerMessage,
|
|
12
|
+
type WorkflowRunnerMessage,
|
|
13
|
+
type WorkflowRunnerResponse,
|
|
14
|
+
} from "./workflow-runner-protocol.js";
|
|
15
|
+
|
|
16
|
+
export type WorkflowRunnerResult = SupervisedRunnerResult;
|
|
17
|
+
|
|
18
|
+
export class WorkflowRunnerSupervisor {
|
|
19
|
+
private readonly supervisor: ChildRunnerSupervisor<WorkflowRunnerMessage, WorkflowRunnerResponse>;
|
|
20
|
+
|
|
21
|
+
constructor(
|
|
22
|
+
readonly envelope: WorkflowRunnerLaunchEnvelope,
|
|
23
|
+
private readonly options: {
|
|
24
|
+
registry: ServerProcessRegistry;
|
|
25
|
+
onMessage: (message: WorkflowRunnerMessage) => Promise<WorkflowRunnerResponse>;
|
|
26
|
+
env?: Record<string, string>;
|
|
27
|
+
startupTimeoutMs?: number;
|
|
28
|
+
runnerEntryPath?: string;
|
|
29
|
+
onSpawn?: (identity: ProcessIdentity) => void;
|
|
30
|
+
onDiagnostic?: (message: string) => void;
|
|
31
|
+
},
|
|
32
|
+
) {
|
|
33
|
+
this.supervisor = new ChildRunnerSupervisor({
|
|
34
|
+
label: "Workflow runner",
|
|
35
|
+
registry: options.registry,
|
|
36
|
+
onMessage: options.onMessage,
|
|
37
|
+
parseMessage: parseRunnerMessage,
|
|
38
|
+
encodeResponse: encodeRunnerLine,
|
|
39
|
+
isReady: (message) => message.kind === "runner.ready",
|
|
40
|
+
isTerminal: (message) => message.kind === "runner.exiting",
|
|
41
|
+
maxMessageBytes: MAX_WORKFLOW_RUNNER_PROTOCOL_MESSAGE_BYTES,
|
|
42
|
+
oversizedMessage: "Runner protocol message exceeds 1 MiB",
|
|
43
|
+
...(options.startupTimeoutMs === undefined
|
|
44
|
+
? {}
|
|
45
|
+
: { startupTimeoutMs: options.startupTimeoutMs }),
|
|
46
|
+
...(options.onSpawn === undefined ? {} : { onSpawn: options.onSpawn }),
|
|
47
|
+
...(options.onDiagnostic === undefined ? {} : { onDiagnostic: options.onDiagnostic }),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async start(): Promise<void> {
|
|
52
|
+
const builtEntry = fileURLToPath(new URL("./workflow-runner-entry.js", import.meta.url));
|
|
53
|
+
const sourceEntry = fileURLToPath(new URL("./workflow-runner-entry.ts", import.meta.url));
|
|
54
|
+
const runnerEntry = this.options.runnerEntryPath ?? builtEntry;
|
|
55
|
+
const args =
|
|
56
|
+
this.options.runnerEntryPath === undefined && !fs.existsSync(builtEntry)
|
|
57
|
+
? ["--import", createRequire(import.meta.url).resolve("tsx"), sourceEntry]
|
|
58
|
+
: [runnerEntry];
|
|
59
|
+
await this.supervisor.start({
|
|
60
|
+
args,
|
|
61
|
+
cwd: this.envelope.projectPath,
|
|
62
|
+
env: {
|
|
63
|
+
...process.env,
|
|
64
|
+
...this.options.env,
|
|
65
|
+
PI_WORKFLOWS_WORKFLOW_RUNNER_LAUNCH: Buffer.from(canonicalJson(this.envelope)).toString(
|
|
66
|
+
"base64url",
|
|
67
|
+
),
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async wait(): Promise<WorkflowRunnerResult> {
|
|
73
|
+
return await this.supervisor.wait();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async stop(outcome: RunnerOutcome = "cancelled"): Promise<void> {
|
|
77
|
+
await this.supervisor.stop(outcome);
|
|
78
|
+
}
|
|
79
|
+
}
|
package/src/state/prune.ts
CHANGED
|
@@ -48,6 +48,7 @@ export async function pruneState(
|
|
|
48
48
|
state: StateDatabase,
|
|
49
49
|
databasePath: string,
|
|
50
50
|
options: StatePruneOptions,
|
|
51
|
+
activeBlobHashes: () => readonly Buffer[] = () => [],
|
|
51
52
|
): Promise<StatePruneReport> {
|
|
52
53
|
validateStatePruneOptions(options);
|
|
53
54
|
const cutoff = parseCutoff(options.before);
|
|
@@ -114,8 +115,9 @@ export async function pruneState(
|
|
|
114
115
|
)`,
|
|
115
116
|
)
|
|
116
117
|
.run();
|
|
117
|
-
const
|
|
118
|
-
|
|
118
|
+
const retainedBlobHashes = activeBlobHashes();
|
|
119
|
+
const blobStats = unreferencedBlobStats(state.connection, retainedBlobHashes);
|
|
120
|
+
deleteUnreferencedBlobs(state.connection, retainedBlobHashes);
|
|
119
121
|
deletedRows = totalChanges(state.connection) - beforeChanges;
|
|
120
122
|
deletedBlobs = blobStats.count;
|
|
121
123
|
deletedBlobBytes = blobStats.bytes;
|
|
@@ -356,21 +358,31 @@ function selectionSignature(database: Database.Database, runIds: string[]): stri
|
|
|
356
358
|
return JSON.stringify([resourceRows, queueRows]);
|
|
357
359
|
}
|
|
358
360
|
|
|
359
|
-
function unreferencedBlobStats(
|
|
360
|
-
|
|
361
|
+
function unreferencedBlobStats(
|
|
362
|
+
database: Database.Database,
|
|
363
|
+
retainedBlobHashes: readonly Buffer[],
|
|
364
|
+
): { count: number; bytes: number } {
|
|
365
|
+
const predicate = blobReferencePredicate(database, retainedBlobHashes.length);
|
|
361
366
|
const row = database
|
|
362
367
|
.prepare(
|
|
363
368
|
`SELECT count(*) AS count, COALESCE(sum(byte_length), 0) AS bytes FROM blobs WHERE ${predicate}`,
|
|
364
369
|
)
|
|
365
|
-
.get();
|
|
370
|
+
.get(...retainedBlobHashes);
|
|
366
371
|
return isBlobStatsRow(row) ? row : { count: 0, bytes: 0 };
|
|
367
372
|
}
|
|
368
373
|
|
|
369
|
-
function deleteUnreferencedBlobs(
|
|
370
|
-
database
|
|
374
|
+
function deleteUnreferencedBlobs(
|
|
375
|
+
database: Database.Database,
|
|
376
|
+
retainedBlobHashes: readonly Buffer[],
|
|
377
|
+
): void {
|
|
378
|
+
database
|
|
379
|
+
.prepare(
|
|
380
|
+
`DELETE FROM blobs WHERE ${blobReferencePredicate(database, retainedBlobHashes.length)}`,
|
|
381
|
+
)
|
|
382
|
+
.run(...retainedBlobHashes);
|
|
371
383
|
}
|
|
372
384
|
|
|
373
|
-
function blobReferencePredicate(database: Database.Database): string {
|
|
385
|
+
function blobReferencePredicate(database: Database.Database, retainedBlobCount: number): string {
|
|
374
386
|
const tables = database
|
|
375
387
|
.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'")
|
|
376
388
|
.all()
|
|
@@ -387,7 +399,13 @@ function blobReferencePredicate(database: Database.Database): string {
|
|
|
387
399
|
);
|
|
388
400
|
}
|
|
389
401
|
}
|
|
390
|
-
|
|
402
|
+
const unreferenced =
|
|
403
|
+
references.length === 0 ? "1 = 1" : `blob_hash NOT IN (${references.join(" UNION ")})`;
|
|
404
|
+
if (retainedBlobCount === 0) return unreferenced;
|
|
405
|
+
const retainedPlaceholders = placeholders(
|
|
406
|
+
Array.from({ length: retainedBlobCount }, () => undefined),
|
|
407
|
+
);
|
|
408
|
+
return `${unreferenced} AND blob_hash NOT IN (${retainedPlaceholders})`;
|
|
391
409
|
}
|
|
392
410
|
|
|
393
411
|
function descendants(root: string, children: Map<string, string[]>): string[] {
|
package/src/state/schema.ts
CHANGED
|
@@ -302,8 +302,8 @@ CREATE TABLE host_commands (
|
|
|
302
302
|
'workflowMessage.reportBranch', 'workflowTurn.report', 'sessionView.clearTerminal',
|
|
303
303
|
'run.restart', 'run.changeSettings', 'followUp.queue', 'followUp.remove', 'session.record',
|
|
304
304
|
'channel.reload', 'channel.recover',
|
|
305
|
-
'
|
|
306
|
-
'
|
|
305
|
+
'resourceManager.list', 'resourceManager.get', 'resourceManager.apply', 'resourceManager.reconcile', 'resourceManager.delete',
|
|
306
|
+
'server.status', 'server.stop', 'state.backup', 'state.prune'
|
|
307
307
|
)),
|
|
308
308
|
idempotency_key TEXT NOT NULL,
|
|
309
309
|
request_fingerprint BLOB NOT NULL CHECK (length(request_fingerprint) = 32),
|
|
@@ -92,7 +92,7 @@ type WorkflowTurnRow = {
|
|
|
92
92
|
endedAt: number | null;
|
|
93
93
|
};
|
|
94
94
|
|
|
95
|
-
/** Durable workflow content that the
|
|
95
|
+
/** Durable workflow content that the server requires Pi to add to one origin session. */
|
|
96
96
|
export class WorkflowMessageStore {
|
|
97
97
|
constructor(readonly state: StateDatabase) {}
|
|
98
98
|
|
package/src/viewer/backup.ts
CHANGED
|
@@ -10,7 +10,7 @@ export function verifyInactiveBackup(
|
|
|
10
10
|
const resolved = path.resolve(databasePath);
|
|
11
11
|
const active = path.resolve(activeDatabasePath);
|
|
12
12
|
if (sameFile(resolved, active)) {
|
|
13
|
-
throw new Error("Active workflow state must be verified through the workflow
|
|
13
|
+
throw new Error("Active workflow state must be verified through the workflow server");
|
|
14
14
|
}
|
|
15
15
|
const state = new StateDatabase({ filePath: resolved, mode: "read-only" });
|
|
16
16
|
try {
|
package/src/viewer/cli.ts
CHANGED
|
@@ -12,35 +12,35 @@ import { renderClientView, runViewer } from "./tui.js";
|
|
|
12
12
|
|
|
13
13
|
const CLI_CLIENT_ID = "pi-workflows-cli";
|
|
14
14
|
|
|
15
|
-
const USAGE = `pi-workflows — workflow runs and
|
|
15
|
+
const USAGE = `pi-workflows — workflow runs and managed resources
|
|
16
16
|
|
|
17
17
|
Usage:
|
|
18
18
|
pi-workflows view [runId] [--once]
|
|
19
19
|
pi-workflows runs
|
|
20
|
-
pi-workflows
|
|
21
|
-
pi-workflows
|
|
20
|
+
pi-workflows resource-managers
|
|
21
|
+
pi-workflows resource-manager <resource-manager> <key>
|
|
22
22
|
pi-workflows state status
|
|
23
23
|
pi-workflows state verify [inactive-backup]
|
|
24
24
|
pi-workflows state backup <destination>
|
|
25
25
|
pi-workflows state prune --before <timestamp> --dry-run
|
|
26
26
|
pi-workflows state prune --before <timestamp> --backup <absolute-path> --apply
|
|
27
|
-
pi-workflows
|
|
28
|
-
pi-workflows
|
|
29
|
-
pi-workflows
|
|
30
|
-
pi-workflows
|
|
27
|
+
pi-workflows server start
|
|
28
|
+
pi-workflows server status
|
|
29
|
+
pi-workflows server stop
|
|
30
|
+
pi-workflows server run [-- <extra pi args>]
|
|
31
31
|
pi-workflows herdr sync [--json]
|
|
32
32
|
pi-workflows herdr setup [--json]
|
|
33
33
|
|
|
34
|
-
Active state is available only through the package-owned workflow
|
|
34
|
+
Active state is available only through the package-owned workflow server.
|
|
35
35
|
`;
|
|
36
36
|
|
|
37
37
|
export type CliArgs = {
|
|
38
38
|
command: string;
|
|
39
39
|
runId?: string;
|
|
40
|
-
|
|
40
|
+
resourceManagerName?: string;
|
|
41
41
|
resourceKey?: string;
|
|
42
42
|
herdrAction?: string;
|
|
43
|
-
|
|
43
|
+
serverAction?: "start" | "status" | "stop" | "run";
|
|
44
44
|
stateAction?: "status" | "verify" | "backup" | "prune";
|
|
45
45
|
backupDestination?: string;
|
|
46
46
|
verifyBackup?: string;
|
|
@@ -55,9 +55,16 @@ export function parseCliArgs(argv: string[]): CliArgs {
|
|
|
55
55
|
const args = [...argv];
|
|
56
56
|
const command = args[0] && !args[0].startsWith("-") ? (args.shift() as string) : "view";
|
|
57
57
|
if (
|
|
58
|
-
![
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
![
|
|
59
|
+
"view",
|
|
60
|
+
"runs",
|
|
61
|
+
"resource-managers",
|
|
62
|
+
"resource-manager",
|
|
63
|
+
"state",
|
|
64
|
+
"server",
|
|
65
|
+
"herdr",
|
|
66
|
+
"help",
|
|
67
|
+
].includes(command)
|
|
61
68
|
) {
|
|
62
69
|
throw new Error(`Unknown command: ${command}`);
|
|
63
70
|
}
|
|
@@ -87,23 +94,24 @@ export function parseCliArgs(argv: string[]): CliArgs {
|
|
|
87
94
|
}
|
|
88
95
|
|
|
89
96
|
if (command !== "herdr" && json) throw new Error("--json is available only for herdr sync");
|
|
90
|
-
if (command === "
|
|
91
|
-
if (project !== undefined) throw new Error("The global
|
|
97
|
+
if (command === "server") {
|
|
98
|
+
if (project !== undefined) throw new Error("The global server does not accept --project");
|
|
92
99
|
const action = positionals[0];
|
|
93
100
|
if (action !== "start" && action !== "status" && action !== "stop" && action !== "run") {
|
|
94
|
-
throw new Error("
|
|
101
|
+
throw new Error("server requires start, status, stop, or run");
|
|
95
102
|
}
|
|
96
|
-
if (positionals.length !== 1) throw new Error("
|
|
103
|
+
if (positionals.length !== 1) throw new Error("server accepts one lifecycle action");
|
|
97
104
|
if (piArgs.length > 0 && action !== "run") {
|
|
98
|
-
throw new Error("Extra Pi arguments are available only for
|
|
105
|
+
throw new Error("Extra Pi arguments are available only for server run");
|
|
99
106
|
}
|
|
100
|
-
return { command,
|
|
107
|
+
return { command, serverAction: action, once, json, piArgs };
|
|
101
108
|
}
|
|
102
|
-
if (command === "
|
|
103
|
-
if (positionals.length !== 2)
|
|
109
|
+
if (command === "resource-manager") {
|
|
110
|
+
if (positionals.length !== 2)
|
|
111
|
+
throw new Error("resource-manager requires <resource-manager> and <key>");
|
|
104
112
|
return {
|
|
105
113
|
command,
|
|
106
|
-
|
|
114
|
+
resourceManagerName: positionals[0] as string,
|
|
107
115
|
resourceKey: positionals[1] as string,
|
|
108
116
|
once,
|
|
109
117
|
json,
|
|
@@ -188,8 +196,8 @@ export async function main(argv = process.argv.slice(2)): Promise<number> {
|
|
|
188
196
|
}
|
|
189
197
|
|
|
190
198
|
try {
|
|
191
|
-
if (args.command === "
|
|
192
|
-
return await
|
|
199
|
+
if (args.command === "server") {
|
|
200
|
+
return await runServer(args.serverAction as "start" | "status" | "stop" | "run", args.piArgs);
|
|
193
201
|
}
|
|
194
202
|
if (args.command === "herdr") {
|
|
195
203
|
const result = syncHerdrPlugin(packageRoot());
|
|
@@ -223,13 +231,17 @@ export async function main(argv = process.argv.slice(2)): Promise<number> {
|
|
|
223
231
|
printJsonLines(runs);
|
|
224
232
|
return 0;
|
|
225
233
|
}
|
|
226
|
-
if (args.command === "
|
|
234
|
+
if (args.command === "resource-managers" || args.command === "resource-manager") {
|
|
227
235
|
const response = await client.request({
|
|
228
|
-
operation:
|
|
236
|
+
operation:
|
|
237
|
+
args.command === "resource-managers" ? "resourceManager.list" : "resourceManager.get",
|
|
229
238
|
payload: {
|
|
230
239
|
projectPath: process.cwd(),
|
|
231
|
-
...(args.command === "
|
|
232
|
-
? {
|
|
240
|
+
...(args.command === "resource-manager"
|
|
241
|
+
? {
|
|
242
|
+
resourceManager: args.resourceManagerName as string,
|
|
243
|
+
key: args.resourceKey as string,
|
|
244
|
+
}
|
|
233
245
|
: {}),
|
|
234
246
|
},
|
|
235
247
|
});
|
|
@@ -298,19 +310,19 @@ async function runStateCommand(client: WorkflowClient, args: CliArgs): Promise<v
|
|
|
298
310
|
process.stdout.write(`${JSON.stringify(response.receipt ?? {})}\n`);
|
|
299
311
|
}
|
|
300
312
|
|
|
301
|
-
async function
|
|
313
|
+
async function runServer(
|
|
302
314
|
action: "start" | "status" | "stop" | "run",
|
|
303
315
|
piArgs: string[] | undefined,
|
|
304
316
|
): Promise<number> {
|
|
305
317
|
if (action === "run") {
|
|
306
|
-
const {
|
|
307
|
-
const
|
|
318
|
+
const { WorkflowServer } = await import("../server/server.js");
|
|
319
|
+
const server = new WorkflowServer({
|
|
308
320
|
piArgs: piArgs ?? [],
|
|
309
|
-
onLog: (message) => process.stdout.write(`[
|
|
321
|
+
onLog: (message) => process.stdout.write(`[server] ${message}\n`),
|
|
310
322
|
});
|
|
311
|
-
await
|
|
323
|
+
await server.start();
|
|
312
324
|
await new Promise<void>((resolve) => {
|
|
313
|
-
const shutdown = () => void
|
|
325
|
+
const shutdown = () => void server.stop().then(resolve);
|
|
314
326
|
process.once("SIGTERM", shutdown);
|
|
315
327
|
process.once("SIGINT", shutdown);
|
|
316
328
|
});
|
|
@@ -321,7 +333,9 @@ async function runHost(
|
|
|
321
333
|
const response =
|
|
322
334
|
action === "start"
|
|
323
335
|
? await client.ensureRunning()
|
|
324
|
-
: await client.request({
|
|
336
|
+
: await client.request({
|
|
337
|
+
operation: action === "status" ? "server.status" : "server.stop",
|
|
338
|
+
});
|
|
325
339
|
process.stdout.write(`${JSON.stringify(response.receipt ?? {})}\n`);
|
|
326
340
|
return response.outcome === "accepted" || response.outcome === "adopted" ? 0 : 1;
|
|
327
341
|
} finally {
|
|
@@ -364,7 +378,7 @@ function printJsonLines(value: JsonValue): void {
|
|
|
364
378
|
|
|
365
379
|
function requireAccepted(response: { outcome: string; error?: string }): void {
|
|
366
380
|
if (response.outcome !== "accepted" && response.outcome !== "adopted") {
|
|
367
|
-
throw new Error(response.error ?? `Workflow
|
|
381
|
+
throw new Error(response.error ?? `Workflow server returned ${response.outcome}`);
|
|
368
382
|
}
|
|
369
383
|
}
|
|
370
384
|
|
package/src/viewer/tui.ts
CHANGED
|
@@ -13,7 +13,7 @@ export type ViewerOptions = {
|
|
|
13
13
|
runId?: string | undefined;
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
/** Interactive client view. All durable reads stay in the workflow
|
|
16
|
+
/** Interactive client view. All durable reads stay in the workflow server. */
|
|
17
17
|
export async function runViewer(options: ViewerOptions): Promise<void> {
|
|
18
18
|
let value: JsonValue = options.runId === undefined ? [] : null;
|
|
19
19
|
let selected = 0;
|
|
@@ -117,8 +117,8 @@ export async function runCommandBatch(
|
|
|
117
117
|
}
|
|
118
118
|
};
|
|
119
119
|
|
|
120
|
-
const
|
|
121
|
-
await Promise.all(Array.from({ length:
|
|
120
|
+
const runnerCount = Math.min(request.maxConcurrency, total);
|
|
121
|
+
await Promise.all(Array.from({ length: runnerCount }, worker));
|
|
122
122
|
|
|
123
123
|
for (let index = 0; index < total; index += 1) {
|
|
124
124
|
if (results[index] !== undefined) continue;
|
package/src/workflows/engine.ts
CHANGED
|
@@ -332,12 +332,11 @@ export class WorkflowEngine {
|
|
|
332
332
|
validateWorkflowDefinition(workflow);
|
|
333
333
|
this.presentationRequired = workflow.presentationPrompt !== undefined;
|
|
334
334
|
// Reset before any await: a park or cancel landing during preparation
|
|
335
|
-
// must survive, or a
|
|
335
|
+
// must survive, or a server drain would hang while the run executes.
|
|
336
336
|
this.cancelled = false;
|
|
337
337
|
this.paused = false;
|
|
338
338
|
this.parked = false;
|
|
339
|
-
const
|
|
340
|
-
const state = loaded.state;
|
|
339
|
+
const state = await this.store.prepareRunResume(runId);
|
|
341
340
|
const sourceMismatch = workflowIdentityMismatch(state, workflow, options.workflowSource);
|
|
342
341
|
if (sourceMismatch && options.force !== true) {
|
|
343
342
|
throw new WorkflowSourceChangedError(runId);
|
|
@@ -456,21 +455,21 @@ export class WorkflowEngine {
|
|
|
456
455
|
this.cancelled = false;
|
|
457
456
|
this.paused = false;
|
|
458
457
|
this.parked = false;
|
|
459
|
-
const
|
|
460
|
-
if (
|
|
458
|
+
const parentState = await this.store.readRunState(parentRunId);
|
|
459
|
+
if (parentState === null) {
|
|
461
460
|
throw new Error(`Cannot continue from unreadable workflow run: ${parentRunId}`);
|
|
462
461
|
}
|
|
463
|
-
if (
|
|
462
|
+
if (parentState.status !== "waiting" || parentState.waitingOn === undefined) {
|
|
464
463
|
throw new Error(
|
|
465
|
-
`Cannot continue workflow run ${parentRunId} with status ${
|
|
464
|
+
`Cannot continue workflow run ${parentRunId} with status ${parentState.status}`,
|
|
466
465
|
);
|
|
467
466
|
}
|
|
468
|
-
const sourceMismatch = workflowIdentityMismatch(
|
|
467
|
+
const sourceMismatch = workflowIdentityMismatch(parentState, workflow, options.workflowSource);
|
|
469
468
|
if (sourceMismatch && options.force !== true) {
|
|
470
469
|
throw new WorkflowSourceChangedError(parentRunId);
|
|
471
470
|
}
|
|
472
471
|
|
|
473
|
-
const waitingNodeId =
|
|
472
|
+
const waitingNodeId = parentState.waitingOn;
|
|
474
473
|
const waitingNode = workflow.nodes[waitingNodeId];
|
|
475
474
|
const humanContract =
|
|
476
475
|
waitingNode?.nodeType === "checkpoint" ? waitingNode.humanDecision : undefined;
|
|
@@ -481,7 +480,7 @@ export class WorkflowEngine {
|
|
|
481
480
|
if (options.humanDecision === undefined) {
|
|
482
481
|
throw new Error(`Checkpoint ${waitingNodeId} requires an accepted verified human decision`);
|
|
483
482
|
}
|
|
484
|
-
const request =
|
|
483
|
+
const request = parentState.finalOutput as HumanDecisionRequest;
|
|
485
484
|
if (
|
|
486
485
|
request?.schema !== "pi-workflows.human-decision-request.v1" ||
|
|
487
486
|
request.decisionId !== options.humanDecision.decisionId ||
|
|
@@ -495,7 +494,7 @@ export class WorkflowEngine {
|
|
|
495
494
|
}
|
|
496
495
|
acceptedResponse = validateHumanDecisionResponse(request, durableDecision.response);
|
|
497
496
|
acceptedNodeId = request.nodeId;
|
|
498
|
-
normalizedInput = structuredClone(
|
|
497
|
+
normalizedInput = structuredClone(parentState.input);
|
|
499
498
|
} else {
|
|
500
499
|
const suppliedInput = input === undefined ? null : input;
|
|
501
500
|
normalizedInput = workflow.input ? await workflow.input(suppliedInput) : suppliedInput;
|
|
@@ -512,9 +511,9 @@ export class WorkflowEngine {
|
|
|
512
511
|
options.runId,
|
|
513
512
|
);
|
|
514
513
|
state.parentRunId = parentRunId;
|
|
515
|
-
state.outputs = structuredClone(
|
|
516
|
-
state.results = structuredClone(
|
|
517
|
-
state.steps = structuredClone(
|
|
514
|
+
state.outputs = structuredClone(parentState.outputs);
|
|
515
|
+
state.results = structuredClone(parentState.results);
|
|
516
|
+
state.steps = structuredClone(parentState.steps);
|
|
518
517
|
if (humanContract !== undefined && options.humanDecision !== undefined) {
|
|
519
518
|
const receipt = {
|
|
520
519
|
decisionId: options.humanDecision.decisionId,
|
|
@@ -557,7 +556,7 @@ export class WorkflowEngine {
|
|
|
557
556
|
...(state.runTitle ? { runTitle: state.runTitle } : {}),
|
|
558
557
|
input: state.input,
|
|
559
558
|
continuedFrom: parentRunId,
|
|
560
|
-
checkpoint:
|
|
559
|
+
checkpoint: parentState.waitingOn,
|
|
561
560
|
carriedSteps: state.steps.length,
|
|
562
561
|
},
|
|
563
562
|
});
|
package/src/workflows/store.ts
CHANGED
|
@@ -156,7 +156,7 @@ export type WorkflowRunTerminalData = {
|
|
|
156
156
|
|
|
157
157
|
export type WorkflowRunStoreOptions = {
|
|
158
158
|
authorityProvider?: (runId: string) => RunWriteAuthority | undefined;
|
|
159
|
-
/**
|
|
159
|
+
/** Server-owned projection updates that must commit with each run snapshot. */
|
|
160
160
|
snapshotLifecycle?: (context: {
|
|
161
161
|
runId: string;
|
|
162
162
|
state: WorkflowRunState;
|
|
@@ -164,13 +164,13 @@ export type WorkflowRunStoreOptions = {
|
|
|
164
164
|
database: StateDatabase;
|
|
165
165
|
now: number;
|
|
166
166
|
}) => void;
|
|
167
|
-
/** The global
|
|
168
|
-
|
|
167
|
+
/** The global server may record an attested Pi session after the runner lease ends. */
|
|
168
|
+
allowServerSessionRecording?: boolean;
|
|
169
169
|
state?: StateDatabase;
|
|
170
170
|
readOnly?: boolean;
|
|
171
171
|
};
|
|
172
172
|
|
|
173
|
-
/** The state boundary used by workflow code, including out-of-process
|
|
173
|
+
/** The state boundary used by workflow code, including out-of-process runners. */
|
|
174
174
|
export interface WorkflowExecutionStore {
|
|
175
175
|
readonly databasePath: string;
|
|
176
176
|
initializeRun(
|
|
@@ -178,11 +178,8 @@ export interface WorkflowExecutionStore {
|
|
|
178
178
|
state: WorkflowRunState,
|
|
179
179
|
options?: InitializeWorkflowRunOptions,
|
|
180
180
|
): Promise<string>;
|
|
181
|
-
prepareRunResume(runId: string): Promise<
|
|
182
|
-
|
|
183
|
-
runId: string,
|
|
184
|
-
options?: ReadWorkflowRunOptions,
|
|
185
|
-
): LoadedWorkflowRun | null | Promise<LoadedWorkflowRun | null>;
|
|
181
|
+
prepareRunResume(runId: string): Promise<WorkflowRunState>;
|
|
182
|
+
readRunState(runId: string): WorkflowRunState | null | Promise<WorkflowRunState | null>;
|
|
186
183
|
writeSnapshot(
|
|
187
184
|
runId: string,
|
|
188
185
|
state: WorkflowRunState,
|
|
@@ -499,7 +496,7 @@ export class WorkflowRunStore {
|
|
|
499
496
|
| ((runId: string) => RunWriteAuthority | undefined)
|
|
500
497
|
| undefined;
|
|
501
498
|
private readonly snapshotLifecycle: WorkflowRunStoreOptions["snapshotLifecycle"];
|
|
502
|
-
private readonly
|
|
499
|
+
private readonly allowServerSessionRecording: boolean;
|
|
503
500
|
private readonly contexts = new Map<string, RunContext>();
|
|
504
501
|
private readonly ownsState: boolean;
|
|
505
502
|
private readonly mutations: StateMutationStore;
|
|
@@ -508,7 +505,7 @@ export class WorkflowRunStore {
|
|
|
508
505
|
constructor(databasePath: string = workflowStatePath(), options: WorkflowRunStoreOptions = {}) {
|
|
509
506
|
this.authorityProvider = options.authorityProvider;
|
|
510
507
|
this.snapshotLifecycle = options.snapshotLifecycle;
|
|
511
|
-
this.
|
|
508
|
+
this.allowServerSessionRecording = options.allowServerSessionRecording === true;
|
|
512
509
|
this.ownsState = options.state === undefined;
|
|
513
510
|
this.state =
|
|
514
511
|
options.state ??
|
|
@@ -526,7 +523,7 @@ export class WorkflowRunStore {
|
|
|
526
523
|
if (this.ownsState) this.state.close();
|
|
527
524
|
}
|
|
528
525
|
|
|
529
|
-
/**
|
|
526
|
+
/** Server-only synchronization after a queue lifecycle mutation on the run resource. */
|
|
530
527
|
synchronizeRevision(runId: string): number {
|
|
531
528
|
const revision = this.resourceRevision(runId);
|
|
532
529
|
const context = this.contexts.get(runId);
|
|
@@ -1466,11 +1463,11 @@ export class WorkflowRunStore {
|
|
|
1466
1463
|
return { runId, originSessionId, followUps };
|
|
1467
1464
|
}
|
|
1468
1465
|
|
|
1469
|
-
async prepareRunResume(runId: string): Promise<
|
|
1470
|
-
const
|
|
1471
|
-
if (
|
|
1472
|
-
if (
|
|
1473
|
-
throw new Error(`Cannot resume workflow run ${runId} with status ${
|
|
1466
|
+
async prepareRunResume(runId: string): Promise<WorkflowRunState> {
|
|
1467
|
+
const state = this.readRunState(runId);
|
|
1468
|
+
if (state === null) throw new Error(`Cannot resume unreadable workflow run: ${runId}`);
|
|
1469
|
+
if (state.status !== "running") {
|
|
1470
|
+
throw new Error(`Cannot resume workflow run ${runId} with status ${state.status}`);
|
|
1474
1471
|
}
|
|
1475
1472
|
this.verifySettingsProjections(runId);
|
|
1476
1473
|
const revision = this.resourceRevision(runId);
|
|
@@ -1498,20 +1495,20 @@ export class WorkflowRunStore {
|
|
|
1498
1495
|
payload: {},
|
|
1499
1496
|
};
|
|
1500
1497
|
insertRunEvent(this.state, row.resourceId, nextRevision, at, traceEvent);
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
this.persistRunState(row,
|
|
1504
|
-
recordViewerDeltas(this.state, runId, runViewerTargets(
|
|
1498
|
+
state.traceSeq = nextRevision;
|
|
1499
|
+
state.updatedAt = at;
|
|
1500
|
+
this.persistRunState(row, state, nextRevision, now);
|
|
1501
|
+
recordViewerDeltas(this.state, runId, runViewerTargets(state, traceEvent), now);
|
|
1505
1502
|
context.revision = nextRevision;
|
|
1506
1503
|
});
|
|
1507
|
-
const prepared = this.
|
|
1504
|
+
const prepared = this.readRunState(runId);
|
|
1508
1505
|
if (prepared === null) throw new Error(`Workflow run became unreadable: ${runId}`);
|
|
1509
1506
|
return prepared;
|
|
1510
1507
|
}
|
|
1511
1508
|
|
|
1512
1509
|
async markRunInterrupted(
|
|
1513
1510
|
runId: string,
|
|
1514
|
-
reason = "Workflow
|
|
1511
|
+
reason = "Workflow server stopped before the run finished",
|
|
1515
1512
|
): Promise<LoadedWorkflowRun | null> {
|
|
1516
1513
|
const loaded = this.readRun(runId);
|
|
1517
1514
|
if (loaded === null || loaded.state.status !== "running") return loaded;
|
|
@@ -1549,7 +1546,7 @@ export class WorkflowRunStore {
|
|
|
1549
1546
|
);
|
|
1550
1547
|
}
|
|
1551
1548
|
|
|
1552
|
-
/**
|
|
1549
|
+
/** Server-only synchronous form for an already serialized command transaction. */
|
|
1553
1550
|
publishUpdateSynchronous(
|
|
1554
1551
|
runId: string,
|
|
1555
1552
|
state: WorkflowRunState,
|
|
@@ -1765,7 +1762,7 @@ export class WorkflowRunStore {
|
|
|
1765
1762
|
},
|
|
1766
1763
|
};
|
|
1767
1764
|
insertRunEvent(this.state, run.resourceId, revision, at, traceEvent);
|
|
1768
|
-
const current = this.
|
|
1765
|
+
const current = this.materializeRunState(run, this.readDefinition(run.definitionHash));
|
|
1769
1766
|
current.traceSeq = revision;
|
|
1770
1767
|
current.updatedAt = at;
|
|
1771
1768
|
this.persistRunState(run, current, revision, now);
|
|
@@ -2246,11 +2243,17 @@ export class WorkflowRunStore {
|
|
|
2246
2243
|
};
|
|
2247
2244
|
}
|
|
2248
2245
|
|
|
2246
|
+
readRunState(runId: string): WorkflowRunState | null {
|
|
2247
|
+
const row = this.readRunRow(runId);
|
|
2248
|
+
if (row === undefined) return null;
|
|
2249
|
+
return this.materializeRunState(row, this.readDefinition(row.definitionHash));
|
|
2250
|
+
}
|
|
2251
|
+
|
|
2249
2252
|
readRun(runId: string, options: ReadWorkflowRunOptions = {}): LoadedWorkflowRun | null {
|
|
2250
2253
|
const row = this.readRunRow(runId);
|
|
2251
2254
|
if (row === undefined) return null;
|
|
2252
2255
|
const snapshot = this.readDefinition(row.definitionHash);
|
|
2253
|
-
const state = this.
|
|
2256
|
+
const state = this.materializeRunState(row, snapshot);
|
|
2254
2257
|
const segments = this.segmentRows(runId).map((segment) => this.loadSegment(segment, state));
|
|
2255
2258
|
const flat = segments.find((segment) => segment.attemptId === "") ?? emptySegment();
|
|
2256
2259
|
return {
|
|
@@ -2529,7 +2532,7 @@ export class WorkflowRunStore {
|
|
|
2529
2532
|
|
|
2530
2533
|
private assertSessionWriteAuthority(runId: string): void {
|
|
2531
2534
|
const run = this.requireRunRow(runId);
|
|
2532
|
-
if (this.
|
|
2535
|
+
if (this.allowServerSessionRecording) return;
|
|
2533
2536
|
this.assertWriteAuthority(run, this.contextFor(runId).revision);
|
|
2534
2537
|
}
|
|
2535
2538
|
|
|
@@ -3522,7 +3525,7 @@ export class WorkflowRunStore {
|
|
|
3522
3525
|
return row;
|
|
3523
3526
|
}
|
|
3524
3527
|
|
|
3525
|
-
private
|
|
3528
|
+
private materializeRunState(row: RunRow, snapshot: WorkflowDefinitionSnapshot): WorkflowRunState {
|
|
3526
3529
|
const steps = this.readSteps(row.runId);
|
|
3527
3530
|
return this.buildRunState(
|
|
3528
3531
|
row,
|
package/src/workflows/types.ts
CHANGED
|
@@ -96,7 +96,7 @@ export type WorkflowProgressData = {
|
|
|
96
96
|
export type WorkflowNodeCommon = {
|
|
97
97
|
/**
|
|
98
98
|
* Per-node active-execution timeout or a callback that derives it from the run context.
|
|
99
|
-
* Waiting, paused, disconnected, and
|
|
99
|
+
* Waiting, paused, disconnected, and server-down origin-session time does not count.
|
|
100
100
|
* Null disables the deadline. Omission falls back to the engine default (15 minutes).
|
|
101
101
|
*/
|
|
102
102
|
timeoutMs?: number | null | ((context: WorkflowNodeContext) => MaybePromise<number | null>);
|
|
@@ -962,7 +962,7 @@ export type WorkflowEngineOptions = {
|
|
|
962
962
|
notificationSink?: WorkflowNotificationSink;
|
|
963
963
|
/** Canonical SQLite database. Defaults to `~/.pi/agent/workflows/state.sqlite`. */
|
|
964
964
|
databasePath?: string;
|
|
965
|
-
/** Durable execution store.
|
|
965
|
+
/** Durable execution store. Runners use a server-backed implementation. */
|
|
966
966
|
store?: import("./store.js").WorkflowExecutionStore;
|
|
967
967
|
/**
|
|
968
968
|
* Awaited after `run_started` is persisted, before any node executes.
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { ControllerCondition, ControllerConditionInput, ControllerResourceStatus, ControllerStatusPatch } from "./types.js";
|
|
2
|
-
export declare function conditionTrue(type: string, reason: string, message?: string): ControllerConditionInput;
|
|
3
|
-
export declare function conditionFalse(type: string, reason: string, message?: string): ControllerConditionInput;
|
|
4
|
-
export declare function conditionUnknown(type: string, reason: string, message?: string): ControllerConditionInput;
|
|
5
|
-
export declare function applyStatusPatch<TStatus>(current: ControllerResourceStatus<TStatus>, patch: ControllerStatusPatch<TStatus> | undefined, generation: number, now: string): ControllerResourceStatus<TStatus>;
|
|
6
|
-
export declare function mergeConditions(current: ControllerCondition[], updates: ControllerConditionInput[], generation: number, now: string): ControllerCondition[];
|