@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.
Files changed (209) hide show
  1. package/README.md +52 -28
  2. package/dist/builtins/autodoc.workflow.d.ts +4 -4
  3. package/dist/builtins/autoimplement.workflow.d.ts +369 -73
  4. package/dist/builtins/autoplan.workflow.d.ts +126 -13
  5. package/dist/builtins/autoplan.workflow.js +200 -29
  6. package/dist/builtins/autoplan.workflow.js.map +1 -1
  7. package/dist/builtins/catalog.js +6 -4
  8. package/dist/builtins/catalog.js.map +1 -1
  9. package/dist/builtins/index.d.ts +2 -1
  10. package/dist/builtins/index.js +1 -0
  11. package/dist/builtins/index.js.map +1 -1
  12. package/dist/builtins/monitor.workflow.d.ts +56 -21
  13. package/dist/builtins/monitor.workflow.js +497 -216
  14. package/dist/builtins/monitor.workflow.js.map +1 -1
  15. package/dist/builtins/plain-summary.workflow.d.ts +32 -0
  16. package/dist/builtins/plain-summary.workflow.js +134 -0
  17. package/dist/builtins/plain-summary.workflow.js.map +1 -0
  18. package/dist/builtins/plan-change.workflow.d.ts +170 -22
  19. package/dist/builtins/sanity-check.workflow.d.ts +45 -3
  20. package/dist/builtins/sanity-check.workflow.js +45 -7
  21. package/dist/builtins/sanity-check.workflow.js.map +1 -1
  22. package/dist/controllers/effects.d.ts +3 -2
  23. package/dist/controllers/effects.js +8 -1
  24. package/dist/controllers/effects.js.map +1 -1
  25. package/dist/controllers/index.d.ts +1 -1
  26. package/dist/controllers/index.js +1 -1
  27. package/dist/controllers/index.js.map +1 -1
  28. package/dist/controllers/manager.d.ts +1 -0
  29. package/dist/controllers/manager.js +41 -17
  30. package/dist/controllers/manager.js.map +1 -1
  31. package/dist/controllers/sqlite.d.ts +107 -85
  32. package/dist/controllers/sqlite.js +1611 -1486
  33. package/dist/controllers/sqlite.js.map +1 -1
  34. package/dist/controllers/store.d.ts +9 -8
  35. package/dist/controllers/store.js +1 -31
  36. package/dist/controllers/store.js.map +1 -1
  37. package/dist/controllers/types.d.ts +3 -0
  38. package/dist/controllers/workflow-engine-scheduler.d.ts +1 -1
  39. package/dist/controllers/workflow-engine-scheduler.js +3 -5
  40. package/dist/controllers/workflow-engine-scheduler.js.map +1 -1
  41. package/dist/controllers/workflows.d.ts +2 -2
  42. package/dist/controllers/workflows.js +11 -10
  43. package/dist/controllers/workflows.js.map +1 -1
  44. package/dist/extension/controller-host.js +2 -2
  45. package/dist/extension/controller-host.js.map +1 -1
  46. package/dist/extension/decision-channels.d.ts +2 -0
  47. package/dist/extension/decision-channels.js +311 -118
  48. package/dist/extension/decision-channels.js.map +1 -1
  49. package/dist/extension/executor.d.ts +9 -2
  50. package/dist/extension/executor.js +89 -2
  51. package/dist/extension/executor.js.map +1 -1
  52. package/dist/extension/herdr-viewer.d.ts +0 -1
  53. package/dist/extension/herdr-viewer.js +0 -4
  54. package/dist/extension/herdr-viewer.js.map +1 -1
  55. package/dist/extension/index.js +156 -103
  56. package/dist/extension/index.js.map +1 -1
  57. package/dist/extension/recorder.d.ts +3 -10
  58. package/dist/extension/recorder.js +13 -22
  59. package/dist/extension/recorder.js.map +1 -1
  60. package/dist/extension/step-message.d.ts +7 -2
  61. package/dist/extension/step-message.js +94 -2
  62. package/dist/extension/step-message.js.map +1 -1
  63. package/dist/extension/widget.js +6 -0
  64. package/dist/extension/widget.js.map +1 -1
  65. package/dist/host/rpc-executor.d.ts +3 -0
  66. package/dist/host/rpc-executor.js +2 -0
  67. package/dist/host/rpc-executor.js.map +1 -1
  68. package/dist/host/runner.d.ts +3 -6
  69. package/dist/host/runner.js +83 -32
  70. package/dist/host/runner.js.map +1 -1
  71. package/dist/render/graph-render.js +14 -8
  72. package/dist/render/graph-render.js.map +1 -1
  73. package/dist/state/database.d.ts +35 -0
  74. package/dist/state/database.js +287 -0
  75. package/dist/state/database.js.map +1 -0
  76. package/dist/state/index.d.ts +4 -0
  77. package/dist/state/index.js +5 -0
  78. package/dist/state/index.js.map +1 -0
  79. package/dist/state/json.d.ts +6 -0
  80. package/dist/state/json.js +38 -0
  81. package/dist/state/json.js.map +1 -0
  82. package/dist/state/mutation.d.ts +57 -0
  83. package/dist/state/mutation.js +255 -0
  84. package/dist/state/mutation.js.map +1 -0
  85. package/dist/state/schema.d.ts +6 -0
  86. package/dist/state/schema.js +473 -0
  87. package/dist/state/schema.js.map +1 -0
  88. package/dist/viewer/cli.d.ts +2 -2
  89. package/dist/viewer/cli.js +142 -122
  90. package/dist/viewer/cli.js.map +1 -1
  91. package/dist/viewer/render.d.ts +4 -4
  92. package/dist/viewer/render.js +2 -17
  93. package/dist/viewer/render.js.map +1 -1
  94. package/dist/viewer/session-reducer.d.ts +1 -1
  95. package/dist/viewer/session-reducer.js +1 -1
  96. package/dist/viewer/tui.d.ts +3 -3
  97. package/dist/viewer/tui.js +15 -13
  98. package/dist/viewer/tui.js.map +1 -1
  99. package/dist/viewer/watch.d.ts +2 -5
  100. package/dist/viewer/watch.js +13 -13
  101. package/dist/viewer/watch.js.map +1 -1
  102. package/dist/workflows/composition.js +12 -2
  103. package/dist/workflows/composition.js.map +1 -1
  104. package/dist/workflows/definition.d.ts +6 -2
  105. package/dist/workflows/definition.js +17 -0
  106. package/dist/workflows/definition.js.map +1 -1
  107. package/dist/workflows/engine.d.ts +6 -6
  108. package/dist/workflows/engine.js +183 -103
  109. package/dist/workflows/engine.js.map +1 -1
  110. package/dist/workflows/errors.d.ts +3 -3
  111. package/dist/workflows/errors.js +3 -3
  112. package/dist/workflows/human-decision.d.ts +29 -4
  113. package/dist/workflows/human-decision.js +547 -283
  114. package/dist/workflows/human-decision.js.map +1 -1
  115. package/dist/workflows/index.d.ts +4 -5
  116. package/dist/workflows/index.js +3 -4
  117. package/dist/workflows/index.js.map +1 -1
  118. package/dist/workflows/schema.js +17 -1
  119. package/dist/workflows/schema.js.map +1 -1
  120. package/dist/workflows/store.d.ts +83 -103
  121. package/dist/workflows/store.js +902 -951
  122. package/dist/workflows/store.js.map +1 -1
  123. package/dist/workflows/types.d.ts +55 -81
  124. package/docs/CONTROLLERS.md +10 -8
  125. package/docs/DEFERRED_TURNS.md +2 -2
  126. package/docs/DESIGN_PHILOSOPHY.md +4 -2
  127. package/docs/HUMAN_DECISIONS.md +13 -14
  128. package/docs/MONITOR.md +179 -231
  129. package/docs/SQLITE_STATE.md +219 -0
  130. package/docs/WORKFLOW_COMPOSITION.md +19 -19
  131. package/docs/WORKFLOW_STEP_MESSAGES.md +20 -18
  132. package/docs/WORKFLOW_UPDATES.md +10 -9
  133. package/docs/development.md +9 -9
  134. package/docs/live-replay-protocol.md +28 -37
  135. package/docs/plans/2026-08-16-workflow-updates-plan.md +1 -1
  136. package/docs/plans/2026-08-19-human-decision-gates-plan.md +1 -1
  137. package/docs/plans/2026-08-21-autoimplement-timeout-fallback-plan.md +1 -1
  138. package/docs/plans/2026-08-21-plan-change-approval-policy-plan.md +1 -1
  139. package/docs/plans/2026-08-21-sanity-check-plan.md +30 -16
  140. package/docs/plans/2026-08-22-goal-finishing-monitor-plan.md +200 -0
  141. package/docs/plans/2026-08-23-assistant-agent-completion-plan.md +701 -0
  142. package/docs/plans/2026-08-23-sqlite-state-plan.md +970 -0
  143. package/docs/plans/replayable-run-bundles-implementation-plan.md +2 -2
  144. package/docs/plans/session-event-replay-implementation-plan.md +1 -1
  145. package/docs/plans/tui-viewer-implementation-plan.md +1 -1
  146. package/docs/session-event-journal.md +63 -434
  147. package/docs/tui-viewer.md +13 -15
  148. package/docs/workflows.md +104 -50
  149. package/examples/workflows/plain-summary.workflow.ts +1 -0
  150. package/herdr-plugin.toml +1 -1
  151. package/package.json +1 -1
  152. package/plugins/herdr/viewer.mjs +1 -11
  153. package/skills/autoplan/SKILL.md +5 -4
  154. package/skills/monitor/SKILL.md +91 -163
  155. package/skills/pi-workflows/SKILL.md +7 -7
  156. package/skills/sanity-check/SKILL.md +1 -1
  157. package/src/builtins/autoplan.workflow.ts +290 -40
  158. package/src/builtins/catalog.ts +6 -4
  159. package/src/builtins/index.ts +7 -1
  160. package/src/builtins/monitor.workflow.ts +663 -249
  161. package/src/builtins/plain-summary.workflow.ts +185 -0
  162. package/src/builtins/sanity-check.workflow.ts +62 -7
  163. package/src/controllers/effects.ts +7 -0
  164. package/src/controllers/index.ts +0 -6
  165. package/src/controllers/manager.ts +72 -32
  166. package/src/controllers/sqlite.ts +2420 -1948
  167. package/src/controllers/store.ts +17 -38
  168. package/src/controllers/types.ts +3 -0
  169. package/src/controllers/workflow-engine-scheduler.ts +4 -8
  170. package/src/controllers/workflows.ts +45 -21
  171. package/src/extension/controller-host.ts +1 -2
  172. package/src/extension/decision-channels.ts +427 -144
  173. package/src/extension/executor.ts +105 -2
  174. package/src/extension/herdr-viewer.ts +0 -5
  175. package/src/extension/index.ts +180 -118
  176. package/src/extension/recorder.ts +13 -22
  177. package/src/extension/step-message.ts +106 -4
  178. package/src/extension/widget.ts +8 -0
  179. package/src/host/rpc-executor.ts +4 -0
  180. package/src/host/runner.ts +93 -41
  181. package/src/render/graph-render.ts +11 -4
  182. package/src/state/database.ts +375 -0
  183. package/src/state/index.ts +29 -0
  184. package/src/state/json.ts +42 -0
  185. package/src/state/mutation.ts +435 -0
  186. package/src/state/schema.ts +475 -0
  187. package/src/viewer/cli.ts +151 -144
  188. package/src/viewer/render.ts +12 -24
  189. package/src/viewer/session-reducer.ts +1 -1
  190. package/src/viewer/tui.ts +18 -17
  191. package/src/viewer/watch.ts +13 -17
  192. package/src/workflows/composition.ts +12 -3
  193. package/src/workflows/definition.ts +36 -3
  194. package/src/workflows/engine.ts +212 -116
  195. package/src/workflows/errors.ts +3 -3
  196. package/src/workflows/human-decision.ts +795 -340
  197. package/src/workflows/index.ts +15 -19
  198. package/src/workflows/schema.ts +19 -1
  199. package/src/workflows/store.ts +1295 -1163
  200. package/src/workflows/types.ts +61 -81
  201. package/dist/workflows/artifacts.d.ts +0 -40
  202. package/dist/workflows/artifacts.js +0 -155
  203. package/dist/workflows/artifacts.js.map +0 -1
  204. package/dist/workflows/migrate-sources.d.ts +0 -42
  205. package/dist/workflows/migrate-sources.js +0 -133
  206. package/dist/workflows/migrate-sources.js.map +0 -1
  207. package/docs/run-bundles.md +0 -481
  208. package/src/workflows/artifacts.ts +0 -188
  209. package/src/workflows/migrate-sources.ts +0 -178
@@ -1,3 +1,5 @@
1
+ import { createHash } from "node:crypto";
2
+ import type { AssistantMessage } from "@earendil-works/pi-ai";
1
3
  import type {
2
4
  AgentStepExecutor,
3
5
  AgentStepRequest,
@@ -27,6 +29,7 @@ export type PromptDelivery = {
27
29
  */
28
30
  export type ConversationHooks = {
29
31
  beginAttempt?: (contract: AgentStepRequest["contract"]) => void;
32
+ recoverAssistant?: (contract: AgentStepRequest["contract"]) => AgentStepSubmission | undefined;
30
33
  mark: () => number;
31
34
  rangeSince: (mark: number) => ConversationRange | undefined;
32
35
  };
@@ -49,6 +52,8 @@ type PendingStep = {
49
52
  nudgesSent: number;
50
53
  /** Conversation mark taken when the prompt was first delivered. */
51
54
  mark: number | null;
55
+ /** Latest finalized assistant message observed for this attempt. */
56
+ assistantMessage?: AssistantMessage;
52
57
  cleanup: () => void;
53
58
  /** Resolves when this step stops being the pending step. */
54
59
  cleared: Promise<void>;
@@ -65,6 +70,7 @@ const DEFAULT_MAX_NUDGES = 2;
65
70
  * bounded number of times before failing the step.
66
71
  */
67
72
  export class ConversationStepExecutor implements AgentStepExecutor {
73
+ readonly assistantMessageMode = "visible" as const;
68
74
  private readonly sendPrompt: (delivery: PromptDelivery) => void;
69
75
  private readonly maxNudges: number;
70
76
  private readonly conversation: ConversationHooks | undefined;
@@ -89,6 +95,10 @@ export class ConversationStepExecutor implements AgentStepExecutor {
89
95
  return this.pending?.request.contract.nodeId ?? null;
90
96
  }
91
97
 
98
+ get pendingCompletion(): "submit" | "assistant" | null {
99
+ return this.pending === null ? null : completionKind(this.pending.request);
100
+ }
101
+
92
102
  /**
93
103
  * Hold the pending step for the user: no nudges are sent while held, so an
94
104
  * escape-interrupted conversation stays quiet until the user resumes.
@@ -127,6 +137,12 @@ export class ConversationStepExecutor implements AgentStepExecutor {
127
137
  if (this.pending) {
128
138
  throw new Error("Another workflow step is already awaiting output");
129
139
  }
140
+ if (completionKind(request) === "assistant") {
141
+ const recovered = this.conversation?.recoverAssistant?.(request.contract);
142
+ if (recovered !== undefined) {
143
+ return recovered;
144
+ }
145
+ }
130
146
  return await new Promise<AgentStepSubmission>((resolve, reject) => {
131
147
  const onAbort = () => {
132
148
  const reason: unknown = signal.reason ?? new Error("Workflow step aborted");
@@ -199,6 +215,13 @@ export class ConversationStepExecutor implements AgentStepExecutor {
199
215
  )}; the pending attempt is ${JSON.stringify(expectedAttempt)}. Use the attempt id from the latest step contract.`,
200
216
  };
201
217
  }
218
+ if (completionKind(pending.request) === "assistant") {
219
+ return {
220
+ accepted: false,
221
+ message:
222
+ "This step completes with a normal assistant response. Do not submit workflow output.",
223
+ };
224
+ }
202
225
  // Race validation against the step being cleared: a hung `validate`
203
226
  // callback must not leave this tool call (and therefore pi) blocked after
204
227
  // a timeout or cancel already resolved the run.
@@ -237,10 +260,22 @@ export class ConversationStepExecutor implements AgentStepExecutor {
237
260
  };
238
261
  }
239
262
 
263
+ /** Keep the latest finalized assistant message for an assistant-output step. */
264
+ handleMessageEnd(message: unknown): void {
265
+ const pending = this.pending;
266
+ if (pending === null || completionKind(pending.request) !== "assistant") {
267
+ return;
268
+ }
269
+ const assistant = assistantMessageLike(message);
270
+ if (assistant !== undefined) {
271
+ pending.assistantMessage = assistant as AssistantMessage;
272
+ }
273
+ }
274
+
240
275
  /**
241
276
  * Called when the agent settles. Returns true when a nudge was sent, false
242
- * when there was nothing to do. Fails the pending step once the nudge
243
- * budget is exhausted.
277
+ * when there was nothing to do. Submitted steps use bounded nudges;
278
+ * assistant-output steps accept or fail the one visible response.
244
279
  */
245
280
  handleAgentSettled(): boolean {
246
281
  const pending = this.pending;
@@ -252,6 +287,33 @@ export class ConversationStepExecutor implements AgentStepExecutor {
252
287
  // steal the conversation back. The step waits for an explicit resume.
253
288
  return false;
254
289
  }
290
+ if (completionKind(pending.request) === "assistant") {
291
+ try {
292
+ const output = visibleAssistantText(
293
+ pending.assistantMessage,
294
+ pending.request.contract.maxOutputChars,
295
+ );
296
+ const conversation =
297
+ pending.mark !== null ? this.conversation?.rangeSince(pending.mark) : undefined;
298
+ const assistantMessage = {
299
+ sha256: createHash("sha256").update(output).digest("hex"),
300
+ ...(conversation?.lastEntryId !== undefined ? { entryId: conversation.lastEntryId } : {}),
301
+ ...(pending.request.contract.maxOutputChars !== undefined
302
+ ? { maxChars: pending.request.contract.maxOutputChars }
303
+ : {}),
304
+ };
305
+ this.clearPending();
306
+ pending.resolve({
307
+ output,
308
+ assistantMessage,
309
+ ...(conversation !== undefined ? { conversation } : {}),
310
+ });
311
+ } catch (error) {
312
+ this.clearPending();
313
+ pending.reject(error);
314
+ }
315
+ return false;
316
+ }
255
317
  if (pending.nudgesSent >= this.maxNudges) {
256
318
  this.clearPending();
257
319
  pending.reject(
@@ -309,3 +371,44 @@ export class ConversationStepExecutor implements AgentStepExecutor {
309
371
  this.pending = null;
310
372
  }
311
373
  }
374
+
375
+ function completionKind(request: AgentStepRequest): "submit" | "assistant" {
376
+ return request.contract.completion;
377
+ }
378
+
379
+ function assistantMessageLike(
380
+ message: unknown,
381
+ ): Pick<AssistantMessage, "role" | "content" | "stopReason" | "errorMessage"> | undefined {
382
+ if (message === null || typeof message !== "object") return undefined;
383
+ const candidate = message as Partial<AssistantMessage>;
384
+ if (candidate.role !== "assistant" || !Array.isArray(candidate.content)) return undefined;
385
+ if (typeof candidate.stopReason !== "string") return undefined;
386
+ return candidate as Pick<AssistantMessage, "role" | "content" | "stopReason" | "errorMessage">;
387
+ }
388
+
389
+ /** Extract the exact visible text blocks from one finalized assistant message. */
390
+ export function visibleAssistantText(message: unknown, maxChars?: number): string {
391
+ const assistant = assistantMessageLike(message);
392
+ if (assistant === undefined) {
393
+ throw new Error("Assistant step settled without a final assistant message");
394
+ }
395
+ if (assistant.stopReason !== "stop" && assistant.stopReason !== "length") {
396
+ throw new Error(
397
+ assistant.errorMessage?.trim() ||
398
+ `Assistant step stopped with ${JSON.stringify(assistant.stopReason)} before a final response`,
399
+ );
400
+ }
401
+ const text = assistant.content
402
+ .filter((part) => part.type === "text")
403
+ .map((part) => part.text)
404
+ .join("\n");
405
+ if (text.trim().length === 0) {
406
+ throw new Error("Assistant step returned no visible text");
407
+ }
408
+ if (maxChars !== undefined && text.length > maxChars) {
409
+ throw new Error(
410
+ `Assistant response has ${text.length} characters, above the configured limit of ${maxChars}`,
411
+ );
412
+ }
413
+ return text;
414
+ }
@@ -14,7 +14,6 @@ export type ViewerPlacement = (typeof VIEWER_PLACEMENTS)[number];
14
14
  export type WorkflowViewTarget = {
15
15
  runId: string;
16
16
  workflowName: string;
17
- runDir: string;
18
17
  };
19
18
 
20
19
  export type HerdrCapability = { available: true } | { available: false; reason: string };
@@ -178,8 +177,6 @@ export class HerdrWorkflowViewer {
178
177
  HERDR_PLUGIN_ENTRYPOINT,
179
178
  "--env",
180
179
  `PI_WORKFLOWS_RUN_ID=${target.runId}`,
181
- "--env",
182
- `PI_WORKFLOWS_RUN_DIR=${target.runDir}`,
183
180
  "--focus",
184
181
  ];
185
182
  if (placement === "tab") {
@@ -229,8 +226,6 @@ export class HerdrWorkflowViewer {
229
226
  created.workspaceId,
230
227
  "--env",
231
228
  `PI_WORKFLOWS_RUN_ID=${target.runId}`,
232
- "--env",
233
- `PI_WORKFLOWS_RUN_DIR=${target.runDir}`,
234
229
  "--focus",
235
230
  ]),
236
231
  );