@osolmaz/pi-workflows 0.12.1 → 0.13.0
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 +52 -28
- package/dist/builtins/autodoc.workflow.d.ts +4 -4
- package/dist/builtins/autoimplement.workflow.d.ts +369 -73
- package/dist/builtins/autoplan.workflow.d.ts +126 -13
- package/dist/builtins/autoplan.workflow.js +200 -29
- package/dist/builtins/autoplan.workflow.js.map +1 -1
- package/dist/builtins/catalog.js +6 -4
- package/dist/builtins/catalog.js.map +1 -1
- package/dist/builtins/index.d.ts +2 -1
- package/dist/builtins/index.js +1 -0
- package/dist/builtins/index.js.map +1 -1
- package/dist/builtins/monitor.workflow.d.ts +56 -21
- package/dist/builtins/monitor.workflow.js +497 -216
- package/dist/builtins/monitor.workflow.js.map +1 -1
- package/dist/builtins/plain-summary.workflow.d.ts +32 -0
- package/dist/builtins/plain-summary.workflow.js +134 -0
- package/dist/builtins/plain-summary.workflow.js.map +1 -0
- package/dist/builtins/plan-change.workflow.d.ts +170 -22
- package/dist/builtins/sanity-check.workflow.d.ts +45 -3
- package/dist/builtins/sanity-check.workflow.js +45 -7
- package/dist/builtins/sanity-check.workflow.js.map +1 -1
- package/dist/controllers/effects.d.ts +3 -2
- package/dist/controllers/effects.js +8 -1
- package/dist/controllers/effects.js.map +1 -1
- package/dist/controllers/index.d.ts +1 -1
- package/dist/controllers/index.js +1 -1
- package/dist/controllers/index.js.map +1 -1
- package/dist/controllers/manager.d.ts +1 -0
- package/dist/controllers/manager.js +41 -17
- package/dist/controllers/manager.js.map +1 -1
- package/dist/controllers/sqlite.d.ts +107 -85
- package/dist/controllers/sqlite.js +1611 -1486
- package/dist/controllers/sqlite.js.map +1 -1
- package/dist/controllers/store.d.ts +9 -8
- package/dist/controllers/store.js +1 -31
- package/dist/controllers/store.js.map +1 -1
- package/dist/controllers/types.d.ts +3 -0
- package/dist/controllers/workflow-engine-scheduler.d.ts +1 -1
- package/dist/controllers/workflow-engine-scheduler.js +3 -5
- package/dist/controllers/workflow-engine-scheduler.js.map +1 -1
- package/dist/controllers/workflows.d.ts +2 -2
- package/dist/controllers/workflows.js +11 -10
- package/dist/controllers/workflows.js.map +1 -1
- package/dist/extension/controller-host.js +2 -2
- package/dist/extension/controller-host.js.map +1 -1
- package/dist/extension/decision-channels.d.ts +2 -0
- package/dist/extension/decision-channels.js +311 -118
- package/dist/extension/decision-channels.js.map +1 -1
- package/dist/extension/executor.d.ts +9 -2
- package/dist/extension/executor.js +89 -2
- package/dist/extension/executor.js.map +1 -1
- package/dist/extension/herdr-viewer.d.ts +0 -1
- package/dist/extension/herdr-viewer.js +0 -4
- package/dist/extension/herdr-viewer.js.map +1 -1
- package/dist/extension/index.js +156 -103
- package/dist/extension/index.js.map +1 -1
- package/dist/extension/recorder.d.ts +3 -10
- package/dist/extension/recorder.js +13 -22
- package/dist/extension/recorder.js.map +1 -1
- package/dist/extension/step-message.d.ts +7 -2
- package/dist/extension/step-message.js +94 -2
- package/dist/extension/step-message.js.map +1 -1
- package/dist/extension/widget.js +6 -0
- package/dist/extension/widget.js.map +1 -1
- package/dist/host/rpc-executor.d.ts +3 -0
- package/dist/host/rpc-executor.js +2 -0
- package/dist/host/rpc-executor.js.map +1 -1
- package/dist/host/runner.d.ts +3 -6
- package/dist/host/runner.js +83 -32
- package/dist/host/runner.js.map +1 -1
- package/dist/render/graph-render.js +14 -8
- package/dist/render/graph-render.js.map +1 -1
- package/dist/state/database.d.ts +35 -0
- package/dist/state/database.js +287 -0
- package/dist/state/database.js.map +1 -0
- package/dist/state/index.d.ts +4 -0
- package/dist/state/index.js +5 -0
- package/dist/state/index.js.map +1 -0
- package/dist/state/json.d.ts +6 -0
- package/dist/state/json.js +38 -0
- package/dist/state/json.js.map +1 -0
- package/dist/state/mutation.d.ts +57 -0
- package/dist/state/mutation.js +255 -0
- package/dist/state/mutation.js.map +1 -0
- package/dist/state/schema.d.ts +6 -0
- package/dist/state/schema.js +473 -0
- package/dist/state/schema.js.map +1 -0
- package/dist/viewer/cli.d.ts +2 -2
- package/dist/viewer/cli.js +142 -122
- package/dist/viewer/cli.js.map +1 -1
- package/dist/viewer/render.d.ts +4 -4
- package/dist/viewer/render.js +2 -17
- package/dist/viewer/render.js.map +1 -1
- package/dist/viewer/session-reducer.d.ts +1 -1
- package/dist/viewer/session-reducer.js +1 -1
- package/dist/viewer/tui.d.ts +3 -3
- package/dist/viewer/tui.js +15 -13
- package/dist/viewer/tui.js.map +1 -1
- package/dist/viewer/watch.d.ts +2 -5
- package/dist/viewer/watch.js +13 -13
- package/dist/viewer/watch.js.map +1 -1
- package/dist/workflows/composition.js +12 -2
- package/dist/workflows/composition.js.map +1 -1
- package/dist/workflows/definition.d.ts +6 -2
- package/dist/workflows/definition.js +17 -0
- package/dist/workflows/definition.js.map +1 -1
- package/dist/workflows/engine.d.ts +6 -6
- package/dist/workflows/engine.js +183 -103
- package/dist/workflows/engine.js.map +1 -1
- package/dist/workflows/errors.d.ts +3 -3
- package/dist/workflows/errors.js +3 -3
- package/dist/workflows/human-decision.d.ts +29 -4
- package/dist/workflows/human-decision.js +547 -283
- package/dist/workflows/human-decision.js.map +1 -1
- package/dist/workflows/index.d.ts +4 -5
- package/dist/workflows/index.js +3 -4
- package/dist/workflows/index.js.map +1 -1
- package/dist/workflows/schema.js +17 -1
- package/dist/workflows/schema.js.map +1 -1
- package/dist/workflows/store.d.ts +83 -103
- package/dist/workflows/store.js +902 -951
- package/dist/workflows/store.js.map +1 -1
- package/dist/workflows/types.d.ts +55 -81
- package/docs/CONTROLLERS.md +10 -8
- package/docs/DEFERRED_TURNS.md +2 -2
- package/docs/DESIGN_PHILOSOPHY.md +4 -2
- package/docs/HUMAN_DECISIONS.md +13 -14
- package/docs/MONITOR.md +179 -231
- package/docs/SQLITE_STATE.md +219 -0
- package/docs/WORKFLOW_COMPOSITION.md +19 -19
- package/docs/WORKFLOW_STEP_MESSAGES.md +20 -18
- package/docs/WORKFLOW_UPDATES.md +10 -9
- package/docs/development.md +9 -9
- package/docs/live-replay-protocol.md +28 -37
- package/docs/plans/2026-08-16-workflow-updates-plan.md +1 -1
- package/docs/plans/2026-08-19-human-decision-gates-plan.md +1 -1
- package/docs/plans/2026-08-21-autoimplement-timeout-fallback-plan.md +1 -1
- package/docs/plans/2026-08-21-plan-change-approval-policy-plan.md +1 -1
- package/docs/plans/2026-08-21-sanity-check-plan.md +30 -16
- package/docs/plans/2026-08-22-goal-finishing-monitor-plan.md +200 -0
- package/docs/plans/2026-08-23-assistant-agent-completion-plan.md +701 -0
- package/docs/plans/2026-08-23-sqlite-state-plan.md +970 -0
- package/docs/plans/replayable-run-bundles-implementation-plan.md +2 -2
- package/docs/plans/session-event-replay-implementation-plan.md +1 -1
- package/docs/plans/tui-viewer-implementation-plan.md +1 -1
- package/docs/session-event-journal.md +63 -434
- package/docs/tui-viewer.md +13 -15
- package/docs/workflows.md +104 -50
- package/examples/workflows/plain-summary.workflow.ts +1 -0
- package/herdr-plugin.toml +1 -1
- package/package.json +1 -1
- package/plugins/herdr/viewer.mjs +1 -11
- package/skills/autoplan/SKILL.md +5 -4
- package/skills/monitor/SKILL.md +91 -163
- package/skills/pi-workflows/SKILL.md +7 -7
- package/skills/sanity-check/SKILL.md +1 -1
- package/src/builtins/autoplan.workflow.ts +290 -40
- package/src/builtins/catalog.ts +6 -4
- package/src/builtins/index.ts +7 -1
- package/src/builtins/monitor.workflow.ts +663 -249
- package/src/builtins/plain-summary.workflow.ts +185 -0
- package/src/builtins/sanity-check.workflow.ts +62 -7
- package/src/controllers/effects.ts +7 -0
- package/src/controllers/index.ts +0 -6
- package/src/controllers/manager.ts +72 -32
- package/src/controllers/sqlite.ts +2420 -1948
- package/src/controllers/store.ts +17 -38
- package/src/controllers/types.ts +3 -0
- package/src/controllers/workflow-engine-scheduler.ts +4 -8
- package/src/controllers/workflows.ts +45 -21
- package/src/extension/controller-host.ts +1 -2
- package/src/extension/decision-channels.ts +427 -144
- package/src/extension/executor.ts +105 -2
- package/src/extension/herdr-viewer.ts +0 -5
- package/src/extension/index.ts +180 -118
- package/src/extension/recorder.ts +13 -22
- package/src/extension/step-message.ts +106 -4
- package/src/extension/widget.ts +8 -0
- package/src/host/rpc-executor.ts +4 -0
- package/src/host/runner.ts +93 -41
- package/src/render/graph-render.ts +11 -4
- package/src/state/database.ts +375 -0
- package/src/state/index.ts +29 -0
- package/src/state/json.ts +42 -0
- package/src/state/mutation.ts +435 -0
- package/src/state/schema.ts +475 -0
- package/src/viewer/cli.ts +151 -144
- package/src/viewer/render.ts +12 -24
- package/src/viewer/session-reducer.ts +1 -1
- package/src/viewer/tui.ts +18 -17
- package/src/viewer/watch.ts +13 -17
- package/src/workflows/composition.ts +12 -3
- package/src/workflows/definition.ts +36 -3
- package/src/workflows/engine.ts +212 -116
- package/src/workflows/errors.ts +3 -3
- package/src/workflows/human-decision.ts +795 -340
- package/src/workflows/index.ts +15 -19
- package/src/workflows/schema.ts +19 -1
- package/src/workflows/store.ts +1295 -1163
- package/src/workflows/types.ts +61 -81
- package/dist/workflows/artifacts.d.ts +0 -40
- package/dist/workflows/artifacts.js +0 -155
- package/dist/workflows/artifacts.js.map +0 -1
- package/dist/workflows/migrate-sources.d.ts +0 -42
- package/dist/workflows/migrate-sources.js +0 -133
- package/dist/workflows/migrate-sources.js.map +0 -1
- package/docs/run-bundles.md +0 -481
- package/src/workflows/artifacts.ts +0 -188
- package/src/workflows/migrate-sources.ts +0 -178
package/src/workflows/types.ts
CHANGED
|
@@ -97,21 +97,35 @@ export type WorkflowEdge =
|
|
|
97
97
|
};
|
|
98
98
|
};
|
|
99
99
|
|
|
100
|
-
/**
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
100
|
+
/** A visible assistant response used as the accepted agent-node output. */
|
|
101
|
+
export type AssistantMessageOutput = {
|
|
102
|
+
kind: "assistant-message";
|
|
103
|
+
/** Optional author-supplied character limit. Omission adds no workflow limit. */
|
|
104
|
+
maxChars?: number;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export type AgentExpectedOutput = string | AssistantMessageOutput;
|
|
108
|
+
|
|
109
|
+
type AgentNodeBase = WorkflowNodeCommon & {
|
|
109
110
|
nodeType: "agent";
|
|
110
111
|
prompt: (context: WorkflowNodeContext) => MaybePromise<string>;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
/** The existing workflow-tool submission form. */
|
|
115
|
+
export type SubmittedAgentNodeDefinition = AgentNodeBase & {
|
|
111
116
|
expectedOutput?: string;
|
|
112
117
|
validate?: (output: unknown, context: WorkflowNodeContext) => MaybePromise<unknown>;
|
|
113
118
|
};
|
|
114
119
|
|
|
120
|
+
/** A model turn whose normal visible assistant text becomes the node output. */
|
|
121
|
+
export type AssistantAgentNodeDefinition = AgentNodeBase & {
|
|
122
|
+
expectedOutput: AssistantMessageOutput;
|
|
123
|
+
validate?: never;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/** A model-shaped step with either submitted or visible assistant output. */
|
|
127
|
+
export type AgentNodeDefinition = SubmittedAgentNodeDefinition | AssistantAgentNodeDefinition;
|
|
128
|
+
|
|
115
129
|
/** A pure local function: shape inputs, route, format, derive values. */
|
|
116
130
|
export type ComputeNodeDefinition = WorkflowNodeCommon & {
|
|
117
131
|
nodeType: "compute";
|
|
@@ -529,7 +543,7 @@ export type WorkflowDefinition<
|
|
|
529
543
|
/**
|
|
530
544
|
* Optional instructions for a normal assistant response after the run ends.
|
|
531
545
|
* The Pi extension resolves this only after the final state is persisted;
|
|
532
|
-
* the engine and
|
|
546
|
+
* the engine and durable store remain presentation-agnostic.
|
|
533
547
|
*/
|
|
534
548
|
presentationPrompt?:
|
|
535
549
|
| string
|
|
@@ -545,27 +559,7 @@ export type WorkflowDefinition<
|
|
|
545
559
|
|
|
546
560
|
export type WorkflowNodeOutcome = "ok" | "timed_out" | "failed" | "cancelled";
|
|
547
561
|
|
|
548
|
-
/**
|
|
549
|
-
* Reference to a content-addressed file under the bundle's `artifacts/`
|
|
550
|
-
* directory. Large string leaves inside persisted values are replaced by
|
|
551
|
-
* `{ "$artifact": ArtifactRef }` at write time (see `docs/run-bundles.md`).
|
|
552
|
-
*/
|
|
553
|
-
export type ArtifactRef = {
|
|
554
|
-
/** Bundle-relative path, `artifacts/sha256-<64 hex>.txt`. */
|
|
555
|
-
path: string;
|
|
556
|
-
mediaType: string;
|
|
557
|
-
bytes: number;
|
|
558
|
-
/** Hex digest of the artifact bytes. */
|
|
559
|
-
sha256: string;
|
|
560
|
-
};
|
|
561
|
-
|
|
562
|
-
/** The sentinel wrapper that replaces an externalized value. */
|
|
563
|
-
export type ArtifactValue = { $artifact: ArtifactRef };
|
|
564
|
-
|
|
565
|
-
/**
|
|
566
|
-
* Explicit linkage from a workflow attempt to the Pi conversation slice it
|
|
567
|
-
* produced. Ids address entries in `session/entries.ndjson` by Pi entry id.
|
|
568
|
-
*/
|
|
562
|
+
/** Explicit linkage from a workflow attempt to the Pi conversation slice it produced. */
|
|
569
563
|
export type ConversationRange = {
|
|
570
564
|
/** First Pi session entry id of the attempt. */
|
|
571
565
|
firstEntryId: string;
|
|
@@ -602,14 +596,13 @@ export type WorkflowStepRecord = {
|
|
|
602
596
|
outcome: WorkflowNodeOutcome;
|
|
603
597
|
startedAt: string;
|
|
604
598
|
finishedAt: string;
|
|
605
|
-
/**
|
|
606
|
-
|
|
607
|
-
* persisted bundle a large prompt may be an `ArtifactValue`.
|
|
608
|
-
*/
|
|
609
|
-
prompt: string | ArtifactValue | null;
|
|
599
|
+
/** Full prompt text for agent steps, `null` for other node types. */
|
|
600
|
+
prompt: string | null;
|
|
610
601
|
output: unknown;
|
|
611
602
|
error?: string;
|
|
612
603
|
action?: WorkflowActionReceipt;
|
|
604
|
+
/** Receipt for a visible assistant-message completion. */
|
|
605
|
+
assistantMessage?: AssistantMessageReceipt;
|
|
613
606
|
/** For agent steps recorded inside a Pi conversation. */
|
|
614
607
|
conversation?: ConversationRange;
|
|
615
608
|
};
|
|
@@ -646,11 +639,7 @@ export type WorkflowCompositionSnapshot = {
|
|
|
646
639
|
|
|
647
640
|
export type WorkflowRunState = {
|
|
648
641
|
schema: "pi-workflows.run-state.v1";
|
|
649
|
-
/**
|
|
650
|
-
* `seq` of the trace event this projection reflects. `trace.ndjson` is the
|
|
651
|
-
* source of truth; a state whose `traceSeq` is older than the trace tail is
|
|
652
|
-
* a stale projection.
|
|
653
|
-
*/
|
|
642
|
+
/** Resource revision of the event reflected by this projection. */
|
|
654
643
|
traceSeq: number;
|
|
655
644
|
runId: string;
|
|
656
645
|
workflowName: string;
|
|
@@ -700,7 +689,7 @@ export type WorkflowNodeSnapshot = {
|
|
|
700
689
|
timeoutMs?: number | null;
|
|
701
690
|
statusDetail?: string;
|
|
702
691
|
summary?: string;
|
|
703
|
-
expectedOutput?:
|
|
692
|
+
expectedOutput?: AgentExpectedOutput;
|
|
704
693
|
actionExecution?: "function" | "shell";
|
|
705
694
|
mountPath?: string[];
|
|
706
695
|
localNodeId?: string;
|
|
@@ -758,7 +747,7 @@ export type WorkflowSessionBinding = {
|
|
|
758
747
|
export type WorkflowSessionEntryRecord = {
|
|
759
748
|
/** Starts at 1, increases by exactly 1 within the file. */
|
|
760
749
|
seq: number;
|
|
761
|
-
/** When the entry was recorded
|
|
750
|
+
/** When the entry was recorded in workflow state. */
|
|
762
751
|
at: string;
|
|
763
752
|
/** Verbatim Pi session entry (has its own id/parentId/timestamp). */
|
|
764
753
|
entry: Record<string, unknown>;
|
|
@@ -808,32 +797,21 @@ export type WorkflowSessionCapture = {
|
|
|
808
797
|
|
|
809
798
|
export type WorkflowTraceEventDraft = Omit<WorkflowTraceEvent, "seq" | "at" | "runId">;
|
|
810
799
|
|
|
811
|
-
export type
|
|
812
|
-
schema: "pi-workflows.run-bundle.v1";
|
|
800
|
+
export type WorkflowRunResult = {
|
|
813
801
|
runId: string;
|
|
814
|
-
|
|
815
|
-
runTitle?: string;
|
|
816
|
-
workflowSource?: WorkflowSource;
|
|
817
|
-
workflowSources?: WorkflowMountedSource[];
|
|
818
|
-
definitionDigest?: string;
|
|
819
|
-
startedAt: string;
|
|
820
|
-
finishedAt?: string;
|
|
821
|
-
status: WorkflowRunStatus;
|
|
822
|
-
traceSchema: "pi-workflows.trace-event.v1";
|
|
823
|
-
paths: {
|
|
824
|
-
workflow: string;
|
|
825
|
-
state: string;
|
|
826
|
-
trace: string;
|
|
827
|
-
/** Bundle-relative session directory, present once a session is bound. */
|
|
828
|
-
session?: string;
|
|
829
|
-
/** Bundle-relative artifacts directory, present once a value was externalized. */
|
|
830
|
-
artifacts?: string;
|
|
831
|
-
};
|
|
802
|
+
state: WorkflowRunState;
|
|
832
803
|
};
|
|
833
804
|
|
|
834
|
-
export type
|
|
835
|
-
|
|
836
|
-
|
|
805
|
+
export type AgentStepCompletion = "submit" | "assistant";
|
|
806
|
+
|
|
807
|
+
export type AssistantMessageReceipt = {
|
|
808
|
+
sha256: string;
|
|
809
|
+
/** Pi session entry containing the visible response, when available. */
|
|
810
|
+
entryId?: string;
|
|
811
|
+
/** Present only when the workflow author supplied a limit. */
|
|
812
|
+
maxChars?: number;
|
|
813
|
+
/** True when recovery adopted an already visible response. */
|
|
814
|
+
recovered?: boolean;
|
|
837
815
|
};
|
|
838
816
|
|
|
839
817
|
/** The step contract handed to the executor alongside the prompt. */
|
|
@@ -842,7 +820,9 @@ export type AgentStepContract = {
|
|
|
842
820
|
workflowName: string;
|
|
843
821
|
nodeId: string;
|
|
844
822
|
attemptId: string;
|
|
823
|
+
completion: AgentStepCompletion;
|
|
845
824
|
expectedOutput?: string;
|
|
825
|
+
maxOutputChars?: number;
|
|
846
826
|
};
|
|
847
827
|
|
|
848
828
|
/** Optional human-facing labels for an agent step. They never affect execution. */
|
|
@@ -869,6 +849,8 @@ export type AgentStepRequest = {
|
|
|
869
849
|
|
|
870
850
|
export type AgentStepSubmission = {
|
|
871
851
|
output: unknown;
|
|
852
|
+
/** Receipt for a visible assistant response. */
|
|
853
|
+
assistantMessage?: AssistantMessageReceipt;
|
|
872
854
|
/**
|
|
873
855
|
* The Pi conversation slice this step produced, when the executor records
|
|
874
856
|
* one. Persisted verbatim into the step record and terminal node event.
|
|
@@ -878,10 +860,15 @@ export type AgentStepSubmission = {
|
|
|
878
860
|
|
|
879
861
|
/**
|
|
880
862
|
* Runs one agent step to completion. Implementations deliver the prompt to
|
|
881
|
-
* the model and resolve once
|
|
863
|
+
* the model and resolve once the configured output has been accepted.
|
|
882
864
|
* Must reject with an `AbortError`-like error when `signal` aborts.
|
|
883
865
|
*/
|
|
884
866
|
export interface AgentStepExecutor {
|
|
867
|
+
/**
|
|
868
|
+
* `visible` supports assistant-message output. `park` asks the engine to
|
|
869
|
+
* leave the run claimable for an origin session. Omission is unsupported.
|
|
870
|
+
*/
|
|
871
|
+
readonly assistantMessageMode?: "visible" | "park" | "unsupported";
|
|
885
872
|
runAgentStep(request: AgentStepRequest, signal: AbortSignal): Promise<AgentStepSubmission>;
|
|
886
873
|
}
|
|
887
874
|
|
|
@@ -909,26 +896,19 @@ export type WorkflowEngineOptions = {
|
|
|
909
896
|
executor: AgentStepExecutor;
|
|
910
897
|
/** Durable destination for notify nodes. Required when a workflow uses one. */
|
|
911
898
|
notificationSink?: WorkflowNotificationSink;
|
|
912
|
-
/**
|
|
913
|
-
|
|
914
|
-
/**
|
|
915
|
-
* Shared run store. Pass the same instance used by a session recorder so
|
|
916
|
-
* trace sequence numbers stay single-writer. Defaults to a new store on
|
|
917
|
-
* `outputRoot`.
|
|
918
|
-
*/
|
|
899
|
+
/** Canonical SQLite database. Defaults to `~/.pi/agent/workflows/state.sqlite`. */
|
|
900
|
+
databasePath?: string;
|
|
901
|
+
/** Shared SQLite run store. */
|
|
919
902
|
store?: import("./store.js").WorkflowRunStore;
|
|
920
903
|
/**
|
|
921
|
-
* Awaited after `run_started` is persisted, before any node executes.
|
|
922
|
-
* is where a session recorder binds, so `session_bound` lands at the start
|
|
923
|
-
* of the trace and can never trail the terminal event.
|
|
904
|
+
* Awaited after `run_started` is persisted, before any node executes.
|
|
924
905
|
*/
|
|
925
|
-
onRunStarted?: (
|
|
906
|
+
onRunStarted?: (runId: string, state: WorkflowRunState) => MaybePromise<void>;
|
|
926
907
|
/**
|
|
927
908
|
* Awaited before the terminal snapshot is persisted. This is where a
|
|
928
|
-
* session recorder stops and drains
|
|
929
|
-
* the terminal event exists. Errors are swallowed: finishing the run wins.
|
|
909
|
+
* session recorder stops and drains. Errors are swallowed: finishing the run wins.
|
|
930
910
|
*/
|
|
931
|
-
onRunFinishing?: (
|
|
911
|
+
onRunFinishing?: (runId: string, state: WorkflowRunState) => MaybePromise<void>;
|
|
932
912
|
/** Default per-node timeout. Defaults to 15 minutes. */
|
|
933
913
|
defaultNodeTimeoutMs?: number;
|
|
934
914
|
/** Guard against unbounded graph loops. Defaults to 100 executed steps. */
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import type { ArtifactRef, ArtifactValue } from "./types.js";
|
|
2
|
-
/**
|
|
3
|
-
* Value externalization for run bundles (see docs/run-bundles.md): string
|
|
4
|
-
* leaves larger than the threshold are written once, content-addressed, under
|
|
5
|
-
* `artifacts/` and replaced by `{ "$artifact": ref }`. Because artifacts are
|
|
6
|
-
* immutable and deduplicated by hash, the same output appearing in `outputs`,
|
|
7
|
-
* `results`, `steps`, and the trace costs one file plus small references.
|
|
8
|
-
*/
|
|
9
|
-
export declare const ARTIFACT_THRESHOLD_BYTES = 4096;
|
|
10
|
-
export declare const ARTIFACTS_DIR = "artifacts";
|
|
11
|
-
export declare function isArtifactValue(value: unknown): value is ArtifactValue;
|
|
12
|
-
/**
|
|
13
|
-
* Writes externalized string leaves for one run bundle. Instances serialize
|
|
14
|
-
* their writes and deduplicate by content hash, so encoding the same large
|
|
15
|
-
* string in several value positions touches the filesystem once.
|
|
16
|
-
*/
|
|
17
|
-
export declare class ArtifactWriter {
|
|
18
|
-
private readonly runDir;
|
|
19
|
-
private readonly written;
|
|
20
|
-
private chain;
|
|
21
|
-
private dirCreated;
|
|
22
|
-
constructor(runDir: string);
|
|
23
|
-
/** True once any artifact exists for this run. */
|
|
24
|
-
get hasArtifacts(): boolean;
|
|
25
|
-
externalize(text: string): Promise<ArtifactValue>;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Encode a persisted value: externalize large string leaves and escape
|
|
29
|
-
* single-key `$artifact`/`$escaped` objects so the sentinel stays
|
|
30
|
-
* unambiguous. Returns the original value when nothing changed.
|
|
31
|
-
*/
|
|
32
|
-
export declare function encodeValue(value: unknown, writer: ArtifactWriter, thresholdBytes?: number): Promise<unknown>;
|
|
33
|
-
/**
|
|
34
|
-
* Walk a persisted value, replacing every `$artifact` sentinel via `resolve`
|
|
35
|
-
* and unwrapping `$escaped` objects. `resolve` may return the artifact
|
|
36
|
-
* contents or a placeholder; it receives the reference untouched.
|
|
37
|
-
*/
|
|
38
|
-
export declare function decodeValueWith(value: unknown, resolve: (ref: ArtifactRef) => unknown): unknown;
|
|
39
|
-
/** Resolve every artifact reference in `value` by reading the bundle files. */
|
|
40
|
-
export declare function resolveArtifacts(value: unknown, runDir: string): Promise<unknown>;
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import fs from "node:fs/promises";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
/**
|
|
5
|
-
* Value externalization for run bundles (see docs/run-bundles.md): string
|
|
6
|
-
* leaves larger than the threshold are written once, content-addressed, under
|
|
7
|
-
* `artifacts/` and replaced by `{ "$artifact": ref }`. Because artifacts are
|
|
8
|
-
* immutable and deduplicated by hash, the same output appearing in `outputs`,
|
|
9
|
-
* `results`, `steps`, and the trace costs one file plus small references.
|
|
10
|
-
*/
|
|
11
|
-
export const ARTIFACT_THRESHOLD_BYTES = 4096;
|
|
12
|
-
export const ARTIFACTS_DIR = "artifacts";
|
|
13
|
-
const ARTIFACT_KEY = "$artifact";
|
|
14
|
-
const ESCAPED_KEY = "$escaped";
|
|
15
|
-
export function isArtifactValue(value) {
|
|
16
|
-
return (typeof value === "object" &&
|
|
17
|
-
value !== null &&
|
|
18
|
-
!Array.isArray(value) &&
|
|
19
|
-
Object.keys(value).length === 1 &&
|
|
20
|
-
ARTIFACT_KEY in value);
|
|
21
|
-
}
|
|
22
|
-
function isEscapedValue(value) {
|
|
23
|
-
return (typeof value === "object" &&
|
|
24
|
-
value !== null &&
|
|
25
|
-
!Array.isArray(value) &&
|
|
26
|
-
Object.keys(value).length === 1 &&
|
|
27
|
-
ESCAPED_KEY in value);
|
|
28
|
-
}
|
|
29
|
-
/** True for a single-key object that would be misread as a sentinel. */
|
|
30
|
-
function needsEscape(value) {
|
|
31
|
-
const keys = Object.keys(value);
|
|
32
|
-
return keys.length === 1 && (keys[0] === ARTIFACT_KEY || keys[0] === ESCAPED_KEY);
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Writes externalized string leaves for one run bundle. Instances serialize
|
|
36
|
-
* their writes and deduplicate by content hash, so encoding the same large
|
|
37
|
-
* string in several value positions touches the filesystem once.
|
|
38
|
-
*/
|
|
39
|
-
export class ArtifactWriter {
|
|
40
|
-
runDir;
|
|
41
|
-
written = new Set();
|
|
42
|
-
chain = Promise.resolve();
|
|
43
|
-
dirCreated = false;
|
|
44
|
-
constructor(runDir) {
|
|
45
|
-
this.runDir = runDir;
|
|
46
|
-
}
|
|
47
|
-
/** True once any artifact exists for this run. */
|
|
48
|
-
get hasArtifacts() {
|
|
49
|
-
return this.written.size > 0;
|
|
50
|
-
}
|
|
51
|
-
async externalize(text) {
|
|
52
|
-
const bytes = Buffer.from(text, "utf8");
|
|
53
|
-
const sha256 = createHash("sha256").update(bytes).digest("hex");
|
|
54
|
-
const relativePath = `${ARTIFACTS_DIR}/sha256-${sha256}.txt`;
|
|
55
|
-
const task = this.chain.then(async () => {
|
|
56
|
-
if (this.written.has(sha256)) {
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
if (!this.dirCreated) {
|
|
60
|
-
await fs.mkdir(path.join(this.runDir, ARTIFACTS_DIR), { recursive: true, mode: 0o700 });
|
|
61
|
-
this.dirCreated = true;
|
|
62
|
-
}
|
|
63
|
-
const filePath = path.join(this.runDir, relativePath);
|
|
64
|
-
// Content-addressed files are immutable; an existing file is complete
|
|
65
|
-
// unless a previous crash left a partial write, which the temp-rename
|
|
66
|
-
// pattern prevents.
|
|
67
|
-
const tempPath = `${filePath}.${process.pid}.tmp`;
|
|
68
|
-
await fs.writeFile(tempPath, bytes, { mode: 0o600 });
|
|
69
|
-
await fs.rename(tempPath, filePath);
|
|
70
|
-
this.written.add(sha256);
|
|
71
|
-
});
|
|
72
|
-
this.chain = task.catch(() => undefined);
|
|
73
|
-
await task;
|
|
74
|
-
return {
|
|
75
|
-
$artifact: {
|
|
76
|
-
path: relativePath,
|
|
77
|
-
mediaType: "text/plain",
|
|
78
|
-
bytes: bytes.byteLength,
|
|
79
|
-
sha256,
|
|
80
|
-
},
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Encode a persisted value: externalize large string leaves and escape
|
|
86
|
-
* single-key `$artifact`/`$escaped` objects so the sentinel stays
|
|
87
|
-
* unambiguous. Returns the original value when nothing changed.
|
|
88
|
-
*/
|
|
89
|
-
export async function encodeValue(value, writer, thresholdBytes = ARTIFACT_THRESHOLD_BYTES) {
|
|
90
|
-
if (typeof value === "string") {
|
|
91
|
-
if (Buffer.byteLength(value, "utf8") <= thresholdBytes) {
|
|
92
|
-
return value;
|
|
93
|
-
}
|
|
94
|
-
return await writer.externalize(value);
|
|
95
|
-
}
|
|
96
|
-
if (Array.isArray(value)) {
|
|
97
|
-
const encoded = await Promise.all(value.map((item) => encodeValue(item, writer, thresholdBytes)));
|
|
98
|
-
return encoded.some((item, index) => item !== value[index]) ? encoded : value;
|
|
99
|
-
}
|
|
100
|
-
if (typeof value === "object" && value !== null) {
|
|
101
|
-
const record = value;
|
|
102
|
-
const entries = await Promise.all(Object.entries(record).map(async ([key, item]) => [key, await encodeValue(item, writer, thresholdBytes)]));
|
|
103
|
-
const changed = entries.some(([key, item]) => item !== record[key]);
|
|
104
|
-
const encoded = changed ? Object.fromEntries(entries) : record;
|
|
105
|
-
return needsEscape(record) ? { [ESCAPED_KEY]: encoded } : encoded;
|
|
106
|
-
}
|
|
107
|
-
return value;
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Walk a persisted value, replacing every `$artifact` sentinel via `resolve`
|
|
111
|
-
* and unwrapping `$escaped` objects. `resolve` may return the artifact
|
|
112
|
-
* contents or a placeholder; it receives the reference untouched.
|
|
113
|
-
*/
|
|
114
|
-
export function decodeValueWith(value, resolve) {
|
|
115
|
-
if (isArtifactValue(value)) {
|
|
116
|
-
return resolve(value.$artifact);
|
|
117
|
-
}
|
|
118
|
-
if (isEscapedValue(value)) {
|
|
119
|
-
// The unwrapped object is user data: process its children but do not
|
|
120
|
-
// re-test the object itself as a sentinel.
|
|
121
|
-
return decodeChildren(value.$escaped, resolve);
|
|
122
|
-
}
|
|
123
|
-
if (Array.isArray(value)) {
|
|
124
|
-
return value.map((item) => decodeValueWith(item, resolve));
|
|
125
|
-
}
|
|
126
|
-
if (typeof value === "object" && value !== null) {
|
|
127
|
-
return decodeChildren(value, resolve);
|
|
128
|
-
}
|
|
129
|
-
return value;
|
|
130
|
-
}
|
|
131
|
-
function decodeChildren(record, resolve) {
|
|
132
|
-
return Object.fromEntries(Object.entries(record).map(([key, item]) => [key, decodeValueWith(item, resolve)]));
|
|
133
|
-
}
|
|
134
|
-
/** Resolve every artifact reference in `value` by reading the bundle files. */
|
|
135
|
-
export async function resolveArtifacts(value, runDir) {
|
|
136
|
-
const refs = [];
|
|
137
|
-
decodeValueWith(value, (ref) => {
|
|
138
|
-
refs.push(ref);
|
|
139
|
-
return null;
|
|
140
|
-
});
|
|
141
|
-
const contents = new Map();
|
|
142
|
-
for (const ref of refs) {
|
|
143
|
-
if (contents.has(ref.path)) {
|
|
144
|
-
continue;
|
|
145
|
-
}
|
|
146
|
-
const resolved = path.resolve(runDir, ref.path);
|
|
147
|
-
// A reference must never escape the bundle directory.
|
|
148
|
-
if (!resolved.startsWith(path.resolve(runDir) + path.sep)) {
|
|
149
|
-
throw new Error(`Artifact path escapes the bundle: ${ref.path}`);
|
|
150
|
-
}
|
|
151
|
-
contents.set(ref.path, await fs.readFile(resolved, "utf8"));
|
|
152
|
-
}
|
|
153
|
-
return decodeValueWith(value, (ref) => contents.get(ref.path) ?? null);
|
|
154
|
-
}
|
|
155
|
-
//# sourceMappingURL=artifacts.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"artifacts.js","sourceRoot":"","sources":["../../src/workflows/artifacts.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import type { BuiltinWorkflowCatalog } from "./catalog.js";
|
|
2
|
-
import { WorkflowRunStore } from "./store.js";
|
|
3
|
-
export type LegacySourceMigrationQueue = {
|
|
4
|
-
repairCanonicalWorkflowSourceRun(options: {
|
|
5
|
-
runId: string;
|
|
6
|
-
workflowName: string;
|
|
7
|
-
workflowSourceRef: string;
|
|
8
|
-
runnerId: string;
|
|
9
|
-
claimToken: string;
|
|
10
|
-
leaseMs: number;
|
|
11
|
-
}): "unchanged" | "claimed" | false;
|
|
12
|
-
claimLegacyWorkflowSourceRun(options: {
|
|
13
|
-
runId: string;
|
|
14
|
-
workflowName: string;
|
|
15
|
-
oldWorkflowPath: string;
|
|
16
|
-
workflowSourceRef: string;
|
|
17
|
-
runnerId: string;
|
|
18
|
-
claimToken: string;
|
|
19
|
-
leaseMs: number;
|
|
20
|
-
}): boolean;
|
|
21
|
-
parkWorkflowRun(options: {
|
|
22
|
-
runId: string;
|
|
23
|
-
claimToken: string;
|
|
24
|
-
}): boolean;
|
|
25
|
-
getWorkflowRun(runId: string): {
|
|
26
|
-
status: "queued" | "starting" | "running" | "parked" | "done" | "failed" | "cancelled";
|
|
27
|
-
} | undefined;
|
|
28
|
-
setWorkflowRunOriginSession?(runId: string, originSessionId: string): boolean;
|
|
29
|
-
};
|
|
30
|
-
export type LegacySourceMigrationResult = {
|
|
31
|
-
migratedRunIds: string[];
|
|
32
|
-
blocked: {
|
|
33
|
-
runId: string;
|
|
34
|
-
reason: string;
|
|
35
|
-
}[];
|
|
36
|
-
};
|
|
37
|
-
/** One bounded migration for nonterminal runs created before canonical sources. */
|
|
38
|
-
export declare function migrateLegacyWorkflowSources(options: {
|
|
39
|
-
catalog: BuiltinWorkflowCatalog;
|
|
40
|
-
store?: WorkflowRunStore;
|
|
41
|
-
queue?: LegacySourceMigrationQueue;
|
|
42
|
-
}): Promise<LegacySourceMigrationResult>;
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
|
-
import fs from "node:fs/promises";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { WorkflowRunStore, listRunBundles } from "./store.js";
|
|
5
|
-
const MIGRATION_LEASE_MS = 30_000;
|
|
6
|
-
/** One bounded migration for nonterminal runs created before canonical sources. */
|
|
7
|
-
export async function migrateLegacyWorkflowSources(options) {
|
|
8
|
-
const store = options.store ?? new WorkflowRunStore();
|
|
9
|
-
const result = { migratedRunIds: [], blocked: [] };
|
|
10
|
-
for (const bundle of await listRunBundles(store.outputRoot)) {
|
|
11
|
-
const state = bundle.state;
|
|
12
|
-
if (state.status !== "running" && state.status !== "waiting")
|
|
13
|
-
continue;
|
|
14
|
-
if (options.queue?.setWorkflowRunOriginSession !== undefined &&
|
|
15
|
-
bundle.sessionBinding !== null) {
|
|
16
|
-
options.queue.setWorkflowRunOriginSession(state.runId, bundle.sessionBinding.piSessionId);
|
|
17
|
-
}
|
|
18
|
-
if (state.workflowSource !== undefined) {
|
|
19
|
-
if (options.queue === undefined)
|
|
20
|
-
continue;
|
|
21
|
-
const claimToken = randomUUID();
|
|
22
|
-
const repaired = options.queue.repairCanonicalWorkflowSourceRun({
|
|
23
|
-
runId: state.runId,
|
|
24
|
-
workflowName: state.workflowName,
|
|
25
|
-
workflowSourceRef: sourceRef(state.workflowSource),
|
|
26
|
-
runnerId: `source-migration-${process.pid}`,
|
|
27
|
-
claimToken,
|
|
28
|
-
leaseMs: MIGRATION_LEASE_MS,
|
|
29
|
-
});
|
|
30
|
-
if (repaired === false) {
|
|
31
|
-
// Another runner can own a canonical run during startup. Its source is
|
|
32
|
-
// already safe; the normal queue lease will make it claimable later.
|
|
33
|
-
}
|
|
34
|
-
else if (repaired === "claimed" &&
|
|
35
|
-
!options.queue.parkWorkflowRun({ runId: state.runId, claimToken })) {
|
|
36
|
-
result.blocked.push({
|
|
37
|
-
runId: state.runId,
|
|
38
|
-
reason: "canonical queue repair could not release its claim",
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
if (state.workflowPath === undefined || state.workflowHash === undefined) {
|
|
44
|
-
result.blocked.push({ runId: state.runId, reason: "legacy workflow identity is incomplete" });
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
const legacyPath = {
|
|
48
|
-
workflowName: state.workflowName,
|
|
49
|
-
workflowPath: state.workflowPath,
|
|
50
|
-
};
|
|
51
|
-
const pathEntry = options.catalog.legacyPathEntry(legacyPath);
|
|
52
|
-
let workflowSource;
|
|
53
|
-
let workflowSourceRef;
|
|
54
|
-
if (pathEntry === undefined) {
|
|
55
|
-
workflowSource = {
|
|
56
|
-
kind: "file",
|
|
57
|
-
path: path.resolve(state.workflowPath),
|
|
58
|
-
hash: state.workflowHash,
|
|
59
|
-
};
|
|
60
|
-
workflowSourceRef = workflowSource.path;
|
|
61
|
-
}
|
|
62
|
-
else {
|
|
63
|
-
const legacy = options.catalog.matchLegacy({
|
|
64
|
-
...legacyPath,
|
|
65
|
-
workflowHash: state.workflowHash,
|
|
66
|
-
});
|
|
67
|
-
if (legacy === undefined) {
|
|
68
|
-
const reason = `legacy built-in ${pathEntry.id} has an unknown source revision`;
|
|
69
|
-
result.blocked.push({ runId: state.runId, reason });
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
workflowSource = {
|
|
73
|
-
kind: "builtin",
|
|
74
|
-
id: legacy.entry.id,
|
|
75
|
-
revision: legacy.revision,
|
|
76
|
-
};
|
|
77
|
-
workflowSourceRef = legacy.entry.ref;
|
|
78
|
-
}
|
|
79
|
-
const claimToken = randomUUID();
|
|
80
|
-
const queueIsDone = options.queue?.getWorkflowRun(state.runId)?.status === "done";
|
|
81
|
-
if (options.queue !== undefined &&
|
|
82
|
-
!queueIsDone &&
|
|
83
|
-
!options.queue.claimLegacyWorkflowSourceRun({
|
|
84
|
-
runId: state.runId,
|
|
85
|
-
workflowName: state.workflowName,
|
|
86
|
-
oldWorkflowPath: state.workflowPath,
|
|
87
|
-
workflowSourceRef,
|
|
88
|
-
runnerId: `source-migration-${process.pid}`,
|
|
89
|
-
claimToken,
|
|
90
|
-
leaseMs: MIGRATION_LEASE_MS,
|
|
91
|
-
})) {
|
|
92
|
-
result.blocked.push({
|
|
93
|
-
runId: state.runId,
|
|
94
|
-
reason: "matching queue row is active or unavailable",
|
|
95
|
-
});
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
const migrated = {
|
|
99
|
-
...state,
|
|
100
|
-
workflowSource,
|
|
101
|
-
};
|
|
102
|
-
delete migrated.workflowPath;
|
|
103
|
-
delete migrated.workflowHash;
|
|
104
|
-
const migratedManifest = {
|
|
105
|
-
...bundle.manifest,
|
|
106
|
-
workflowSource: migrated.workflowSource,
|
|
107
|
-
};
|
|
108
|
-
delete migratedManifest.workflowPath;
|
|
109
|
-
// State is the migration commit point. Queue and manifest updates are
|
|
110
|
-
// idempotent, so a crash before this write can safely retry.
|
|
111
|
-
await writeJsonAtomic(path.join(bundle.runDir, "manifest.json"), migratedManifest);
|
|
112
|
-
await writeJsonAtomic(path.join(bundle.runDir, "state.json"), migrated);
|
|
113
|
-
if (options.queue !== undefined &&
|
|
114
|
-
!queueIsDone &&
|
|
115
|
-
!options.queue.parkWorkflowRun({ runId: state.runId, claimToken })) {
|
|
116
|
-
result.blocked.push({
|
|
117
|
-
runId: state.runId,
|
|
118
|
-
reason: "migration completed but its queue claim could not be released",
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
result.migratedRunIds.push(state.runId);
|
|
122
|
-
}
|
|
123
|
-
return result;
|
|
124
|
-
}
|
|
125
|
-
function sourceRef(source) {
|
|
126
|
-
return source.kind === "builtin" ? `builtin:${source.id}` : source.path;
|
|
127
|
-
}
|
|
128
|
-
async function writeJsonAtomic(filePath, value) {
|
|
129
|
-
const tempPath = `${filePath}.${process.pid}.${randomUUID()}.source-migration.tmp`;
|
|
130
|
-
await fs.writeFile(tempPath, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
|
|
131
|
-
await fs.rename(tempPath, filePath);
|
|
132
|
-
}
|
|
133
|
-
//# sourceMappingURL=migrate-sources.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"migrate-sources.js","sourceRoot":"","sources":["../../src/workflows/migrate-sources.ts"],"names":[],"mappings":"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"}
|