@osolmaz/pi-workflows 0.13.4 → 0.15.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 (237) hide show
  1. package/README.md +146 -160
  2. package/dist/builtins/autoimplement.workflow.js +3 -1
  3. package/dist/builtins/autoimplement.workflow.js.map +1 -1
  4. package/dist/builtins/change-verification.workflow.js +7 -2
  5. package/dist/builtins/change-verification.workflow.js.map +1 -1
  6. package/dist/builtins/metadata.d.ts +22 -0
  7. package/dist/builtins/metadata.js +10 -0
  8. package/dist/builtins/metadata.js.map +1 -0
  9. package/dist/builtins/monitor.workflow.js +4 -1
  10. package/dist/builtins/monitor.workflow.js.map +1 -1
  11. package/dist/builtins/sanity-check.workflow.js +4 -1
  12. package/dist/builtins/sanity-check.workflow.js.map +1 -1
  13. package/dist/builtins/workspace-preparation.workflow.js +3 -1
  14. package/dist/builtins/workspace-preparation.workflow.js.map +1 -1
  15. package/dist/controllers/index.d.ts +1 -2
  16. package/dist/controllers/index.js +0 -1
  17. package/dist/controllers/index.js.map +1 -1
  18. package/dist/controllers/sqlite.d.ts +85 -31
  19. package/dist/controllers/sqlite.js +541 -126
  20. package/dist/controllers/sqlite.js.map +1 -1
  21. package/dist/controllers/store.d.ts +1 -0
  22. package/dist/extension/controller-command.d.ts +22 -0
  23. package/dist/extension/controller-command.js +32 -0
  24. package/dist/extension/controller-command.js.map +1 -0
  25. package/dist/extension/index.d.ts +2 -9
  26. package/dist/extension/index.js +876 -2928
  27. package/dist/extension/index.js.map +1 -1
  28. package/dist/extension/message-card.d.ts +13 -0
  29. package/dist/extension/message-card.js +46 -0
  30. package/dist/extension/message-card.js.map +1 -0
  31. package/dist/extension/step-message.d.ts +5 -6
  32. package/dist/extension/step-message.js +41 -52
  33. package/dist/extension/step-message.js.map +1 -1
  34. package/dist/host/child-worker-supervisor.d.ts +50 -0
  35. package/dist/host/child-worker-supervisor.js +159 -0
  36. package/dist/host/child-worker-supervisor.js.map +1 -0
  37. package/dist/host/client.d.ts +48 -0
  38. package/dist/host/client.js +216 -0
  39. package/dist/host/client.js.map +1 -0
  40. package/dist/host/controller-worker-entry.d.ts +2 -0
  41. package/dist/host/controller-worker-entry.js +244 -0
  42. package/dist/host/controller-worker-entry.js.map +1 -0
  43. package/dist/host/controller-worker-protocol.d.ts +36 -0
  44. package/dist/host/controller-worker-protocol.js +49 -0
  45. package/dist/host/controller-worker-protocol.js.map +1 -0
  46. package/dist/host/controller-worker-supervisor.d.ts +21 -0
  47. package/dist/host/controller-worker-supervisor.js +54 -0
  48. package/dist/host/controller-worker-supervisor.js.map +1 -0
  49. package/dist/host/host-entry.d.ts +2 -0
  50. package/dist/host/host-entry.js +23 -0
  51. package/dist/host/host-entry.js.map +1 -0
  52. package/dist/host/processes.d.ts +17 -12
  53. package/dist/host/processes.js +154 -50
  54. package/dist/host/processes.js.map +1 -1
  55. package/dist/host/protocol.d.ts +38 -0
  56. package/dist/host/protocol.js +156 -0
  57. package/dist/host/protocol.js.map +1 -0
  58. package/dist/host/resolver-entry.d.ts +49 -0
  59. package/dist/host/resolver-entry.js +149 -0
  60. package/dist/host/resolver-entry.js.map +1 -0
  61. package/dist/host/rpc-executor.d.ts +14 -3
  62. package/dist/host/rpc-executor.js +63 -30
  63. package/dist/host/rpc-executor.js.map +1 -1
  64. package/dist/host/runner.d.ts +95 -29
  65. package/dist/host/runner.js +2311 -348
  66. package/dist/host/runner.js.map +1 -1
  67. package/dist/host/state.d.ts +174 -0
  68. package/dist/host/state.js +689 -0
  69. package/dist/host/state.js.map +1 -0
  70. package/dist/host/worker-entry.d.ts +10 -0
  71. package/dist/host/worker-entry.js +446 -0
  72. package/dist/host/worker-entry.js.map +1 -0
  73. package/dist/host/worker-protocol.d.ts +31 -0
  74. package/dist/host/worker-protocol.js +122 -0
  75. package/dist/host/worker-protocol.js.map +1 -0
  76. package/dist/host/worker-store.d.ts +80 -0
  77. package/dist/host/worker-store.js +91 -0
  78. package/dist/host/worker-store.js.map +1 -0
  79. package/dist/host/worker-supervisor.d.ts +22 -0
  80. package/dist/host/worker-supervisor.js +54 -0
  81. package/dist/host/worker-supervisor.js.map +1 -0
  82. package/dist/render/canvas.d.ts +2 -1
  83. package/dist/render/canvas.js +40 -15
  84. package/dist/render/canvas.js.map +1 -1
  85. package/dist/render/graph-render.d.ts +7 -3
  86. package/dist/render/graph-render.js +125 -74
  87. package/dist/render/graph-render.js.map +1 -1
  88. package/dist/state/database.js +3 -1
  89. package/dist/state/database.js.map +1 -1
  90. package/dist/state/index.d.ts +1 -0
  91. package/dist/state/index.js +1 -0
  92. package/dist/state/index.js.map +1 -1
  93. package/dist/state/prune.js +36 -10
  94. package/dist/state/prune.js.map +1 -1
  95. package/dist/state/schema.d.ts +1 -1
  96. package/dist/state/schema.js +171 -3
  97. package/dist/state/schema.js.map +1 -1
  98. package/dist/state/viewer.d.ts +46 -0
  99. package/dist/state/viewer.js +249 -0
  100. package/dist/state/viewer.js.map +1 -0
  101. package/dist/viewer/cli.d.ts +1 -1
  102. package/dist/viewer/cli.js +47 -18
  103. package/dist/viewer/cli.js.map +1 -1
  104. package/dist/workflows/composition.js +25 -2
  105. package/dist/workflows/composition.js.map +1 -1
  106. package/dist/workflows/definition.d.ts +3 -1
  107. package/dist/workflows/definition.js +25 -0
  108. package/dist/workflows/definition.js.map +1 -1
  109. package/dist/workflows/engine.d.ts +1 -0
  110. package/dist/workflows/engine.js +157 -42
  111. package/dist/workflows/engine.js.map +1 -1
  112. package/dist/workflows/errors.d.ts +3 -1
  113. package/dist/workflows/errors.js +4 -7
  114. package/dist/workflows/errors.js.map +1 -1
  115. package/dist/workflows/human-decision.d.ts +3 -0
  116. package/dist/workflows/human-decision.js +31 -0
  117. package/dist/workflows/human-decision.js.map +1 -1
  118. package/dist/workflows/index.d.ts +1 -1
  119. package/dist/workflows/index.js +1 -1
  120. package/dist/workflows/index.js.map +1 -1
  121. package/dist/workflows/schema.js +17 -6
  122. package/dist/workflows/schema.js.map +1 -1
  123. package/dist/{viewer → workflows}/session-reducer.d.ts +3 -1
  124. package/dist/{viewer → workflows}/session-reducer.js +4 -0
  125. package/dist/workflows/session-reducer.js.map +1 -0
  126. package/dist/workflows/store.d.ts +87 -1
  127. package/dist/workflows/store.js +892 -93
  128. package/dist/workflows/store.js.map +1 -1
  129. package/dist/workflows/tool-input.d.ts +0 -22
  130. package/dist/workflows/tool-input.js +1 -44
  131. package/dist/workflows/tool-input.js.map +1 -1
  132. package/dist/workflows/types.d.ts +36 -4
  133. package/docs/2026-08-25-workflow-follow-ups.md +8 -6
  134. package/docs/2026-08-30-out-of-process-workflow-host-plan.md +353 -0
  135. package/docs/CONTROLLERS.md +11 -11
  136. package/docs/DEFERRED_TURNS.md +61 -27
  137. package/docs/HUMAN_DECISIONS.md +12 -4
  138. package/docs/SQLITE_STATE.md +42 -8
  139. package/docs/WORKFLOW_HOST.md +452 -0
  140. package/docs/development.md +46 -30
  141. package/docs/live-replay-protocol.md +129 -100
  142. package/docs/plans/2026-08-19-human-decision-gates-plan.md +34 -8
  143. package/docs/plans/2026-08-27-workflow-terminal-restart-plan.md +399 -0
  144. package/docs/plans/2026-08-28-piw-incremental-viewer-plan.md +293 -0
  145. package/docs/plans/piw-viewer-experience-implementation-plan.md +11 -1
  146. package/docs/plans/tui-viewer-implementation-plan.md +5 -0
  147. package/docs/tui-viewer.md +18 -3
  148. package/docs/workflows.md +164 -156
  149. package/examples/workflows/command-batch.workflow.ts +2 -0
  150. package/examples/workflows/shell.workflow.ts +2 -1
  151. package/herdr-plugin.toml +1 -1
  152. package/package.json +1 -1
  153. package/skills/autodoc/SKILL.md +1 -1
  154. package/skills/autoimplement/SKILL.md +1 -1
  155. package/skills/autoplan/SKILL.md +1 -1
  156. package/skills/pi-workflows/SKILL.md +2 -0
  157. package/src/builtins/autoimplement.workflow.ts +3 -0
  158. package/src/builtins/change-verification.workflow.ts +7 -2
  159. package/src/builtins/metadata.ts +9 -0
  160. package/src/builtins/monitor.workflow.ts +4 -0
  161. package/src/builtins/sanity-check.workflow.ts +4 -0
  162. package/src/builtins/workspace-preparation.workflow.ts +3 -1
  163. package/src/controllers/index.ts +3 -5
  164. package/src/controllers/sqlite.ts +929 -225
  165. package/src/controllers/store.ts +1 -0
  166. package/src/extension/controller-command.ts +45 -0
  167. package/src/extension/index.ts +948 -3506
  168. package/src/extension/message-card.ts +61 -0
  169. package/src/extension/step-message.ts +58 -63
  170. package/src/host/child-worker-supervisor.ts +183 -0
  171. package/src/host/client.ts +293 -0
  172. package/src/host/controller-worker-entry.ts +311 -0
  173. package/src/host/controller-worker-protocol.ts +104 -0
  174. package/src/host/controller-worker-supervisor.ts +79 -0
  175. package/src/host/host-entry.ts +23 -0
  176. package/src/host/processes.ts +171 -54
  177. package/src/host/protocol.ts +196 -0
  178. package/src/host/resolver-entry.ts +241 -0
  179. package/src/host/rpc-executor.ts +76 -34
  180. package/src/host/runner.ts +2813 -422
  181. package/src/host/state.ts +1160 -0
  182. package/src/host/worker-entry.ts +533 -0
  183. package/src/host/worker-protocol.ts +165 -0
  184. package/src/host/worker-store.ts +229 -0
  185. package/src/host/worker-supervisor.ts +74 -0
  186. package/src/render/canvas.ts +44 -10
  187. package/src/render/graph-render.ts +145 -90
  188. package/src/state/database.ts +2 -1
  189. package/src/state/index.ts +14 -0
  190. package/src/state/prune.ts +35 -9
  191. package/src/state/schema.ts +171 -3
  192. package/src/state/viewer.ts +356 -0
  193. package/src/viewer/cli.ts +49 -17
  194. package/src/workflows/composition.ts +36 -2
  195. package/src/workflows/definition.ts +32 -0
  196. package/src/workflows/engine.ts +157 -54
  197. package/src/workflows/errors.ts +11 -2
  198. package/src/workflows/human-decision.ts +49 -0
  199. package/src/workflows/index.ts +2 -0
  200. package/src/workflows/schema.ts +19 -6
  201. package/src/{viewer → workflows}/session-reducer.ts +11 -1
  202. package/src/workflows/store.ts +1316 -108
  203. package/src/workflows/tool-input.ts +2 -60
  204. package/src/workflows/types.ts +32 -4
  205. package/dist/controllers/workflow-engine-scheduler.d.ts +0 -29
  206. package/dist/controllers/workflow-engine-scheduler.js +0 -175
  207. package/dist/controllers/workflow-engine-scheduler.js.map +0 -1
  208. package/dist/extension/controller-host.d.ts +0 -48
  209. package/dist/extension/controller-host.js +0 -110
  210. package/dist/extension/controller-host.js.map +0 -1
  211. package/dist/extension/deferred-turn-coordinator.d.ts +0 -32
  212. package/dist/extension/deferred-turn-coordinator.js +0 -143
  213. package/dist/extension/deferred-turn-coordinator.js.map +0 -1
  214. package/dist/extension/deferred-turn.d.ts +0 -44
  215. package/dist/extension/deferred-turn.js +0 -110
  216. package/dist/extension/deferred-turn.js.map +0 -1
  217. package/dist/extension/executor.d.ts +0 -86
  218. package/dist/extension/executor.js +0 -311
  219. package/dist/extension/executor.js.map +0 -1
  220. package/dist/extension/follow-up-coordinator.d.ts +0 -27
  221. package/dist/extension/follow-up-coordinator.js +0 -131
  222. package/dist/extension/follow-up-coordinator.js.map +0 -1
  223. package/dist/extension/recorder.d.ts +0 -84
  224. package/dist/extension/recorder.js +0 -528
  225. package/dist/extension/recorder.js.map +0 -1
  226. package/dist/extension/session-events.d.ts +0 -133
  227. package/dist/extension/session-events.js +0 -61
  228. package/dist/extension/session-events.js.map +0 -1
  229. package/dist/viewer/session-reducer.js.map +0 -1
  230. package/src/controllers/workflow-engine-scheduler.ts +0 -246
  231. package/src/extension/controller-host.ts +0 -167
  232. package/src/extension/deferred-turn-coordinator.ts +0 -171
  233. package/src/extension/deferred-turn.ts +0 -166
  234. package/src/extension/executor.ts +0 -411
  235. package/src/extension/follow-up-coordinator.ts +0 -151
  236. package/src/extension/recorder.ts +0 -655
  237. package/src/extension/session-events.ts +0 -121
@@ -20,13 +20,22 @@ export class CancelledError extends Error {
20
20
  * The current claim holder owns the run from that point on, so the fenced
21
21
  * writer must stop changing the run immediately.
22
22
  */
23
+ export type ClaimLostReason =
24
+ | "missingAuthority"
25
+ | "expired"
26
+ | "ownerChanged"
27
+ | "tokenChanged"
28
+ | "generationChanged";
29
+
23
30
  export class ClaimLostError extends Error {
24
31
  readonly runId: string;
32
+ readonly reason: ClaimLostReason;
25
33
 
26
- constructor(runId: string) {
27
- super(`Workflow run claim lost: ${runId}`);
34
+ constructor(runId: string, reason: ClaimLostReason = "missingAuthority") {
35
+ super(`Workflow run claim lost (${reason}): ${runId}`);
28
36
  this.name = "ClaimLostError";
29
37
  this.runId = runId;
38
+ this.reason = reason;
30
39
  }
31
40
  }
32
41
 
@@ -1,6 +1,7 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
2
  import { StateDatabase, workflowStatePath } from "../state/database.js";
3
3
  import { resourceIdFor, tokenHash } from "../state/mutation.js";
4
+ import { recordViewerDeltas } from "../state/viewer.js";
4
5
  import {
5
6
  digestCanonical,
6
7
  normalizeDecisionPresentation,
@@ -451,6 +452,7 @@ export class HumanDecisionStore {
451
452
  requestHash,
452
453
  now,
453
454
  );
455
+ recordDecisionViewerChange(this.state, request.runId, request.decisionId, now);
454
456
  return "created";
455
457
  });
456
458
  }
@@ -473,6 +475,13 @@ export class HumanDecisionStore {
473
475
  request: HumanDecisionRequest,
474
476
  submission: HumanDecisionSubmission,
475
477
  ): Promise<HumanDecisionAcceptance> {
478
+ return this.acceptSync(request, submission);
479
+ }
480
+
481
+ acceptSync(
482
+ request: HumanDecisionRequest,
483
+ submission: HumanDecisionSubmission,
484
+ ): HumanDecisionAcceptance {
476
485
  validateHumanDecisionRequestIntegrity(request);
477
486
  const normalized = validateHumanDecisionSubmission(request, submission);
478
487
  const attemptedAt = new Date().toISOString();
@@ -511,6 +520,10 @@ export class HumanDecisionStore {
511
520
  request: HumanDecisionRequest,
512
521
  now = new Date(),
513
522
  ): Promise<HumanDecisionAcceptance> {
523
+ return this.resolveTimeoutSync(request, now);
524
+ }
525
+
526
+ resolveTimeoutSync(request: HumanDecisionRequest, now = new Date()): HumanDecisionAcceptance {
514
527
  validateHumanDecisionRequestIntegrity(request);
515
528
  if (request.expiresAt === undefined || request.defaultResponse === undefined) {
516
529
  throw new Error("Human decision request has no timeout default");
@@ -630,6 +643,7 @@ export class HumanDecisionStore {
630
643
  now,
631
644
  );
632
645
  this.enqueueResolutionEffects(row.resourceId, row.revision + 1, request, "cancelled", now);
646
+ recordDecisionViewerChange(this.state, request.runId, request.decisionId, now);
633
647
  return "created";
634
648
  });
635
649
  }
@@ -732,6 +746,22 @@ export class HumanDecisionStore {
732
746
  return rows.filter(isRequestHashRow).map((row) => this.readRequestFromHash(row.requestHash));
733
747
  }
734
748
 
749
+ async listExpiredDefaultRequests(now = new Date()): Promise<HumanDecisionRequest[]> {
750
+ const timestamp = now.getTime();
751
+ if (!Number.isFinite(timestamp)) throw new Error("Human decision timeout date is invalid");
752
+ const rows = this.state.connection
753
+ .prepare(
754
+ `SELECT h.request_hash AS requestHash
755
+ FROM human_decisions h
756
+ LEFT JOIN human_decision_resolutions r ON r.decision_id = h.decision_id
757
+ WHERE h.deadline_at IS NOT NULL AND h.deadline_at <= ?
758
+ AND h.default_response_hash IS NOT NULL AND r.decision_id IS NULL
759
+ ORDER BY h.deadline_at, h.decision_id`,
760
+ )
761
+ .all(timestamp);
762
+ return rows.filter(isRequestHashRow).map((row) => this.readRequestFromHash(row.requestHash));
763
+ }
764
+
735
765
  private attemptAcceptance(
736
766
  request: HumanDecisionRequest,
737
767
  decision: ResolvedHumanDecision,
@@ -837,6 +867,7 @@ export class HumanDecisionStore {
837
867
  );
838
868
  this.enqueueResolutionEffects(row.resourceId, row.revision + 1, request, "accepted", now);
839
869
  this.markSubmission(request.decisionId, attempt.attemptId, "won", responseHash);
870
+ recordDecisionViewerChange(this.state, request.runId, request.decisionId, now);
840
871
  return { status: "accepted", decision };
841
872
  });
842
873
  }
@@ -1175,6 +1206,24 @@ export class HumanDecisionStore {
1175
1206
  }
1176
1207
  }
1177
1208
 
1209
+ function recordDecisionViewerChange(
1210
+ state: StateDatabase,
1211
+ runId: string,
1212
+ decisionId: string,
1213
+ now: number,
1214
+ ): void {
1215
+ recordViewerDeltas(
1216
+ state,
1217
+ runId,
1218
+ [
1219
+ { targetType: "graph" },
1220
+ { targetType: "replay" },
1221
+ { targetType: "inspector", targetKey: `decision:${decisionId}` },
1222
+ ],
1223
+ now,
1224
+ );
1225
+ }
1226
+
1178
1227
  function digestBuffer(value: string): Buffer {
1179
1228
  const hex = value.startsWith("sha256:") ? value.slice(7) : value;
1180
1229
  if (!/^[a-f0-9]{64}$/i.test(hex)) throw new Error("Expected a SHA-256 digest");
@@ -6,8 +6,10 @@ export {
6
6
  compute,
7
7
  defineWorkflow,
8
8
  defineWorkflowRegistry,
9
+ idempotentEffect,
9
10
  includeWorkflow,
10
11
  includedResult,
12
+ manualEffect,
11
13
  isWorkflowDefinition,
12
14
  notify,
13
15
  shell,
@@ -104,6 +104,24 @@ export function assertValidActionNode(node: ActionNodeDefinition, nodeId = "acti
104
104
  if (hasExec === hasRun) {
105
105
  fail(`node ${nodeId} requires exactly one of run or exec`);
106
106
  }
107
+ if (node.effect === undefined || node.effect === null || typeof node.effect !== "object") {
108
+ fail(`node ${nodeId} requires a managed effect`);
109
+ }
110
+ if (typeof node.effect.type !== "string" || node.effect.type.trim().length === 0) {
111
+ fail(`node ${nodeId} effect type must be nonempty text`);
112
+ }
113
+ if (node.effect.recovery !== "idempotent" && node.effect.recovery !== "manual") {
114
+ fail(`node ${nodeId} effect recovery must be idempotent or manual`);
115
+ }
116
+ if (
117
+ typeof node.effect.idempotencyKey !== "string" &&
118
+ typeof node.effect.idempotencyKey !== "function"
119
+ ) {
120
+ fail(`node ${nodeId} effect idempotencyKey must be text or a function`);
121
+ }
122
+ if (!("request" in node.effect)) {
123
+ fail(`node ${nodeId} effect requires request data or a request function`);
124
+ }
107
125
  if (hasExec) {
108
126
  if (typeof node.exec !== "function") {
109
127
  fail(`node ${nodeId} exec must be a function`);
@@ -121,12 +139,7 @@ export function assertValidShellActionNode(
121
139
  node: ShellActionNodeDefinition,
122
140
  nodeId = "shell",
123
141
  ): void {
124
- if (typeof node.exec !== "function") {
125
- fail(`node ${nodeId} requires an exec function`);
126
- }
127
- assertOptionalFunction(node.parse, `node ${nodeId} parse`);
128
- assertShellUpdates(node, nodeId);
129
- assertCommonNodeFields(node, nodeId);
142
+ assertValidActionNode(node, nodeId);
130
143
  }
131
144
 
132
145
  function assertShellUpdates(node: ShellActionNodeDefinition, nodeId: string): void {
@@ -1,4 +1,4 @@
1
- import type { WorkflowSessionEntryRecord, WorkflowSessionEventRecord } from "../workflows/types.js";
1
+ import type { WorkflowSessionEntryRecord, WorkflowSessionEventRecord } from "./types.js";
2
2
 
3
3
  export type TemporalContentBlock = {
4
4
  contentIndex: number;
@@ -267,6 +267,16 @@ export function reduceSessionEvents(
267
267
  return foldSessionEvents(entries, events, throughSeq);
268
268
  }
269
269
 
270
+ /** Continue a bounded page fold from a durable replay checkpoint. */
271
+ export function reduceSessionEventsFromCheckpoint(
272
+ entries: WorkflowSessionEntryRecord[],
273
+ events: WorkflowSessionEventRecord[],
274
+ throughSeq: number,
275
+ checkpoint: TemporalSessionState,
276
+ ): TemporalSessionState {
277
+ return foldSessionEvents(entries, events, throughSeq, checkpoint);
278
+ }
279
+
270
280
  /**
271
281
  * In-memory seek index. Checkpoints are viewer-only cache state and are never
272
282
  * persisted into a SQLite run state.