@osolmaz/pi-workflows 0.16.1 → 0.16.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (327) hide show
  1. package/README.md +41 -41
  2. package/dist/builtins/monitor.workflow.d.ts +1 -1
  3. package/dist/builtins/monitor.workflow.js +8 -8
  4. package/dist/builtins/pi-agent-group.js +3 -3
  5. package/dist/builtins/pi-agent-group.js.map +1 -1
  6. package/dist/channels/adapter-entry.js +14 -14
  7. package/dist/channels/adapter-entry.js.map +1 -1
  8. package/dist/client/client.d.ts +6 -6
  9. package/dist/client/client.js +27 -27
  10. package/dist/client/client.js.map +1 -1
  11. package/dist/client/protocol.d.ts +4 -1
  12. package/dist/client/protocol.js +17 -11
  13. package/dist/client/protocol.js.map +1 -1
  14. package/dist/client/resolver.d.ts +4 -4
  15. package/dist/client/view.d.ts +7 -1
  16. package/dist/client/view.js +1 -0
  17. package/dist/client/view.js.map +1 -1
  18. package/dist/extension/index.d.ts +1 -1
  19. package/dist/extension/index.js +66 -47
  20. package/dist/extension/index.js.map +1 -1
  21. package/dist/extension/recorder.d.ts +1 -1
  22. package/dist/extension/recorder.js +1 -1
  23. package/dist/extension/recorder.js.map +1 -1
  24. package/dist/extension/remote-recorder-store.d.ts +1 -1
  25. package/dist/extension/remote-recorder-store.js +2 -2
  26. package/dist/extension/remote-recorder-store.js.map +1 -1
  27. package/dist/extension/{controller-command.d.ts → resource-manager-command.d.ts} +6 -6
  28. package/dist/extension/{controller-command.js → resource-manager-command.js} +7 -7
  29. package/dist/extension/resource-manager-command.js.map +1 -0
  30. package/dist/extension/session-delivery.d.ts +1 -1
  31. package/dist/extension/session-delivery.js +2 -2
  32. package/dist/extension/session-delivery.js.map +1 -1
  33. package/dist/extension/session-view.d.ts +1 -1
  34. package/dist/extension/session-view.js +1 -1
  35. package/dist/extension/session-view.js.map +1 -1
  36. package/dist/extension/workflow-message-coordinator.d.ts +4 -3
  37. package/dist/extension/workflow-message-coordinator.js +81 -29
  38. package/dist/extension/workflow-message-coordinator.js.map +1 -1
  39. package/dist/resource-managers/conditions.d.ts +6 -0
  40. package/dist/{controllers → resource-managers}/conditions.js +3 -3
  41. package/dist/resource-managers/conditions.js.map +1 -0
  42. package/dist/resource-managers/definition.d.ts +6 -0
  43. package/dist/resource-managers/definition.js +45 -0
  44. package/dist/resource-managers/definition.js.map +1 -0
  45. package/dist/resource-managers/effects.d.ts +15 -0
  46. package/dist/{controllers → resource-managers}/effects.js +2 -2
  47. package/dist/resource-managers/effects.js.map +1 -0
  48. package/dist/resource-managers/errors.d.ts +12 -0
  49. package/dist/resource-managers/errors.js +25 -0
  50. package/dist/resource-managers/errors.js.map +1 -0
  51. package/dist/resource-managers/index.d.ts +12 -0
  52. package/dist/resource-managers/index.js +12 -0
  53. package/dist/resource-managers/index.js.map +1 -0
  54. package/dist/resource-managers/json.js.map +1 -0
  55. package/dist/resource-managers/loader.d.ts +23 -0
  56. package/dist/resource-managers/loader.js +75 -0
  57. package/dist/resource-managers/loader.js.map +1 -0
  58. package/dist/resource-managers/results.d.ts +5 -0
  59. package/dist/resource-managers/results.js.map +1 -0
  60. package/dist/resource-managers/runtime.d.ts +59 -0
  61. package/dist/{controllers/manager.js → resource-managers/runtime.js} +57 -57
  62. package/dist/resource-managers/runtime.js.map +1 -0
  63. package/dist/{controllers → resource-managers}/sqlite.d.ts +51 -43
  64. package/dist/{controllers → resource-managers}/sqlite.js +187 -125
  65. package/dist/resource-managers/sqlite.js.map +1 -0
  66. package/dist/{controllers → resource-managers}/store.d.ts +33 -33
  67. package/dist/{controllers → resource-managers}/store.js.map +1 -1
  68. package/dist/{controllers → resource-managers}/types.d.ts +41 -41
  69. package/dist/{controllers → resource-managers}/types.js.map +1 -1
  70. package/dist/resource-managers/workflows.d.ts +34 -0
  71. package/dist/{controllers → resource-managers}/workflows.js +15 -15
  72. package/dist/resource-managers/workflows.js.map +1 -0
  73. package/dist/server/channel-effects.js.map +1 -0
  74. package/dist/{host → server}/channel-supervisor.d.ts +6 -6
  75. package/dist/{host → server}/channel-supervisor.js +3 -3
  76. package/dist/server/channel-supervisor.js.map +1 -0
  77. package/dist/{host/child-worker-supervisor.d.ts → server/child-runner-supervisor.d.ts} +15 -13
  78. package/dist/{host/child-worker-supervisor.js → server/child-runner-supervisor.js} +6 -5
  79. package/dist/server/child-runner-supervisor.js.map +1 -0
  80. package/dist/{host → server}/processes.d.ts +3 -3
  81. package/dist/{host → server}/processes.js +3 -3
  82. package/dist/server/processes.js.map +1 -0
  83. package/dist/{host → server}/resolver-entry.d.ts +6 -6
  84. package/dist/{host → server}/resolver-entry.js +14 -14
  85. package/dist/server/resolver-entry.js.map +1 -0
  86. package/dist/server/resource-runner-entry.d.ts +2 -0
  87. package/dist/{host/controller-worker-entry.js → server/resource-runner-entry.js} +33 -33
  88. package/dist/server/resource-runner-entry.js.map +1 -0
  89. package/dist/server/resource-runner-protocol.d.ts +36 -0
  90. package/dist/server/resource-runner-protocol.js +49 -0
  91. package/dist/server/resource-runner-protocol.js.map +1 -0
  92. package/dist/server/resource-runner-supervisor.d.ts +21 -0
  93. package/dist/{host/worker-supervisor.js → server/resource-runner-supervisor.js} +17 -17
  94. package/dist/server/resource-runner-supervisor.js.map +1 -0
  95. package/dist/{host → server}/rpc-bridge.d.ts +3 -3
  96. package/dist/{host → server}/rpc-bridge.js +3 -3
  97. package/dist/server/rpc-bridge.js.map +1 -0
  98. package/dist/{host → server}/rpc-executor.d.ts +1 -1
  99. package/dist/{host → server}/rpc-executor.js +3 -3
  100. package/dist/server/rpc-executor.js.map +1 -0
  101. package/dist/{host/host-entry.js → server/server-entry.js} +6 -6
  102. package/dist/server/server-entry.js.map +1 -0
  103. package/dist/{host/runner.d.ts → server/server.d.ts} +47 -39
  104. package/dist/{host/runner.js → server/server.js} +624 -535
  105. package/dist/server/server.js.map +1 -0
  106. package/dist/{host → server}/state.d.ts +25 -25
  107. package/dist/{host → server}/state.js +49 -49
  108. package/dist/server/state.js.map +1 -0
  109. package/dist/{host → server}/view.d.ts +7 -7
  110. package/dist/{host → server}/view.js +19 -15
  111. package/dist/server/view.js.map +1 -0
  112. package/dist/server/workflow-runner-content.d.ts +6 -0
  113. package/dist/server/workflow-runner-content.js +96 -0
  114. package/dist/server/workflow-runner-content.js.map +1 -0
  115. package/dist/server/workflow-runner-entry.d.ts +13 -0
  116. package/dist/{host/worker-entry.js → server/workflow-runner-entry.js} +89 -57
  117. package/dist/server/workflow-runner-entry.js.map +1 -0
  118. package/dist/server/workflow-runner-protocol.d.ts +69 -0
  119. package/dist/server/workflow-runner-protocol.js +176 -0
  120. package/dist/server/workflow-runner-protocol.js.map +1 -0
  121. package/dist/{host/worker-store.d.ts → server/workflow-runner-store.d.ts} +11 -11
  122. package/dist/{host/worker-store.js → server/workflow-runner-store.js} +8 -8
  123. package/dist/server/workflow-runner-store.js.map +1 -0
  124. package/dist/server/workflow-runner-supervisor.d.ts +22 -0
  125. package/dist/server/workflow-runner-supervisor.js +56 -0
  126. package/dist/server/workflow-runner-supervisor.js.map +1 -0
  127. package/dist/state/prune.d.ts +1 -1
  128. package/dist/state/prune.js +17 -10
  129. package/dist/state/prune.js.map +1 -1
  130. package/dist/state/schema.js +2 -2
  131. package/dist/state/workflow-messages.d.ts +3 -1
  132. package/dist/state/workflow-messages.js +32 -1
  133. package/dist/state/workflow-messages.js.map +1 -1
  134. package/dist/viewer/backup.js +1 -1
  135. package/dist/viewer/backup.js.map +1 -1
  136. package/dist/viewer/cli.d.ts +2 -2
  137. package/dist/viewer/cli.js +46 -32
  138. package/dist/viewer/cli.js.map +1 -1
  139. package/dist/viewer/tui.d.ts +1 -1
  140. package/dist/viewer/tui.js +1 -1
  141. package/dist/viewer/tui.js.map +1 -1
  142. package/dist/workflows/command-batch.js +2 -2
  143. package/dist/workflows/composition.js +0 -4
  144. package/dist/workflows/composition.js.map +1 -1
  145. package/dist/workflows/definition.js +0 -8
  146. package/dist/workflows/definition.js.map +1 -1
  147. package/dist/workflows/engine.js +18 -20
  148. package/dist/workflows/engine.js.map +1 -1
  149. package/dist/workflows/schema.js +0 -4
  150. package/dist/workflows/schema.js.map +1 -1
  151. package/dist/workflows/store.d.ts +27 -11
  152. package/dist/workflows/store.js +66 -18
  153. package/dist/workflows/store.js.map +1 -1
  154. package/dist/workflows/types.d.ts +3 -5
  155. package/docs/2026-08-20-durable-workflow-launch-plan.md +12 -12
  156. package/docs/2026-08-25-workflow-follow-ups.md +7 -7
  157. package/docs/2026-08-25-workflow-settings.md +3 -3
  158. package/docs/2026-08-30-out-of-process-workflow-host-plan.md +3 -3
  159. package/docs/2026-09-01-restore-session-delivery-controls-plan.md +5 -5
  160. package/docs/2026-09-01-unified-workflow-client-plan.md +6 -6
  161. package/docs/2026-09-02-installed-live-e2e-plan.md +3 -3
  162. package/docs/2026-09-02-unify-workflow-messages-plan.md +8 -8
  163. package/docs/2026-09-04-workflow-run-state-plan.md +363 -0
  164. package/docs/DEFERRED_TURNS.md +7 -7
  165. package/docs/DESIGN_PHILOSOPHY.md +1 -1
  166. package/docs/HUMAN_DECISIONS.md +11 -11
  167. package/docs/HUMAN_DECISION_PRESENTATIONS.md +4 -4
  168. package/docs/MONITOR.md +1 -1
  169. package/docs/RESOURCE_MANAGERS.md +221 -0
  170. package/docs/SQLITE_STATE.md +40 -34
  171. package/docs/WORKFLOW_COMPOSITION.md +1 -1
  172. package/docs/{WORKFLOW_HOST.md → WORKFLOW_SERVER.md} +131 -116
  173. package/docs/WORKFLOW_STEP_MESSAGES.md +18 -18
  174. package/docs/WORKFLOW_UPDATES.md +16 -16
  175. package/docs/development.md +15 -14
  176. package/docs/live-replay-protocol.md +20 -20
  177. package/docs/plans/2026-08-04-controller-runtime-plan.md +25 -25
  178. package/docs/plans/2026-08-05-always-on-workflows-plan.md +8 -8
  179. package/docs/plans/2026-08-10-agent-managed-monitor-workflows-plan.md +2 -2
  180. package/docs/plans/2026-08-13-session-addressed-workflow-notifications-plan.md +1 -1
  181. package/docs/plans/2026-08-16-workflow-updates-plan.md +5 -5
  182. package/docs/plans/2026-08-19-human-decision-gates-plan.md +1 -1
  183. package/docs/plans/2026-08-20-bounded-command-batches-plan.md +1 -1
  184. package/docs/plans/2026-08-21-deferred-turn-intents-plan.md +3 -3
  185. package/docs/plans/2026-08-21-sanity-check-plan.md +2 -2
  186. package/docs/plans/2026-08-23-assistant-agent-completion-plan.md +5 -5
  187. package/docs/plans/2026-08-23-sqlite-state-plan.md +25 -25
  188. package/docs/plans/2026-08-25-live-workflow-settings-plan.md +4 -4
  189. package/docs/plans/2026-08-27-workflow-terminal-restart-plan.md +3 -3
  190. package/docs/plans/2026-09-04-workflow-runner-resume-state-plan.md +318 -0
  191. package/docs/plans/piw-viewer-experience-implementation-plan.md +1 -1
  192. package/docs/tui-viewer.md +9 -9
  193. package/docs/workflows.md +78 -63
  194. package/examples/{controllers/pull-request.controller.ts → resource-managers/pull-request.resource-manager.ts} +12 -12
  195. package/herdr-plugin.toml +1 -1
  196. package/package.json +5 -5
  197. package/protocol/client.v1.schema.json +7 -7
  198. package/protocol/fixtures/client-v1.json +1 -1
  199. package/src/builtins/monitor.workflow.ts +9 -9
  200. package/src/builtins/pi-agent-group.ts +3 -3
  201. package/src/channels/adapter-entry.ts +14 -14
  202. package/src/client/client.ts +33 -32
  203. package/src/client/protocol.ts +16 -11
  204. package/src/client/resolver.ts +4 -4
  205. package/src/client/view.ts +9 -1
  206. package/src/extension/index.ts +81 -53
  207. package/src/extension/recorder.ts +1 -1
  208. package/src/extension/remote-recorder-store.ts +2 -2
  209. package/src/extension/resource-manager-command.ts +45 -0
  210. package/src/extension/session-delivery.ts +2 -2
  211. package/src/extension/session-view.ts +1 -1
  212. package/src/extension/workflow-message-coordinator.ts +94 -30
  213. package/src/{controllers → resource-managers}/conditions.ts +23 -23
  214. package/src/resource-managers/definition.ts +71 -0
  215. package/src/{controllers → resource-managers}/effects.ts +9 -9
  216. package/src/resource-managers/errors.ts +27 -0
  217. package/src/resource-managers/index.ts +88 -0
  218. package/src/resource-managers/loader.ts +111 -0
  219. package/src/{controllers → resource-managers}/results.ts +4 -4
  220. package/src/{controllers/manager.ts → resource-managers/runtime.ts} +95 -92
  221. package/src/{controllers → resource-managers}/sqlite.ts +275 -181
  222. package/src/{controllers → resource-managers}/store.ts +46 -38
  223. package/src/{controllers → resource-managers}/types.ts +50 -41
  224. package/src/{controllers → resource-managers}/workflows.ts +43 -41
  225. package/src/{host → server}/channel-supervisor.ts +8 -8
  226. package/src/{host/child-worker-supervisor.ts → server/child-runner-supervisor.ts} +22 -18
  227. package/src/{host → server}/processes.ts +3 -3
  228. package/src/{host → server}/resolver-entry.ts +28 -25
  229. package/src/{host/controller-worker-entry.ts → server/resource-runner-entry.ts} +67 -65
  230. package/src/server/resource-runner-protocol.ts +103 -0
  231. package/src/server/resource-runner-supervisor.ts +76 -0
  232. package/src/{host → server}/rpc-bridge.ts +3 -3
  233. package/src/{host → server}/rpc-executor.ts +4 -4
  234. package/src/{host/host-entry.ts → server/server-entry.ts} +5 -5
  235. package/src/{host/runner.ts → server/server.ts} +786 -672
  236. package/src/{host → server}/state.ts +78 -67
  237. package/src/{host → server}/view.ts +21 -17
  238. package/src/server/workflow-runner-content.ts +119 -0
  239. package/src/{host/worker-entry.ts → server/workflow-runner-entry.ts} +131 -82
  240. package/src/server/workflow-runner-protocol.ts +241 -0
  241. package/src/{host/worker-store.ts → server/workflow-runner-store.ts} +18 -20
  242. package/src/server/workflow-runner-supervisor.ts +79 -0
  243. package/src/state/prune.ts +27 -9
  244. package/src/state/schema.ts +2 -2
  245. package/src/state/workflow-messages.ts +52 -1
  246. package/src/viewer/backup.ts +1 -1
  247. package/src/viewer/cli.ts +50 -36
  248. package/src/viewer/tui.ts +1 -1
  249. package/src/workflows/command-batch.ts +2 -2
  250. package/src/workflows/composition.ts +0 -5
  251. package/src/workflows/definition.ts +0 -8
  252. package/src/workflows/engine.ts +18 -21
  253. package/src/workflows/schema.ts +0 -6
  254. package/src/workflows/store.ts +95 -28
  255. package/src/workflows/types.ts +3 -5
  256. package/dist/controllers/conditions.d.ts +0 -6
  257. package/dist/controllers/conditions.js.map +0 -1
  258. package/dist/controllers/definition.d.ts +0 -6
  259. package/dist/controllers/definition.js +0 -45
  260. package/dist/controllers/definition.js.map +0 -1
  261. package/dist/controllers/effects.d.ts +0 -15
  262. package/dist/controllers/effects.js.map +0 -1
  263. package/dist/controllers/errors.d.ts +0 -12
  264. package/dist/controllers/errors.js +0 -25
  265. package/dist/controllers/errors.js.map +0 -1
  266. package/dist/controllers/index.d.ts +0 -12
  267. package/dist/controllers/index.js +0 -12
  268. package/dist/controllers/index.js.map +0 -1
  269. package/dist/controllers/json.js.map +0 -1
  270. package/dist/controllers/loader.d.ts +0 -23
  271. package/dist/controllers/loader.js +0 -74
  272. package/dist/controllers/loader.js.map +0 -1
  273. package/dist/controllers/manager.d.ts +0 -59
  274. package/dist/controllers/manager.js.map +0 -1
  275. package/dist/controllers/results.d.ts +0 -5
  276. package/dist/controllers/results.js.map +0 -1
  277. package/dist/controllers/sqlite.js.map +0 -1
  278. package/dist/controllers/workflows.d.ts +0 -34
  279. package/dist/controllers/workflows.js.map +0 -1
  280. package/dist/extension/controller-command.js.map +0 -1
  281. package/dist/host/channel-effects.js.map +0 -1
  282. package/dist/host/channel-supervisor.js.map +0 -1
  283. package/dist/host/child-worker-supervisor.js.map +0 -1
  284. package/dist/host/controller-worker-entry.d.ts +0 -2
  285. package/dist/host/controller-worker-entry.js.map +0 -1
  286. package/dist/host/controller-worker-protocol.d.ts +0 -36
  287. package/dist/host/controller-worker-protocol.js +0 -49
  288. package/dist/host/controller-worker-protocol.js.map +0 -1
  289. package/dist/host/controller-worker-supervisor.d.ts +0 -21
  290. package/dist/host/controller-worker-supervisor.js +0 -54
  291. package/dist/host/controller-worker-supervisor.js.map +0 -1
  292. package/dist/host/host-entry.js.map +0 -1
  293. package/dist/host/processes.js.map +0 -1
  294. package/dist/host/resolver-entry.js.map +0 -1
  295. package/dist/host/rpc-bridge.js.map +0 -1
  296. package/dist/host/rpc-executor.js.map +0 -1
  297. package/dist/host/runner.js.map +0 -1
  298. package/dist/host/state.js.map +0 -1
  299. package/dist/host/view.js.map +0 -1
  300. package/dist/host/worker-entry.d.ts +0 -10
  301. package/dist/host/worker-entry.js.map +0 -1
  302. package/dist/host/worker-protocol.d.ts +0 -31
  303. package/dist/host/worker-protocol.js +0 -122
  304. package/dist/host/worker-protocol.js.map +0 -1
  305. package/dist/host/worker-store.js.map +0 -1
  306. package/dist/host/worker-supervisor.d.ts +0 -22
  307. package/dist/host/worker-supervisor.js.map +0 -1
  308. package/docs/CONTROLLERS.md +0 -217
  309. package/src/controllers/definition.ts +0 -65
  310. package/src/controllers/errors.ts +0 -27
  311. package/src/controllers/index.ts +0 -88
  312. package/src/controllers/loader.ts +0 -104
  313. package/src/extension/controller-command.ts +0 -45
  314. package/src/host/controller-worker-protocol.ts +0 -104
  315. package/src/host/controller-worker-supervisor.ts +0 -79
  316. package/src/host/worker-protocol.ts +0 -165
  317. package/src/host/worker-supervisor.ts +0 -74
  318. /package/dist/{controllers → resource-managers}/json.d.ts +0 -0
  319. /package/dist/{controllers → resource-managers}/json.js +0 -0
  320. /package/dist/{controllers → resource-managers}/results.js +0 -0
  321. /package/dist/{controllers → resource-managers}/store.js +0 -0
  322. /package/dist/{controllers → resource-managers}/types.js +0 -0
  323. /package/dist/{host → server}/channel-effects.d.ts +0 -0
  324. /package/dist/{host → server}/channel-effects.js +0 -0
  325. /package/dist/{host/host-entry.d.ts → server/server-entry.d.ts} +0 -0
  326. /package/src/{controllers → resource-managers}/json.ts +0 -0
  327. /package/src/{host → server}/channel-effects.ts +0 -0
@@ -4,8 +4,6 @@ import type { WorkflowSettingsScopeRecord } from "../workflows/settings.js";
4
4
  import {
5
5
  createDefinitionSnapshot,
6
6
  type InitializeWorkflowRunOptions,
7
- type LoadedWorkflowRun,
8
- type ReadWorkflowRunOptions,
9
7
  type WorkflowExecutionStore,
10
8
  } from "../workflows/store.js";
11
9
  import type {
@@ -22,28 +20,31 @@ import type {
22
20
  WorkflowUpdateInput,
23
21
  WorkflowUpdateRecord,
24
22
  } from "../workflows/types.js";
25
- import type { WorkerMessageKind, WorkerStoreOperation } from "./worker-protocol.js";
26
- import { workerKindForOperation } from "./worker-protocol.js";
23
+ import type {
24
+ WorkflowRunnerMessageKind,
25
+ WorkflowRunnerStoreOperation,
26
+ } from "./workflow-runner-protocol.js";
27
+ import { runnerKindForOperation } from "./workflow-runner-protocol.js";
27
28
 
28
- export interface WorkerStoreTransport {
29
+ export interface WorkflowRunnerStoreTransport {
29
30
  request(options: {
30
31
  messageId: string;
31
- operation: WorkerStoreOperation;
32
- kind: WorkerMessageKind;
32
+ operation: WorkflowRunnerStoreOperation;
33
+ kind: WorkflowRunnerMessageKind;
33
34
  expectedRevision: number;
34
35
  attemptId?: string;
35
36
  payload: JsonValue;
36
37
  }): Promise<{ result?: JsonValue; revision?: number }>;
37
38
  }
38
39
 
39
- /** A worker-side store that can only propose state changes to its host. */
40
- export class HostBackedWorkflowStore implements WorkflowExecutionStore {
41
- readonly databasePath = "host://pi-workflows-state";
40
+ /** A runner-side store that can only propose state changes to its server. */
41
+ export class ServerBackedWorkflowStore implements WorkflowExecutionStore {
42
+ readonly databasePath = "server://pi-workflows-state";
42
43
  private revision: number;
43
44
 
44
45
  constructor(
45
46
  private readonly runId: string,
46
- private readonly transport: WorkerStoreTransport,
47
+ private readonly transport: WorkflowRunnerStoreTransport,
47
48
  initialRevision = 0,
48
49
  ) {
49
50
  this.revision = initialRevision;
@@ -62,15 +63,12 @@ export class HostBackedWorkflowStore implements WorkflowExecutionStore {
62
63
  });
63
64
  }
64
65
 
65
- async prepareRunResume(runId: string): Promise<LoadedWorkflowRun> {
66
- return await this.call<LoadedWorkflowRun>("store.prepareRunResume", { runId });
66
+ async prepareRunResume(runId: string): Promise<WorkflowRunState> {
67
+ return await this.call<WorkflowRunState>("store.prepareRunResume", { runId });
67
68
  }
68
69
 
69
- async readRun(
70
- runId: string,
71
- options: ReadWorkflowRunOptions = {},
72
- ): Promise<LoadedWorkflowRun | null> {
73
- return await this.call<LoadedWorkflowRun | null>("store.readRun", { runId, options });
70
+ async readRunState(runId: string): Promise<WorkflowRunState | null> {
71
+ return await this.call<WorkflowRunState | null>("store.readRunState", { runId });
74
72
  }
75
73
 
76
74
  async writeSnapshot(
@@ -211,14 +209,14 @@ export class HostBackedWorkflowStore implements WorkflowExecutionStore {
211
209
  }
212
210
 
213
211
  private async call<T>(
214
- operation: WorkerStoreOperation,
212
+ operation: WorkflowRunnerStoreOperation,
215
213
  payload: Record<string, unknown>,
216
214
  attemptId?: string,
217
215
  ): Promise<T> {
218
216
  const response = await this.transport.request({
219
217
  messageId: randomUUID(),
220
218
  operation,
221
- kind: workerKindForOperation(operation, payload),
219
+ kind: runnerKindForOperation(operation, payload),
222
220
  expectedRevision: this.revision,
223
221
  ...(attemptId === undefined ? {} : { attemptId }),
224
222
  payload: payload as JsonValue,
@@ -0,0 +1,79 @@
1
+ import fs from "node:fs";
2
+ import { createRequire } from "node:module";
3
+ import { fileURLToPath } from "node:url";
4
+ import { canonicalJson } from "../state/json.js";
5
+ import { ChildRunnerSupervisor, type SupervisedRunnerResult } from "./child-runner-supervisor.js";
6
+ import { ServerProcessRegistry, type ProcessIdentity } from "./processes.js";
7
+ import type { WorkflowRunnerLaunchEnvelope, RunnerOutcome } from "./state.js";
8
+ import {
9
+ MAX_WORKFLOW_RUNNER_PROTOCOL_MESSAGE_BYTES,
10
+ encodeRunnerLine,
11
+ parseRunnerMessage,
12
+ type WorkflowRunnerMessage,
13
+ type WorkflowRunnerResponse,
14
+ } from "./workflow-runner-protocol.js";
15
+
16
+ export type WorkflowRunnerResult = SupervisedRunnerResult;
17
+
18
+ export class WorkflowRunnerSupervisor {
19
+ private readonly supervisor: ChildRunnerSupervisor<WorkflowRunnerMessage, WorkflowRunnerResponse>;
20
+
21
+ constructor(
22
+ readonly envelope: WorkflowRunnerLaunchEnvelope,
23
+ private readonly options: {
24
+ registry: ServerProcessRegistry;
25
+ onMessage: (message: WorkflowRunnerMessage) => Promise<WorkflowRunnerResponse>;
26
+ env?: Record<string, string>;
27
+ startupTimeoutMs?: number;
28
+ runnerEntryPath?: string;
29
+ onSpawn?: (identity: ProcessIdentity) => void;
30
+ onDiagnostic?: (message: string) => void;
31
+ },
32
+ ) {
33
+ this.supervisor = new ChildRunnerSupervisor({
34
+ label: "Workflow runner",
35
+ registry: options.registry,
36
+ onMessage: options.onMessage,
37
+ parseMessage: parseRunnerMessage,
38
+ encodeResponse: encodeRunnerLine,
39
+ isReady: (message) => message.kind === "runner.ready",
40
+ isTerminal: (message) => message.kind === "runner.exiting",
41
+ maxMessageBytes: MAX_WORKFLOW_RUNNER_PROTOCOL_MESSAGE_BYTES,
42
+ oversizedMessage: "Runner protocol message exceeds 1 MiB",
43
+ ...(options.startupTimeoutMs === undefined
44
+ ? {}
45
+ : { startupTimeoutMs: options.startupTimeoutMs }),
46
+ ...(options.onSpawn === undefined ? {} : { onSpawn: options.onSpawn }),
47
+ ...(options.onDiagnostic === undefined ? {} : { onDiagnostic: options.onDiagnostic }),
48
+ });
49
+ }
50
+
51
+ async start(): Promise<void> {
52
+ const builtEntry = fileURLToPath(new URL("./workflow-runner-entry.js", import.meta.url));
53
+ const sourceEntry = fileURLToPath(new URL("./workflow-runner-entry.ts", import.meta.url));
54
+ const runnerEntry = this.options.runnerEntryPath ?? builtEntry;
55
+ const args =
56
+ this.options.runnerEntryPath === undefined && !fs.existsSync(builtEntry)
57
+ ? ["--import", createRequire(import.meta.url).resolve("tsx"), sourceEntry]
58
+ : [runnerEntry];
59
+ await this.supervisor.start({
60
+ args,
61
+ cwd: this.envelope.projectPath,
62
+ env: {
63
+ ...process.env,
64
+ ...this.options.env,
65
+ PI_WORKFLOWS_WORKFLOW_RUNNER_LAUNCH: Buffer.from(canonicalJson(this.envelope)).toString(
66
+ "base64url",
67
+ ),
68
+ },
69
+ });
70
+ }
71
+
72
+ async wait(): Promise<WorkflowRunnerResult> {
73
+ return await this.supervisor.wait();
74
+ }
75
+
76
+ async stop(outcome: RunnerOutcome = "cancelled"): Promise<void> {
77
+ await this.supervisor.stop(outcome);
78
+ }
79
+ }
@@ -48,6 +48,7 @@ export async function pruneState(
48
48
  state: StateDatabase,
49
49
  databasePath: string,
50
50
  options: StatePruneOptions,
51
+ activeBlobHashes: () => readonly Buffer[] = () => [],
51
52
  ): Promise<StatePruneReport> {
52
53
  validateStatePruneOptions(options);
53
54
  const cutoff = parseCutoff(options.before);
@@ -114,8 +115,9 @@ export async function pruneState(
114
115
  )`,
115
116
  )
116
117
  .run();
117
- const blobStats = unreferencedBlobStats(state.connection);
118
- deleteUnreferencedBlobs(state.connection);
118
+ const retainedBlobHashes = activeBlobHashes();
119
+ const blobStats = unreferencedBlobStats(state.connection, retainedBlobHashes);
120
+ deleteUnreferencedBlobs(state.connection, retainedBlobHashes);
119
121
  deletedRows = totalChanges(state.connection) - beforeChanges;
120
122
  deletedBlobs = blobStats.count;
121
123
  deletedBlobBytes = blobStats.bytes;
@@ -356,21 +358,31 @@ function selectionSignature(database: Database.Database, runIds: string[]): stri
356
358
  return JSON.stringify([resourceRows, queueRows]);
357
359
  }
358
360
 
359
- function unreferencedBlobStats(database: Database.Database): { count: number; bytes: number } {
360
- const predicate = blobReferencePredicate(database);
361
+ function unreferencedBlobStats(
362
+ database: Database.Database,
363
+ retainedBlobHashes: readonly Buffer[],
364
+ ): { count: number; bytes: number } {
365
+ const predicate = blobReferencePredicate(database, retainedBlobHashes.length);
361
366
  const row = database
362
367
  .prepare(
363
368
  `SELECT count(*) AS count, COALESCE(sum(byte_length), 0) AS bytes FROM blobs WHERE ${predicate}`,
364
369
  )
365
- .get();
370
+ .get(...retainedBlobHashes);
366
371
  return isBlobStatsRow(row) ? row : { count: 0, bytes: 0 };
367
372
  }
368
373
 
369
- function deleteUnreferencedBlobs(database: Database.Database): void {
370
- database.prepare(`DELETE FROM blobs WHERE ${blobReferencePredicate(database)}`).run();
374
+ function deleteUnreferencedBlobs(
375
+ database: Database.Database,
376
+ retainedBlobHashes: readonly Buffer[],
377
+ ): void {
378
+ database
379
+ .prepare(
380
+ `DELETE FROM blobs WHERE ${blobReferencePredicate(database, retainedBlobHashes.length)}`,
381
+ )
382
+ .run(...retainedBlobHashes);
371
383
  }
372
384
 
373
- function blobReferencePredicate(database: Database.Database): string {
385
+ function blobReferencePredicate(database: Database.Database, retainedBlobCount: number): string {
374
386
  const tables = database
375
387
  .prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'")
376
388
  .all()
@@ -387,7 +399,13 @@ function blobReferencePredicate(database: Database.Database): string {
387
399
  );
388
400
  }
389
401
  }
390
- return references.length === 0 ? "1 = 1" : `blob_hash NOT IN (${references.join(" UNION ")})`;
402
+ const unreferenced =
403
+ references.length === 0 ? "1 = 1" : `blob_hash NOT IN (${references.join(" UNION ")})`;
404
+ if (retainedBlobCount === 0) return unreferenced;
405
+ const retainedPlaceholders = placeholders(
406
+ Array.from({ length: retainedBlobCount }, () => undefined),
407
+ );
408
+ return `${unreferenced} AND blob_hash NOT IN (${retainedPlaceholders})`;
391
409
  }
392
410
 
393
411
  function descendants(root: string, children: Map<string, string[]>): string[] {
@@ -302,8 +302,8 @@ CREATE TABLE host_commands (
302
302
  'workflowMessage.reportBranch', 'workflowTurn.report', 'sessionView.clearTerminal',
303
303
  'run.restart', 'run.changeSettings', 'followUp.queue', 'followUp.remove', 'session.record',
304
304
  'channel.reload', 'channel.recover',
305
- 'controller.list', 'controller.get', 'controller.apply', 'controller.reconcile', 'controller.delete',
306
- 'host.status', 'host.stop', 'state.backup', 'state.prune'
305
+ 'resourceManager.list', 'resourceManager.get', 'resourceManager.apply', 'resourceManager.reconcile', 'resourceManager.delete',
306
+ 'server.status', 'server.stop', 'state.backup', 'state.prune'
307
307
  )),
308
308
  idempotency_key TEXT NOT NULL,
309
309
  request_fingerprint BLOB NOT NULL CHECK (length(request_fingerprint) = 32),
@@ -92,7 +92,7 @@ type WorkflowTurnRow = {
92
92
  endedAt: number | null;
93
93
  };
94
94
 
95
- /** Durable workflow content that the host requires Pi to add to one origin session. */
95
+ /** Durable workflow content that the server requires Pi to add to one origin session. */
96
96
  export class WorkflowMessageStore {
97
97
  constructor(readonly state: StateDatabase) {}
98
98
 
@@ -279,6 +279,12 @@ export class WorkflowMessageStore {
279
279
  throw new Error("Workflow turn does not match its message");
280
280
  }
281
281
  if (message.status !== "sent") throw new Error("Workflow turn requires a sent message");
282
+ const open = this.openTurnForMessage(options.workflowMessageId);
283
+ if (open !== undefined) {
284
+ throw new Error(
285
+ `Workflow message ${options.workflowMessageId} already has open turn ${open.workflowTurnId}`,
286
+ );
287
+ }
282
288
  this.state.connection
283
289
  .prepare(
284
290
  `INSERT INTO workflow_turns(
@@ -337,6 +343,51 @@ export class WorkflowMessageStore {
337
343
  });
338
344
  }
339
345
 
346
+ settleOpenTurnsForRun(
347
+ runId: string,
348
+ stopReason: WorkflowTurnStopReason = "lost",
349
+ now: number = Date.now(),
350
+ ): WorkflowTurn[] {
351
+ return this.state.transaction(() => {
352
+ const open = this.state.connection
353
+ .prepare(
354
+ `SELECT workflow_turn_id AS workflowTurnId, workflow_message_id AS workflowMessageId,
355
+ run_id AS runId, target_session_id AS targetSessionId, state,
356
+ stop_reason AS stopReason, response_session_entry_id AS responseSessionEntryId,
357
+ started_at AS startedAt, ended_at AS endedAt
358
+ FROM workflow_turns WHERE run_id = ? AND state = 'started' ORDER BY started_at`,
359
+ )
360
+ .all(runId)
361
+ .filter(isWorkflowTurnRow)
362
+ .map(mapTurn);
363
+ this.state.connection
364
+ .prepare(
365
+ `UPDATE workflow_turns SET state = 'ended', stop_reason = ?, ended_at = ?
366
+ WHERE run_id = ? AND state = 'started'`,
367
+ )
368
+ .run(stopReason, now, runId);
369
+ return open.map((turn) => this.requireTurn(turn.workflowTurnId));
370
+ });
371
+ }
372
+
373
+ cancelPendingForRun(
374
+ runId: string,
375
+ now: number = Date.now(),
376
+ kinds: readonly WorkflowMessageKind[] = ["step", "decision", "followUp"],
377
+ ): number {
378
+ if (kinds.length === 0) return 0;
379
+ return this.state.transaction(
380
+ () =>
381
+ this.state.connection
382
+ .prepare(
383
+ `UPDATE workflow_messages SET status = 'cancelled', updated_at = ?
384
+ WHERE run_id = ? AND status = 'pending'
385
+ AND kind IN (${kinds.map(() => "?").join(", ")})`,
386
+ )
387
+ .run(now, runId, ...kinds).changes,
388
+ );
389
+ }
390
+
340
391
  getTurn(workflowTurnId: string): WorkflowTurn | undefined {
341
392
  const row = this.state.connection
342
393
  .prepare(
@@ -10,7 +10,7 @@ export function verifyInactiveBackup(
10
10
  const resolved = path.resolve(databasePath);
11
11
  const active = path.resolve(activeDatabasePath);
12
12
  if (sameFile(resolved, active)) {
13
- throw new Error("Active workflow state must be verified through the workflow host");
13
+ throw new Error("Active workflow state must be verified through the workflow server");
14
14
  }
15
15
  const state = new StateDatabase({ filePath: resolved, mode: "read-only" });
16
16
  try {
package/src/viewer/cli.ts CHANGED
@@ -12,35 +12,35 @@ import { renderClientView, runViewer } from "./tui.js";
12
12
 
13
13
  const CLI_CLIENT_ID = "pi-workflows-cli";
14
14
 
15
- const USAGE = `pi-workflows — workflow runs and controller resources
15
+ const USAGE = `pi-workflows — workflow runs and managed resources
16
16
 
17
17
  Usage:
18
18
  pi-workflows view [runId] [--once]
19
19
  pi-workflows runs
20
- pi-workflows controllers
21
- pi-workflows controller <controller> <key>
20
+ pi-workflows resource-managers
21
+ pi-workflows resource-manager <resource-manager> <key>
22
22
  pi-workflows state status
23
23
  pi-workflows state verify [inactive-backup]
24
24
  pi-workflows state backup <destination>
25
25
  pi-workflows state prune --before <timestamp> --dry-run
26
26
  pi-workflows state prune --before <timestamp> --backup <absolute-path> --apply
27
- pi-workflows host start
28
- pi-workflows host status
29
- pi-workflows host stop
30
- pi-workflows host run [-- <extra pi args>]
27
+ pi-workflows server start
28
+ pi-workflows server status
29
+ pi-workflows server stop
30
+ pi-workflows server run [-- <extra pi args>]
31
31
  pi-workflows herdr sync [--json]
32
32
  pi-workflows herdr setup [--json]
33
33
 
34
- Active state is available only through the package-owned workflow host.
34
+ Active state is available only through the package-owned workflow server.
35
35
  `;
36
36
 
37
37
  export type CliArgs = {
38
38
  command: string;
39
39
  runId?: string;
40
- controllerName?: string;
40
+ resourceManagerName?: string;
41
41
  resourceKey?: string;
42
42
  herdrAction?: string;
43
- hostAction?: "start" | "status" | "stop" | "run";
43
+ serverAction?: "start" | "status" | "stop" | "run";
44
44
  stateAction?: "status" | "verify" | "backup" | "prune";
45
45
  backupDestination?: string;
46
46
  verifyBackup?: string;
@@ -55,9 +55,16 @@ export function parseCliArgs(argv: string[]): CliArgs {
55
55
  const args = [...argv];
56
56
  const command = args[0] && !args[0].startsWith("-") ? (args.shift() as string) : "view";
57
57
  if (
58
- !["view", "runs", "controllers", "controller", "state", "host", "herdr", "help"].includes(
59
- command,
60
- )
58
+ ![
59
+ "view",
60
+ "runs",
61
+ "resource-managers",
62
+ "resource-manager",
63
+ "state",
64
+ "server",
65
+ "herdr",
66
+ "help",
67
+ ].includes(command)
61
68
  ) {
62
69
  throw new Error(`Unknown command: ${command}`);
63
70
  }
@@ -87,23 +94,24 @@ export function parseCliArgs(argv: string[]): CliArgs {
87
94
  }
88
95
 
89
96
  if (command !== "herdr" && json) throw new Error("--json is available only for herdr sync");
90
- if (command === "host") {
91
- if (project !== undefined) throw new Error("The global host does not accept --project");
97
+ if (command === "server") {
98
+ if (project !== undefined) throw new Error("The global server does not accept --project");
92
99
  const action = positionals[0];
93
100
  if (action !== "start" && action !== "status" && action !== "stop" && action !== "run") {
94
- throw new Error("host requires start, status, stop, or run");
101
+ throw new Error("server requires start, status, stop, or run");
95
102
  }
96
- if (positionals.length !== 1) throw new Error("host accepts one lifecycle action");
103
+ if (positionals.length !== 1) throw new Error("server accepts one lifecycle action");
97
104
  if (piArgs.length > 0 && action !== "run") {
98
- throw new Error("Extra Pi arguments are available only for host run");
105
+ throw new Error("Extra Pi arguments are available only for server run");
99
106
  }
100
- return { command, hostAction: action, once, json, piArgs };
107
+ return { command, serverAction: action, once, json, piArgs };
101
108
  }
102
- if (command === "controller") {
103
- if (positionals.length !== 2) throw new Error("controller requires <controller> and <key>");
109
+ if (command === "resource-manager") {
110
+ if (positionals.length !== 2)
111
+ throw new Error("resource-manager requires <resource-manager> and <key>");
104
112
  return {
105
113
  command,
106
- controllerName: positionals[0] as string,
114
+ resourceManagerName: positionals[0] as string,
107
115
  resourceKey: positionals[1] as string,
108
116
  once,
109
117
  json,
@@ -188,8 +196,8 @@ export async function main(argv = process.argv.slice(2)): Promise<number> {
188
196
  }
189
197
 
190
198
  try {
191
- if (args.command === "host") {
192
- return await runHost(args.hostAction as "start" | "status" | "stop" | "run", args.piArgs);
199
+ if (args.command === "server") {
200
+ return await runServer(args.serverAction as "start" | "status" | "stop" | "run", args.piArgs);
193
201
  }
194
202
  if (args.command === "herdr") {
195
203
  const result = syncHerdrPlugin(packageRoot());
@@ -223,13 +231,17 @@ export async function main(argv = process.argv.slice(2)): Promise<number> {
223
231
  printJsonLines(runs);
224
232
  return 0;
225
233
  }
226
- if (args.command === "controllers" || args.command === "controller") {
234
+ if (args.command === "resource-managers" || args.command === "resource-manager") {
227
235
  const response = await client.request({
228
- operation: args.command === "controllers" ? "controller.list" : "controller.get",
236
+ operation:
237
+ args.command === "resource-managers" ? "resourceManager.list" : "resourceManager.get",
229
238
  payload: {
230
239
  projectPath: process.cwd(),
231
- ...(args.command === "controller"
232
- ? { controller: args.controllerName as string, key: args.resourceKey as string }
240
+ ...(args.command === "resource-manager"
241
+ ? {
242
+ resourceManager: args.resourceManagerName as string,
243
+ key: args.resourceKey as string,
244
+ }
233
245
  : {}),
234
246
  },
235
247
  });
@@ -298,19 +310,19 @@ async function runStateCommand(client: WorkflowClient, args: CliArgs): Promise<v
298
310
  process.stdout.write(`${JSON.stringify(response.receipt ?? {})}\n`);
299
311
  }
300
312
 
301
- async function runHost(
313
+ async function runServer(
302
314
  action: "start" | "status" | "stop" | "run",
303
315
  piArgs: string[] | undefined,
304
316
  ): Promise<number> {
305
317
  if (action === "run") {
306
- const { WorkflowHost } = await import("../host/runner.js");
307
- const host = new WorkflowHost({
318
+ const { WorkflowServer } = await import("../server/server.js");
319
+ const server = new WorkflowServer({
308
320
  piArgs: piArgs ?? [],
309
- onLog: (message) => process.stdout.write(`[host] ${message}\n`),
321
+ onLog: (message) => process.stdout.write(`[server] ${message}\n`),
310
322
  });
311
- await host.start();
323
+ await server.start();
312
324
  await new Promise<void>((resolve) => {
313
- const shutdown = () => void host.stop().then(resolve);
325
+ const shutdown = () => void server.stop().then(resolve);
314
326
  process.once("SIGTERM", shutdown);
315
327
  process.once("SIGINT", shutdown);
316
328
  });
@@ -321,7 +333,9 @@ async function runHost(
321
333
  const response =
322
334
  action === "start"
323
335
  ? await client.ensureRunning()
324
- : await client.request({ operation: action === "status" ? "host.status" : "host.stop" });
336
+ : await client.request({
337
+ operation: action === "status" ? "server.status" : "server.stop",
338
+ });
325
339
  process.stdout.write(`${JSON.stringify(response.receipt ?? {})}\n`);
326
340
  return response.outcome === "accepted" || response.outcome === "adopted" ? 0 : 1;
327
341
  } finally {
@@ -364,7 +378,7 @@ function printJsonLines(value: JsonValue): void {
364
378
 
365
379
  function requireAccepted(response: { outcome: string; error?: string }): void {
366
380
  if (response.outcome !== "accepted" && response.outcome !== "adopted") {
367
- throw new Error(response.error ?? `Workflow host returned ${response.outcome}`);
381
+ throw new Error(response.error ?? `Workflow server returned ${response.outcome}`);
368
382
  }
369
383
  }
370
384
 
package/src/viewer/tui.ts CHANGED
@@ -13,7 +13,7 @@ export type ViewerOptions = {
13
13
  runId?: string | undefined;
14
14
  };
15
15
 
16
- /** Interactive client view. All durable reads stay in the workflow host. */
16
+ /** Interactive client view. All durable reads stay in the workflow server. */
17
17
  export async function runViewer(options: ViewerOptions): Promise<void> {
18
18
  let value: JsonValue = options.runId === undefined ? [] : null;
19
19
  let selected = 0;
@@ -117,8 +117,8 @@ export async function runCommandBatch(
117
117
  }
118
118
  };
119
119
 
120
- const workerCount = Math.min(request.maxConcurrency, total);
121
- await Promise.all(Array.from({ length: workerCount }, worker));
120
+ const runnerCount = Math.min(request.maxConcurrency, total);
121
+ await Promise.all(Array.from({ length: runnerCount }, worker));
122
122
 
123
123
  for (let index = 0; index < total; index += 1) {
124
124
  if (results[index] !== undefined) continue;
@@ -589,15 +589,10 @@ function wrappedEffect(
589
589
  project: (context: WorkflowNodeContext) => WorkflowNodeContext,
590
590
  ): WorkflowManagedEffect | undefined {
591
591
  if (node.effect === undefined) return undefined;
592
- const idempotencyKey = node.effect.idempotencyKey;
593
592
  const request = node.effect.request;
594
593
  return {
595
594
  type: node.effect.type,
596
595
  recovery: node.effect.recovery,
597
- idempotencyKey:
598
- typeof idempotencyKey === "function"
599
- ? (context: WorkflowNodeContext) => idempotencyKey(project(context))
600
- : idempotencyKey,
601
596
  request:
602
597
  typeof request === "function"
603
598
  ? (context: WorkflowNodeContext) => request(project(context))
@@ -191,14 +191,6 @@ function workflowEffect(
191
191
  return {
192
192
  type: normalized,
193
193
  recovery,
194
- idempotencyKey: ({ state }: WorkflowNodeContext) => {
195
- const nodeId = state.currentNode;
196
- if (nodeId === undefined) {
197
- throw new Error("Managed effect resolution requires an active workflow node");
198
- }
199
- const nodeInvocation = state.steps.filter((step) => step.nodeId === nodeId).length + 1;
200
- return `${state.runId}:${normalized}:${nodeId}:${nodeInvocation}`;
201
- },
202
194
  request: ({ input, outputs }: WorkflowNodeContext) => ({ input, outputs }),
203
195
  };
204
196
  }