@osolmaz/pi-workflows 0.1.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.
Files changed (113) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +182 -0
  3. package/dist/extension/executor.d.ts +58 -0
  4. package/dist/extension/executor.js +201 -0
  5. package/dist/extension/executor.js.map +1 -0
  6. package/dist/extension/index.d.ts +17 -0
  7. package/dist/extension/index.js +504 -0
  8. package/dist/extension/index.js.map +1 -0
  9. package/dist/extension/widget.d.ts +21 -0
  10. package/dist/extension/widget.js +142 -0
  11. package/dist/extension/widget.js.map +1 -0
  12. package/dist/render/ansi.d.ts +16 -0
  13. package/dist/render/ansi.js +42 -0
  14. package/dist/render/ansi.js.map +1 -0
  15. package/dist/render/canvas.d.ts +40 -0
  16. package/dist/render/canvas.js +177 -0
  17. package/dist/render/canvas.js.map +1 -0
  18. package/dist/render/format.d.ts +3 -0
  19. package/dist/render/format.js +17 -0
  20. package/dist/render/format.js.map +1 -0
  21. package/dist/render/graph-render.d.ts +22 -0
  22. package/dist/render/graph-render.js +520 -0
  23. package/dist/render/graph-render.js.map +1 -0
  24. package/dist/render/graph.d.ts +46 -0
  25. package/dist/render/graph.js +272 -0
  26. package/dist/render/graph.js.map +1 -0
  27. package/dist/viewer/cli.d.ts +10 -0
  28. package/dist/viewer/cli.js +132 -0
  29. package/dist/viewer/cli.js.map +1 -0
  30. package/dist/viewer/render.d.ts +19 -0
  31. package/dist/viewer/render.js +162 -0
  32. package/dist/viewer/render.js.map +1 -0
  33. package/dist/viewer/tui.d.ts +11 -0
  34. package/dist/viewer/tui.js +140 -0
  35. package/dist/viewer/tui.js.map +1 -0
  36. package/dist/viewer/watch.d.ts +9 -0
  37. package/dist/viewer/watch.js +46 -0
  38. package/dist/viewer/watch.js.map +1 -0
  39. package/dist/workflows/decision.d.ts +25 -0
  40. package/dist/workflows/decision.js +96 -0
  41. package/dist/workflows/decision.js.map +1 -0
  42. package/dist/workflows/definition.d.ts +9 -0
  43. package/dist/workflows/definition.js +61 -0
  44. package/dist/workflows/definition.js.map +1 -0
  45. package/dist/workflows/engine.d.ts +65 -0
  46. package/dist/workflows/engine.js +574 -0
  47. package/dist/workflows/engine.js.map +1 -0
  48. package/dist/workflows/errors.d.ts +9 -0
  49. package/dist/workflows/errors.js +24 -0
  50. package/dist/workflows/errors.js.map +1 -0
  51. package/dist/workflows/graph.d.ts +17 -0
  52. package/dist/workflows/graph.js +127 -0
  53. package/dist/workflows/graph.js.map +1 -0
  54. package/dist/workflows/index.d.ts +11 -0
  55. package/dist/workflows/index.js +11 -0
  56. package/dist/workflows/index.js.map +1 -0
  57. package/dist/workflows/json.d.ts +14 -0
  58. package/dist/workflows/json.js +134 -0
  59. package/dist/workflows/json.js.map +1 -0
  60. package/dist/workflows/loader.d.ts +28 -0
  61. package/dist/workflows/loader.js +94 -0
  62. package/dist/workflows/loader.js.map +1 -0
  63. package/dist/workflows/schema.d.ts +7 -0
  64. package/dist/workflows/schema.js +176 -0
  65. package/dist/workflows/schema.js.map +1 -0
  66. package/dist/workflows/shell.d.ts +9 -0
  67. package/dist/workflows/shell.js +177 -0
  68. package/dist/workflows/shell.js.map +1 -0
  69. package/dist/workflows/store.d.ts +35 -0
  70. package/dist/workflows/store.js +181 -0
  71. package/dist/workflows/store.js.map +1 -0
  72. package/dist/workflows/text.d.ts +10 -0
  73. package/dist/workflows/text.js +32 -0
  74. package/dist/workflows/text.js.map +1 -0
  75. package/dist/workflows/types.d.ts +280 -0
  76. package/dist/workflows/types.js +2 -0
  77. package/dist/workflows/types.js.map +1 -0
  78. package/docs/development.md +130 -0
  79. package/docs/run-bundles.md +114 -0
  80. package/docs/workflows.md +311 -0
  81. package/examples/workflows/autoimplement.workflow.ts +92 -0
  82. package/examples/workflows/autoresearch.workflow.ts +139 -0
  83. package/examples/workflows/branch.workflow.ts +63 -0
  84. package/examples/workflows/echo.workflow.ts +23 -0
  85. package/examples/workflows/elegant-solution.workflow.ts +95 -0
  86. package/examples/workflows/shell.workflow.ts +31 -0
  87. package/examples/workflows/two-turn.workflow.ts +64 -0
  88. package/package.json +80 -0
  89. package/src/extension/executor.ts +251 -0
  90. package/src/extension/index.ts +627 -0
  91. package/src/extension/widget.ts +183 -0
  92. package/src/render/ansi.ts +47 -0
  93. package/src/render/canvas.ts +196 -0
  94. package/src/render/format.ts +19 -0
  95. package/src/render/graph-render.ts +738 -0
  96. package/src/render/graph.ts +341 -0
  97. package/src/viewer/cli.ts +150 -0
  98. package/src/viewer/render.ts +236 -0
  99. package/src/viewer/tui.ts +159 -0
  100. package/src/viewer/watch.ts +55 -0
  101. package/src/workflows/decision.ts +127 -0
  102. package/src/workflows/definition.ts +104 -0
  103. package/src/workflows/engine.ts +793 -0
  104. package/src/workflows/errors.ts +27 -0
  105. package/src/workflows/graph.ts +161 -0
  106. package/src/workflows/index.ts +76 -0
  107. package/src/workflows/json.ts +155 -0
  108. package/src/workflows/loader.ts +123 -0
  109. package/src/workflows/schema.ts +218 -0
  110. package/src/workflows/shell.ts +199 -0
  111. package/src/workflows/store.ts +234 -0
  112. package/src/workflows/text.ts +34 -0
  113. package/src/workflows/types.ts +318 -0
@@ -0,0 +1,318 @@
1
+ export type MaybePromise<T> = T | Promise<T>;
2
+
3
+ /**
4
+ * Context passed to node callbacks (prompt builders, compute/action runners,
5
+ * validators). `outputs` maps node ids to their accepted outputs; `results`
6
+ * maps node ids to the full result record of their latest attempt.
7
+ */
8
+ export type WorkflowNodeContext<TInput = unknown> = {
9
+ input: TInput;
10
+ outputs: Record<string, unknown>;
11
+ results: Record<string, WorkflowNodeResult>;
12
+ state: WorkflowRunState;
13
+ /**
14
+ * Aborted when the node times out or the run is cancelled. Long-running
15
+ * callbacks should observe it (pass it to fetch/spawn or check
16
+ * `signal.aborted`) so side effects stop when the engine gives up on the
17
+ * node.
18
+ */
19
+ signal: AbortSignal;
20
+ };
21
+
22
+ export type WorkflowNodeCommon = {
23
+ /** Per-node timeout. Falls back to the engine default (15 minutes). */
24
+ timeoutMs?: number;
25
+ /** Short human-readable label shown in the viewer while the node runs. */
26
+ statusDetail?: string;
27
+ };
28
+
29
+ /**
30
+ * Edges route between nodes. A node has at most one outgoing edge: either a
31
+ * plain `to` edge or a `switch` edge that routes on a JSON path into the
32
+ * node's output (`$.field`, `$output.field`) or result (`$result.outcome`).
33
+ */
34
+ export type WorkflowEdge =
35
+ | {
36
+ from: string;
37
+ to: string;
38
+ }
39
+ | {
40
+ from: string;
41
+ switch: {
42
+ on: string;
43
+ cases: Record<string, string>;
44
+ };
45
+ };
46
+
47
+ /**
48
+ * A model-shaped step. The engine sends the prompt into the pi conversation
49
+ * and the model completes the step by calling the `workflow` tool with a JSON
50
+ * output. `expectedOutput` is appended to the step contract so the model
51
+ * knows what shape to submit. `validate` may reject (throw) or normalize the
52
+ * submitted output; rejections are surfaced to the model so it can retry
53
+ * within the same step.
54
+ */
55
+ export type AgentNodeDefinition = WorkflowNodeCommon & {
56
+ nodeType: "agent";
57
+ prompt: (context: WorkflowNodeContext) => MaybePromise<string>;
58
+ expectedOutput?: string;
59
+ validate?: (output: unknown, context: WorkflowNodeContext) => MaybePromise<unknown>;
60
+ };
61
+
62
+ /** A pure local function: shape inputs, route, format, derive values. */
63
+ export type ComputeNodeDefinition = WorkflowNodeCommon & {
64
+ nodeType: "compute";
65
+ run: (context: WorkflowNodeContext) => MaybePromise<unknown>;
66
+ };
67
+
68
+ /** A deterministic runtime-owned step implemented as a local function. */
69
+ export type FunctionActionNodeDefinition = WorkflowNodeCommon & {
70
+ nodeType: "action";
71
+ run: (context: WorkflowNodeContext) => MaybePromise<unknown>;
72
+ };
73
+
74
+ export type ShellActionExecution = {
75
+ command: string;
76
+ args?: string[];
77
+ cwd?: string;
78
+ env?: Record<string, string>;
79
+ stdin?: string;
80
+ shell?: boolean | string;
81
+ allowNonZeroExit?: boolean;
82
+ timeoutMs?: number;
83
+ /** Cap on captured stdout/stderr each, default 1,000,000 characters. */
84
+ maxOutputChars?: number;
85
+ };
86
+
87
+ export type ShellActionResult = {
88
+ command: string;
89
+ args: string[];
90
+ cwd: string;
91
+ stdout: string;
92
+ stderr: string;
93
+ exitCode: number | null;
94
+ signal: NodeJS.Signals | null;
95
+ durationMs: number;
96
+ };
97
+
98
+ /** A deterministic runtime-owned step implemented as a shell command. */
99
+ export type ShellActionNodeDefinition = WorkflowNodeCommon & {
100
+ nodeType: "action";
101
+ exec: (context: WorkflowNodeContext) => MaybePromise<ShellActionExecution>;
102
+ parse?: (result: ShellActionResult, context: WorkflowNodeContext) => MaybePromise<unknown>;
103
+ };
104
+
105
+ export type ActionNodeDefinition = FunctionActionNodeDefinition | ShellActionNodeDefinition;
106
+
107
+ /**
108
+ * A pause point. The run terminates with status `waiting` so a human (or an
109
+ * external trigger) can decide how to continue. The optional `run` callback
110
+ * produces the checkpoint's output before the run pauses.
111
+ */
112
+ export type CheckpointNodeDefinition = WorkflowNodeCommon & {
113
+ nodeType: "checkpoint";
114
+ summary?: string;
115
+ run?: (context: WorkflowNodeContext) => MaybePromise<unknown>;
116
+ };
117
+
118
+ export type WorkflowNodeDefinition =
119
+ | AgentNodeDefinition
120
+ | ComputeNodeDefinition
121
+ | ActionNodeDefinition
122
+ | CheckpointNodeDefinition;
123
+
124
+ export type WorkflowPresentationContext = {
125
+ /** Final persisted state of the workflow run. */
126
+ state: WorkflowRunState;
127
+ /** Convenience alias for `state.finalOutput`. */
128
+ finalOutput: unknown;
129
+ /** Aborted if a new run starts, the session closes, or prompt generation times out. */
130
+ signal: AbortSignal;
131
+ };
132
+
133
+ export type WorkflowDefinition = {
134
+ name: string;
135
+ /** Optional human-readable run title (static or derived from input). */
136
+ title?:
137
+ | string
138
+ | ((context: { input: unknown; workflowName: string }) => MaybePromise<string | undefined>);
139
+ /**
140
+ * Optional instructions for a normal assistant response after the run ends.
141
+ * The Pi extension resolves this only after the final state is persisted;
142
+ * the engine and run bundle remain presentation-agnostic.
143
+ */
144
+ presentationPrompt?:
145
+ | string
146
+ | ((context: WorkflowPresentationContext) => MaybePromise<string | undefined>);
147
+ startAt: string;
148
+ nodes: Record<string, WorkflowNodeDefinition>;
149
+ edges: WorkflowEdge[];
150
+ /** Guard against unbounded loops. Defaults to the engine's maxSteps. */
151
+ maxSteps?: number;
152
+ };
153
+
154
+ export type WorkflowNodeOutcome = "ok" | "timed_out" | "failed" | "cancelled";
155
+
156
+ export type WorkflowNodeResult = {
157
+ attemptId: string;
158
+ nodeId: string;
159
+ nodeType: WorkflowNodeDefinition["nodeType"];
160
+ outcome: WorkflowNodeOutcome;
161
+ startedAt: string;
162
+ finishedAt: string;
163
+ durationMs: number;
164
+ output?: unknown;
165
+ error?: string;
166
+ };
167
+
168
+ export type WorkflowActionReceipt = {
169
+ actionType: "shell" | "function";
170
+ command?: string;
171
+ args?: string[];
172
+ cwd?: string;
173
+ exitCode?: number | null;
174
+ signal?: NodeJS.Signals | null;
175
+ durationMs?: number;
176
+ };
177
+
178
+ export type WorkflowStepRecord = {
179
+ attemptId: string;
180
+ nodeId: string;
181
+ nodeType: WorkflowNodeDefinition["nodeType"];
182
+ outcome: WorkflowNodeOutcome;
183
+ startedAt: string;
184
+ finishedAt: string;
185
+ promptText: string | null;
186
+ output: unknown;
187
+ error?: string;
188
+ action?: WorkflowActionReceipt;
189
+ };
190
+
191
+ export type WorkflowRunStatus =
192
+ | "running"
193
+ | "waiting"
194
+ | "completed"
195
+ | "failed"
196
+ | "timed_out"
197
+ | "cancelled";
198
+
199
+ export type WorkflowRunState = {
200
+ runId: string;
201
+ workflowName: string;
202
+ runTitle?: string;
203
+ workflowPath?: string;
204
+ startedAt: string;
205
+ finishedAt?: string;
206
+ updatedAt: string;
207
+ status: WorkflowRunStatus;
208
+ input: unknown;
209
+ outputs: Record<string, unknown>;
210
+ results: Record<string, WorkflowNodeResult>;
211
+ steps: WorkflowStepRecord[];
212
+ currentNode?: string;
213
+ currentAttemptId?: string;
214
+ currentNodeType?: WorkflowNodeDefinition["nodeType"];
215
+ currentNodeStartedAt?: string;
216
+ statusDetail?: string;
217
+ /** True while the run is held at a step boundary by a pause request. */
218
+ paused?: boolean;
219
+ waitingOn?: string;
220
+ finalOutput?: unknown;
221
+ error?: string;
222
+ };
223
+
224
+ export type WorkflowNodeSnapshot = {
225
+ nodeType: WorkflowNodeDefinition["nodeType"];
226
+ timeoutMs?: number;
227
+ statusDetail?: string;
228
+ summary?: string;
229
+ expectedOutput?: string;
230
+ actionExecution?: "function" | "shell";
231
+ };
232
+
233
+ export type WorkflowDefinitionSnapshot = {
234
+ schema: "pi-workflows.definition-snapshot.v1";
235
+ name: string;
236
+ startAt: string;
237
+ nodes: Record<string, WorkflowNodeSnapshot>;
238
+ edges: WorkflowEdge[];
239
+ };
240
+
241
+ export type WorkflowTraceEvent = {
242
+ seq: number;
243
+ at: string;
244
+ scope: "run" | "node" | "agent" | "action";
245
+ type: string;
246
+ runId: string;
247
+ nodeId?: string;
248
+ attemptId?: string;
249
+ payload: Record<string, unknown>;
250
+ };
251
+
252
+ export type WorkflowTraceEventDraft = Omit<WorkflowTraceEvent, "seq" | "at" | "runId">;
253
+
254
+ export type WorkflowRunManifest = {
255
+ schema: "pi-workflows.run-bundle.v1";
256
+ runId: string;
257
+ workflowName: string;
258
+ runTitle?: string;
259
+ workflowPath?: string;
260
+ startedAt: string;
261
+ finishedAt?: string;
262
+ status: WorkflowRunStatus;
263
+ traceSchema: "pi-workflows.trace-event.v1";
264
+ paths: {
265
+ workflow: string;
266
+ state: string;
267
+ trace: string;
268
+ };
269
+ };
270
+
271
+ export type WorkflowRunResult = {
272
+ runDir: string;
273
+ state: WorkflowRunState;
274
+ };
275
+
276
+ /** The step contract handed to the executor alongside the prompt. */
277
+ export type AgentStepContract = {
278
+ runId: string;
279
+ workflowName: string;
280
+ nodeId: string;
281
+ attemptId: string;
282
+ expectedOutput?: string;
283
+ };
284
+
285
+ export type AgentStepRequest = {
286
+ contract: AgentStepContract;
287
+ prompt: string;
288
+ /**
289
+ * Validate a submission from the model. Returns the normalized output or an
290
+ * error message the executor should surface to the model for retry.
291
+ */
292
+ accept: (output: unknown) => Promise<{ ok: true; value: unknown } | { ok: false; error: string }>;
293
+ };
294
+
295
+ export type AgentStepSubmission = {
296
+ output: unknown;
297
+ };
298
+
299
+ /**
300
+ * Runs one agent step to completion. Implementations deliver the prompt to
301
+ * the model and resolve once a submission has been accepted via `accept`.
302
+ * Must reject with an `AbortError`-like error when `signal` aborts.
303
+ */
304
+ export interface AgentStepExecutor {
305
+ runAgentStep(request: AgentStepRequest, signal: AbortSignal): Promise<AgentStepSubmission>;
306
+ }
307
+
308
+ export type WorkflowEngineOptions = {
309
+ executor: AgentStepExecutor;
310
+ /** Root directory for run bundles. Defaults to `~/.pi/agent/workflows/runs`. */
311
+ outputRoot?: string;
312
+ /** Default per-node timeout. Defaults to 15 minutes. */
313
+ defaultNodeTimeoutMs?: number;
314
+ /** Guard against unbounded graph loops. Defaults to 100 executed steps. */
315
+ maxSteps?: number;
316
+ /** Observer invoked after every persisted trace event. */
317
+ onEvent?: (event: WorkflowTraceEvent, state: WorkflowRunState) => void;
318
+ };