@osolmaz/pi-workflows 0.16.2 → 0.16.4

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 (315) 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 +1 -1
  16. package/dist/extension/index.d.ts +1 -1
  17. package/dist/extension/index.js +32 -32
  18. package/dist/extension/index.js.map +1 -1
  19. package/dist/extension/recorder.d.ts +1 -1
  20. package/dist/extension/recorder.js +1 -1
  21. package/dist/extension/recorder.js.map +1 -1
  22. package/dist/extension/remote-recorder-store.d.ts +1 -1
  23. package/dist/extension/remote-recorder-store.js +2 -2
  24. package/dist/extension/remote-recorder-store.js.map +1 -1
  25. package/dist/extension/{controller-command.d.ts → resource-manager-command.d.ts} +6 -6
  26. package/dist/extension/{controller-command.js → resource-manager-command.js} +7 -7
  27. package/dist/extension/resource-manager-command.js.map +1 -0
  28. package/dist/extension/session-delivery.d.ts +1 -1
  29. package/dist/extension/session-delivery.js +2 -2
  30. package/dist/extension/session-delivery.js.map +1 -1
  31. package/dist/extension/session-view.d.ts +1 -1
  32. package/dist/extension/session-view.js +1 -1
  33. package/dist/extension/session-view.js.map +1 -1
  34. package/dist/extension/workflow-message-coordinator.d.ts +1 -1
  35. package/dist/extension/workflow-message-coordinator.js +6 -6
  36. package/dist/extension/workflow-message-coordinator.js.map +1 -1
  37. package/dist/resource-managers/conditions.d.ts +6 -0
  38. package/dist/{controllers → resource-managers}/conditions.js +3 -3
  39. package/dist/resource-managers/conditions.js.map +1 -0
  40. package/dist/resource-managers/definition.d.ts +6 -0
  41. package/dist/resource-managers/definition.js +45 -0
  42. package/dist/resource-managers/definition.js.map +1 -0
  43. package/dist/resource-managers/effects.d.ts +15 -0
  44. package/dist/{controllers → resource-managers}/effects.js +2 -2
  45. package/dist/resource-managers/effects.js.map +1 -0
  46. package/dist/resource-managers/errors.d.ts +12 -0
  47. package/dist/resource-managers/errors.js +25 -0
  48. package/dist/resource-managers/errors.js.map +1 -0
  49. package/dist/resource-managers/index.d.ts +12 -0
  50. package/dist/resource-managers/index.js +12 -0
  51. package/dist/resource-managers/index.js.map +1 -0
  52. package/dist/resource-managers/json.js.map +1 -0
  53. package/dist/resource-managers/loader.d.ts +23 -0
  54. package/dist/resource-managers/loader.js +75 -0
  55. package/dist/resource-managers/loader.js.map +1 -0
  56. package/dist/resource-managers/results.d.ts +5 -0
  57. package/dist/resource-managers/results.js.map +1 -0
  58. package/dist/resource-managers/runtime.d.ts +59 -0
  59. package/dist/{controllers/manager.js → resource-managers/runtime.js} +57 -57
  60. package/dist/resource-managers/runtime.js.map +1 -0
  61. package/dist/{controllers → resource-managers}/sqlite.d.ts +44 -44
  62. package/dist/{controllers → resource-managers}/sqlite.js +139 -130
  63. package/dist/resource-managers/sqlite.js.map +1 -0
  64. package/dist/{controllers → resource-managers}/store.d.ts +33 -33
  65. package/dist/{controllers → resource-managers}/store.js.map +1 -1
  66. package/dist/{controllers → resource-managers}/types.d.ts +41 -41
  67. package/dist/{controllers → resource-managers}/types.js.map +1 -1
  68. package/dist/resource-managers/workflows.d.ts +34 -0
  69. package/dist/{controllers → resource-managers}/workflows.js +15 -15
  70. package/dist/resource-managers/workflows.js.map +1 -0
  71. package/dist/server/channel-effects.js.map +1 -0
  72. package/dist/{host → server}/channel-supervisor.d.ts +6 -6
  73. package/dist/{host → server}/channel-supervisor.js +3 -3
  74. package/dist/server/channel-supervisor.js.map +1 -0
  75. package/dist/{host/child-worker-supervisor.d.ts → server/child-runner-supervisor.d.ts} +15 -13
  76. package/dist/{host/child-worker-supervisor.js → server/child-runner-supervisor.js} +6 -5
  77. package/dist/server/child-runner-supervisor.js.map +1 -0
  78. package/dist/{host → server}/processes.d.ts +3 -3
  79. package/dist/{host → server}/processes.js +3 -3
  80. package/dist/server/processes.js.map +1 -0
  81. package/dist/{host → server}/resolver-entry.d.ts +6 -6
  82. package/dist/{host → server}/resolver-entry.js +14 -14
  83. package/dist/server/resolver-entry.js.map +1 -0
  84. package/dist/server/resource-runner-entry.d.ts +2 -0
  85. package/dist/{host/controller-worker-entry.js → server/resource-runner-entry.js} +33 -33
  86. package/dist/server/resource-runner-entry.js.map +1 -0
  87. package/dist/server/resource-runner-protocol.d.ts +36 -0
  88. package/dist/server/resource-runner-protocol.js +49 -0
  89. package/dist/server/resource-runner-protocol.js.map +1 -0
  90. package/dist/server/resource-runner-supervisor.d.ts +21 -0
  91. package/dist/{host/worker-supervisor.js → server/resource-runner-supervisor.js} +17 -17
  92. package/dist/server/resource-runner-supervisor.js.map +1 -0
  93. package/dist/{host → server}/rpc-bridge.d.ts +3 -3
  94. package/dist/{host → server}/rpc-bridge.js +3 -3
  95. package/dist/server/rpc-bridge.js.map +1 -0
  96. package/dist/{host → server}/rpc-executor.d.ts +1 -1
  97. package/dist/{host → server}/rpc-executor.js +3 -3
  98. package/dist/server/rpc-executor.js.map +1 -0
  99. package/dist/{host/host-entry.js → server/server-entry.js} +6 -6
  100. package/dist/server/server-entry.js.map +1 -0
  101. package/dist/{host/runner.d.ts → server/server.d.ts} +39 -38
  102. package/dist/{host/runner.js → server/server.js} +408 -374
  103. package/dist/server/server.js.map +1 -0
  104. package/dist/{host → server}/state.d.ts +25 -25
  105. package/dist/{host → server}/state.js +49 -49
  106. package/dist/server/state.js.map +1 -0
  107. package/dist/{host → server}/view.d.ts +7 -7
  108. package/dist/{host → server}/view.js +13 -13
  109. package/dist/server/view.js.map +1 -0
  110. package/dist/server/workflow-runner-content.d.ts +6 -0
  111. package/dist/server/workflow-runner-content.js +96 -0
  112. package/dist/server/workflow-runner-content.js.map +1 -0
  113. package/dist/{host/worker-entry.d.ts → server/workflow-runner-entry.d.ts} +3 -3
  114. package/dist/{host/worker-entry.js → server/workflow-runner-entry.js} +68 -35
  115. package/dist/server/workflow-runner-entry.js.map +1 -0
  116. package/dist/server/workflow-runner-protocol.d.ts +69 -0
  117. package/dist/server/workflow-runner-protocol.js +176 -0
  118. package/dist/server/workflow-runner-protocol.js.map +1 -0
  119. package/dist/{host/worker-store.d.ts → server/workflow-runner-store.d.ts} +11 -11
  120. package/dist/{host/worker-store.js → server/workflow-runner-store.js} +8 -8
  121. package/dist/server/workflow-runner-store.js.map +1 -0
  122. package/dist/server/workflow-runner-supervisor.d.ts +22 -0
  123. package/dist/server/workflow-runner-supervisor.js +56 -0
  124. package/dist/server/workflow-runner-supervisor.js.map +1 -0
  125. package/dist/state/prune.d.ts +1 -1
  126. package/dist/state/prune.js +17 -10
  127. package/dist/state/prune.js.map +1 -1
  128. package/dist/state/schema.js +2 -2
  129. package/dist/state/workflow-messages.d.ts +1 -1
  130. package/dist/state/workflow-messages.js +1 -1
  131. package/dist/state/workflow-messages.js.map +1 -1
  132. package/dist/viewer/backup.js +1 -1
  133. package/dist/viewer/backup.js.map +1 -1
  134. package/dist/viewer/cli.d.ts +2 -2
  135. package/dist/viewer/cli.js +46 -32
  136. package/dist/viewer/cli.js.map +1 -1
  137. package/dist/viewer/tui.d.ts +1 -1
  138. package/dist/viewer/tui.js +1 -1
  139. package/dist/viewer/tui.js.map +1 -1
  140. package/dist/workflows/command-batch.js +2 -2
  141. package/dist/workflows/engine.js +14 -15
  142. package/dist/workflows/engine.js.map +1 -1
  143. package/dist/workflows/store.d.ts +12 -11
  144. package/dist/workflows/store.js +24 -18
  145. package/dist/workflows/store.js.map +1 -1
  146. package/dist/workflows/types.d.ts +2 -2
  147. package/docs/2026-08-20-durable-workflow-launch-plan.md +12 -12
  148. package/docs/2026-08-25-workflow-follow-ups.md +7 -7
  149. package/docs/2026-08-25-workflow-settings.md +3 -3
  150. package/docs/2026-08-30-out-of-process-workflow-host-plan.md +3 -3
  151. package/docs/2026-09-01-restore-session-delivery-controls-plan.md +5 -5
  152. package/docs/2026-09-01-unified-workflow-client-plan.md +6 -6
  153. package/docs/2026-09-02-installed-live-e2e-plan.md +3 -3
  154. package/docs/2026-09-02-unify-workflow-messages-plan.md +8 -8
  155. package/docs/2026-09-04-workflow-run-state-plan.md +6 -6
  156. package/docs/DEFERRED_TURNS.md +7 -7
  157. package/docs/DESIGN_PHILOSOPHY.md +1 -1
  158. package/docs/HUMAN_DECISIONS.md +11 -11
  159. package/docs/HUMAN_DECISION_PRESENTATIONS.md +4 -4
  160. package/docs/MONITOR.md +1 -1
  161. package/docs/RESOURCE_MANAGERS.md +221 -0
  162. package/docs/SQLITE_STATE.md +35 -33
  163. package/docs/WORKFLOW_COMPOSITION.md +1 -1
  164. package/docs/{WORKFLOW_HOST.md → WORKFLOW_SERVER.md} +125 -119
  165. package/docs/WORKFLOW_STEP_MESSAGES.md +18 -18
  166. package/docs/WORKFLOW_UPDATES.md +16 -16
  167. package/docs/development.md +15 -14
  168. package/docs/live-replay-protocol.md +20 -20
  169. package/docs/plans/2026-08-04-controller-runtime-plan.md +25 -25
  170. package/docs/plans/2026-08-05-always-on-workflows-plan.md +8 -8
  171. package/docs/plans/2026-08-10-agent-managed-monitor-workflows-plan.md +2 -2
  172. package/docs/plans/2026-08-13-session-addressed-workflow-notifications-plan.md +1 -1
  173. package/docs/plans/2026-08-16-workflow-updates-plan.md +5 -5
  174. package/docs/plans/2026-08-19-human-decision-gates-plan.md +1 -1
  175. package/docs/plans/2026-08-20-bounded-command-batches-plan.md +1 -1
  176. package/docs/plans/2026-08-21-deferred-turn-intents-plan.md +3 -3
  177. package/docs/plans/2026-08-21-sanity-check-plan.md +2 -2
  178. package/docs/plans/2026-08-23-assistant-agent-completion-plan.md +5 -5
  179. package/docs/plans/2026-08-23-sqlite-state-plan.md +25 -25
  180. package/docs/plans/2026-08-25-live-workflow-settings-plan.md +4 -4
  181. package/docs/plans/2026-08-27-workflow-terminal-restart-plan.md +3 -3
  182. package/docs/plans/2026-09-04-workflow-runner-resume-state-plan.md +318 -0
  183. package/docs/plans/piw-viewer-experience-implementation-plan.md +1 -1
  184. package/docs/tui-viewer.md +9 -9
  185. package/docs/workflows.md +72 -66
  186. package/examples/{controllers/pull-request.controller.ts → resource-managers/pull-request.resource-manager.ts} +12 -12
  187. package/herdr-plugin.toml +1 -1
  188. package/package.json +12 -11
  189. package/protocol/client.v1.schema.json +7 -7
  190. package/protocol/fixtures/client-v1.json +1 -1
  191. package/src/builtins/monitor.workflow.ts +9 -9
  192. package/src/builtins/pi-agent-group.ts +3 -3
  193. package/src/channels/adapter-entry.ts +14 -14
  194. package/src/client/client.ts +33 -32
  195. package/src/client/protocol.ts +16 -11
  196. package/src/client/resolver.ts +4 -4
  197. package/src/client/view.ts +1 -1
  198. package/src/extension/index.ts +44 -34
  199. package/src/extension/recorder.ts +1 -1
  200. package/src/extension/remote-recorder-store.ts +2 -2
  201. package/src/extension/resource-manager-command.ts +45 -0
  202. package/src/extension/session-delivery.ts +2 -2
  203. package/src/extension/session-view.ts +1 -1
  204. package/src/extension/workflow-message-coordinator.ts +6 -6
  205. package/src/{controllers → resource-managers}/conditions.ts +23 -23
  206. package/src/resource-managers/definition.ts +71 -0
  207. package/src/{controllers → resource-managers}/effects.ts +9 -9
  208. package/src/resource-managers/errors.ts +27 -0
  209. package/src/resource-managers/index.ts +88 -0
  210. package/src/resource-managers/loader.ts +111 -0
  211. package/src/{controllers → resource-managers}/results.ts +4 -4
  212. package/src/{controllers/manager.ts → resource-managers/runtime.ts} +95 -92
  213. package/src/{controllers → resource-managers}/sqlite.ts +198 -186
  214. package/src/{controllers → resource-managers}/store.ts +46 -38
  215. package/src/{controllers → resource-managers}/types.ts +50 -41
  216. package/src/{controllers → resource-managers}/workflows.ts +43 -41
  217. package/src/{host → server}/channel-supervisor.ts +8 -8
  218. package/src/{host/child-worker-supervisor.ts → server/child-runner-supervisor.ts} +22 -18
  219. package/src/{host → server}/processes.ts +3 -3
  220. package/src/{host → server}/resolver-entry.ts +28 -25
  221. package/src/{host/controller-worker-entry.ts → server/resource-runner-entry.ts} +67 -65
  222. package/src/server/resource-runner-protocol.ts +103 -0
  223. package/src/server/resource-runner-supervisor.ts +76 -0
  224. package/src/{host → server}/rpc-bridge.ts +3 -3
  225. package/src/{host → server}/rpc-executor.ts +4 -4
  226. package/src/{host/host-entry.ts → server/server-entry.ts} +5 -5
  227. package/src/{host/runner.ts → server/server.ts} +526 -461
  228. package/src/{host → server}/state.ts +78 -67
  229. package/src/{host → server}/view.ts +15 -15
  230. package/src/server/workflow-runner-content.ts +119 -0
  231. package/src/{host/worker-entry.ts → server/workflow-runner-entry.ts} +98 -57
  232. package/src/server/workflow-runner-protocol.ts +241 -0
  233. package/src/{host/worker-store.ts → server/workflow-runner-store.ts} +18 -20
  234. package/src/server/workflow-runner-supervisor.ts +79 -0
  235. package/src/state/prune.ts +27 -9
  236. package/src/state/schema.ts +2 -2
  237. package/src/state/workflow-messages.ts +1 -1
  238. package/src/viewer/backup.ts +1 -1
  239. package/src/viewer/cli.ts +50 -36
  240. package/src/viewer/tui.ts +1 -1
  241. package/src/workflows/command-batch.ts +2 -2
  242. package/src/workflows/engine.ts +14 -15
  243. package/src/workflows/store.ts +31 -28
  244. package/src/workflows/types.ts +2 -2
  245. package/dist/controllers/conditions.d.ts +0 -6
  246. package/dist/controllers/conditions.js.map +0 -1
  247. package/dist/controllers/definition.d.ts +0 -6
  248. package/dist/controllers/definition.js +0 -45
  249. package/dist/controllers/definition.js.map +0 -1
  250. package/dist/controllers/effects.d.ts +0 -15
  251. package/dist/controllers/effects.js.map +0 -1
  252. package/dist/controllers/errors.d.ts +0 -12
  253. package/dist/controllers/errors.js +0 -25
  254. package/dist/controllers/errors.js.map +0 -1
  255. package/dist/controllers/index.d.ts +0 -12
  256. package/dist/controllers/index.js +0 -12
  257. package/dist/controllers/index.js.map +0 -1
  258. package/dist/controllers/json.js.map +0 -1
  259. package/dist/controllers/loader.d.ts +0 -23
  260. package/dist/controllers/loader.js +0 -74
  261. package/dist/controllers/loader.js.map +0 -1
  262. package/dist/controllers/manager.d.ts +0 -59
  263. package/dist/controllers/manager.js.map +0 -1
  264. package/dist/controllers/results.d.ts +0 -5
  265. package/dist/controllers/results.js.map +0 -1
  266. package/dist/controllers/sqlite.js.map +0 -1
  267. package/dist/controllers/workflows.d.ts +0 -34
  268. package/dist/controllers/workflows.js.map +0 -1
  269. package/dist/extension/controller-command.js.map +0 -1
  270. package/dist/host/channel-effects.js.map +0 -1
  271. package/dist/host/channel-supervisor.js.map +0 -1
  272. package/dist/host/child-worker-supervisor.js.map +0 -1
  273. package/dist/host/controller-worker-entry.d.ts +0 -2
  274. package/dist/host/controller-worker-entry.js.map +0 -1
  275. package/dist/host/controller-worker-protocol.d.ts +0 -36
  276. package/dist/host/controller-worker-protocol.js +0 -49
  277. package/dist/host/controller-worker-protocol.js.map +0 -1
  278. package/dist/host/controller-worker-supervisor.d.ts +0 -21
  279. package/dist/host/controller-worker-supervisor.js +0 -54
  280. package/dist/host/controller-worker-supervisor.js.map +0 -1
  281. package/dist/host/host-entry.js.map +0 -1
  282. package/dist/host/processes.js.map +0 -1
  283. package/dist/host/resolver-entry.js.map +0 -1
  284. package/dist/host/rpc-bridge.js.map +0 -1
  285. package/dist/host/rpc-executor.js.map +0 -1
  286. package/dist/host/runner.js.map +0 -1
  287. package/dist/host/state.js.map +0 -1
  288. package/dist/host/view.js.map +0 -1
  289. package/dist/host/worker-entry.js.map +0 -1
  290. package/dist/host/worker-protocol.d.ts +0 -46
  291. package/dist/host/worker-protocol.js +0 -122
  292. package/dist/host/worker-protocol.js.map +0 -1
  293. package/dist/host/worker-store.js.map +0 -1
  294. package/dist/host/worker-supervisor.d.ts +0 -22
  295. package/dist/host/worker-supervisor.js.map +0 -1
  296. package/docs/CONTROLLERS.md +0 -217
  297. package/src/controllers/definition.ts +0 -65
  298. package/src/controllers/errors.ts +0 -27
  299. package/src/controllers/index.ts +0 -88
  300. package/src/controllers/loader.ts +0 -104
  301. package/src/extension/controller-command.ts +0 -45
  302. package/src/host/controller-worker-protocol.ts +0 -104
  303. package/src/host/controller-worker-supervisor.ts +0 -79
  304. package/src/host/worker-protocol.ts +0 -176
  305. package/src/host/worker-supervisor.ts +0 -74
  306. /package/dist/{controllers → resource-managers}/json.d.ts +0 -0
  307. /package/dist/{controllers → resource-managers}/json.js +0 -0
  308. /package/dist/{controllers → resource-managers}/results.js +0 -0
  309. /package/dist/{controllers → resource-managers}/store.js +0 -0
  310. /package/dist/{controllers → resource-managers}/types.js +0 -0
  311. /package/dist/{host → server}/channel-effects.d.ts +0 -0
  312. /package/dist/{host → server}/channel-effects.js +0 -0
  313. /package/dist/{host/host-entry.d.ts → server/server-entry.d.ts} +0 -0
  314. /package/src/{controllers → resource-managers}/json.ts +0 -0
  315. /package/src/{host → server}/channel-effects.ts +0 -0
@@ -17,10 +17,10 @@ import {
17
17
  decisionWorkflowMessageContent,
18
18
  stepWorkflowMessageContent,
19
19
  } from "../workflows/workflow-message-content.js";
20
- import type { WorkerMessage, WorkerResponse } from "./worker-protocol.js";
20
+ import type { WorkflowRunnerMessage, WorkflowRunnerResponse } from "./workflow-runner-protocol.js";
21
21
 
22
- export type HostClaim = {
23
- hostId: string;
22
+ export type ServerClaim = {
23
+ serverId: string;
24
24
  token: string;
25
25
  epoch: number;
26
26
  pid: number;
@@ -28,9 +28,9 @@ export type HostClaim = {
28
28
  expiresAt: number;
29
29
  };
30
30
 
31
- export type HostStatusRecord = {
31
+ export type WorkflowServerStatusRecord = {
32
32
  epoch: number;
33
- hostId: string | null;
33
+ serverId: string | null;
34
34
  pid: number | null;
35
35
  processStartIdentity: string | null;
36
36
  startedAt: string | null;
@@ -39,11 +39,11 @@ export type HostStatusRecord = {
39
39
  live: boolean;
40
40
  };
41
41
 
42
- export type WorkerLaunchEnvelope = {
42
+ export type WorkflowRunnerLaunchEnvelope = {
43
43
  schema: "pi-workflows.worker-launch.v1";
44
44
  runId: string;
45
45
  generation: number;
46
- workerEpoch: string;
46
+ runnerEpoch: string;
47
47
  projectPath: string;
48
48
  workflowSource: JsonValue;
49
49
  definitionDigest: string;
@@ -51,7 +51,7 @@ export type WorkerLaunchEnvelope = {
51
51
  protocolVersion: 1;
52
52
  };
53
53
 
54
- export type WorkerOutcome =
54
+ export type RunnerOutcome =
55
55
  | "exited"
56
56
  | "cancelled"
57
57
  | "timedOut"
@@ -86,7 +86,7 @@ export type InteractiveRequestRecord = {
86
86
  consumedAt: string | null;
87
87
  };
88
88
 
89
- export class HostStateStore {
89
+ export class ServerStateStore {
90
90
  readonly state: StateDatabase;
91
91
  private readonly ownsState: boolean;
92
92
  readonly workflowMessages: WorkflowMessageStore;
@@ -106,19 +106,19 @@ export class HostStateStore {
106
106
  if (this.ownsState) this.state.close();
107
107
  }
108
108
 
109
- acquireHost(options: {
110
- hostId: string;
109
+ acquireServer(options: {
110
+ serverId: string;
111
111
  pid: number;
112
112
  processStartIdentity: string;
113
113
  leaseMs: number;
114
114
  now?: number;
115
- }): HostClaim {
115
+ }): ServerClaim {
116
116
  const now = options.now ?? Date.now();
117
117
  requireLeaseMs(options.leaseMs);
118
118
  return this.state.transaction(() => {
119
- const current = this.hostRow();
120
- if (current.hostId !== null && current.expiresAt !== null && current.expiresAt > now) {
121
- throw new Error(`A live Pi Workflows host already owns epoch ${current.epoch}`);
119
+ const current = this.serverRow();
120
+ if (current.serverId !== null && current.expiresAt !== null && current.expiresAt > now) {
121
+ throw new Error(`A live Pi Workflows server already owns epoch ${current.epoch}`);
122
122
  }
123
123
  const token = randomBytes(32).toString("base64url");
124
124
  const epoch = current.epoch + 1;
@@ -132,7 +132,7 @@ export class HostStateStore {
132
132
  )
133
133
  .run(
134
134
  epoch,
135
- options.hostId,
135
+ options.serverId,
136
136
  tokenHash(token),
137
137
  options.pid,
138
138
  options.processStartIdentity,
@@ -142,9 +142,10 @@ export class HostStateStore {
142
142
  current.epoch,
143
143
  now,
144
144
  );
145
- if (changed.changes !== 1) throw new Error("Pi Workflows host claim changed during startup");
145
+ if (changed.changes !== 1)
146
+ throw new Error("Pi Workflows server claim changed during startup");
146
147
  return {
147
- hostId: options.hostId,
148
+ serverId: options.serverId,
148
149
  token,
149
150
  epoch,
150
151
  pid: options.pid,
@@ -154,7 +155,7 @@ export class HostStateStore {
154
155
  });
155
156
  }
156
157
 
157
- renewHost(claim: HostClaim, leaseMs: number, now: number = Date.now()): HostClaim {
158
+ renewServer(claim: ServerClaim, leaseMs: number, now: number = Date.now()): ServerClaim {
158
159
  requireLeaseMs(leaseMs);
159
160
  return this.state.transaction(() => {
160
161
  const expiresAt = now + leaseMs;
@@ -168,18 +169,18 @@ export class HostStateStore {
168
169
  now,
169
170
  expiresAt,
170
171
  claim.epoch,
171
- claim.hostId,
172
+ claim.serverId,
172
173
  tokenHash(claim.token),
173
174
  claim.pid,
174
175
  claim.processStartIdentity,
175
176
  now,
176
177
  );
177
- if (changed.changes !== 1) throw new Error("Pi Workflows host claim lost");
178
+ if (changed.changes !== 1) throw new Error("Pi Workflows server claim lost");
178
179
  return { ...claim, expiresAt };
179
180
  });
180
181
  }
181
182
 
182
- releaseHost(claim: HostClaim, _now: number = Date.now()): boolean {
183
+ releaseServer(claim: ServerClaim, _now: number = Date.now()): boolean {
183
184
  return this.state.transaction(
184
185
  () =>
185
186
  this.state.connection
@@ -192,7 +193,7 @@ export class HostStateStore {
192
193
  )
193
194
  .run(
194
195
  claim.epoch,
195
- claim.hostId,
196
+ claim.serverId,
196
197
  tokenHash(claim.token),
197
198
  claim.pid,
198
199
  claim.processStartIdentity,
@@ -200,17 +201,17 @@ export class HostStateStore {
200
201
  );
201
202
  }
202
203
 
203
- hostStatus(now: number = Date.now()): HostStatusRecord {
204
- const row = this.hostRow();
204
+ serverStatus(now: number = Date.now()): WorkflowServerStatusRecord {
205
+ const row = this.serverRow();
205
206
  return {
206
207
  epoch: row.epoch,
207
- hostId: row.hostId,
208
+ serverId: row.serverId,
208
209
  pid: row.pid,
209
210
  processStartIdentity: row.processStartIdentity,
210
211
  startedAt: iso(row.startedAt),
211
212
  heartbeatAt: iso(row.heartbeatAt),
212
213
  expiresAt: iso(row.expiresAt),
213
- live: row.hostId !== null && row.expiresAt !== null && row.expiresAt > now,
214
+ live: row.serverId !== null && row.expiresAt !== null && row.expiresAt > now,
214
215
  };
215
216
  }
216
217
 
@@ -260,7 +261,7 @@ export class HostStateStore {
260
261
 
261
262
  executeCommand(
262
263
  request: ClientRequest,
263
- hostEpoch: number,
264
+ serverEpoch: number,
264
265
  operation: () => Omit<ClientResponse, "schema" | "type" | "requestId">,
265
266
  ): ClientResponse {
266
267
  const existing = this.adoptCommand(request);
@@ -300,7 +301,7 @@ export class HostStateStore {
300
301
  response.outcome,
301
302
  receiptHash,
302
303
  errorHash,
303
- hostEpoch,
304
+ serverEpoch,
304
305
  now,
305
306
  now,
306
307
  );
@@ -308,7 +309,7 @@ export class HostStateStore {
308
309
  });
309
310
  }
310
311
 
311
- recordWorkerStart(envelope: WorkerLaunchEnvelope, hostEpoch: number): void {
312
+ recordRunnerStart(envelope: WorkflowRunnerLaunchEnvelope, serverEpoch: number): void {
312
313
  const now = Date.now();
313
314
  this.state.transaction(() => {
314
315
  const launchHash = this.state.putJson(envelope, now);
@@ -319,34 +320,41 @@ export class HostStateStore {
319
320
  status, started_at
320
321
  ) VALUES (?, ?, ?, ?, ?, 'starting', ?)`,
321
322
  )
322
- .run(envelope.workerEpoch, envelope.runId, envelope.generation, hostEpoch, launchHash, now);
323
+ .run(
324
+ envelope.runnerEpoch,
325
+ envelope.runId,
326
+ envelope.generation,
327
+ serverEpoch,
328
+ launchHash,
329
+ now,
330
+ );
323
331
  });
324
332
  }
325
333
 
326
- attachWorkerProcess(workerEpoch: string, pid: number, processStartIdentity: string): void {
334
+ attachRunnerProcess(runnerEpoch: string, pid: number, processStartIdentity: string): void {
327
335
  const changed = this.state.connection
328
336
  .prepare(
329
337
  `UPDATE run_workers SET pid = ?, process_start_identity = ?
330
338
  WHERE worker_epoch = ? AND status = 'starting' AND pid IS NULL`,
331
339
  )
332
- .run(pid, processStartIdentity, workerEpoch);
333
- if (changed.changes !== 1) throw new Error(`Worker epoch is not starting: ${workerEpoch}`);
340
+ .run(pid, processStartIdentity, runnerEpoch);
341
+ if (changed.changes !== 1) throw new Error(`Runner epoch is not starting: ${runnerEpoch}`);
334
342
  }
335
343
 
336
- markWorkerReady(workerEpoch: string): void {
344
+ markRunnerReady(runnerEpoch: string): void {
337
345
  const now = Date.now();
338
346
  const changed = this.state.connection
339
347
  .prepare(
340
348
  `UPDATE run_workers SET status = 'running', ready_at = ?
341
349
  WHERE worker_epoch = ? AND status = 'starting'`,
342
350
  )
343
- .run(now, workerEpoch);
344
- if (changed.changes !== 1) throw new Error(`Worker epoch cannot become ready: ${workerEpoch}`);
351
+ .run(now, runnerEpoch);
352
+ if (changed.changes !== 1) throw new Error(`Runner epoch cannot become ready: ${runnerEpoch}`);
345
353
  }
346
354
 
347
- finishWorker(options: {
348
- workerEpoch: string;
349
- outcome: WorkerOutcome;
355
+ finishRunner(options: {
356
+ runnerEpoch: string;
357
+ outcome: RunnerOutcome;
350
358
  exitCode?: number | null;
351
359
  signal?: string | null;
352
360
  diagnostic?: string;
@@ -367,36 +375,39 @@ export class HostStateStore {
367
375
  options.exitCode ?? null,
368
376
  options.signal ?? null,
369
377
  diagnosticHash,
370
- options.workerEpoch,
378
+ options.runnerEpoch,
371
379
  );
372
380
  if (changed.changes !== 1)
373
- throw new Error(`Worker epoch is not active: ${options.workerEpoch}`);
381
+ throw new Error(`Runner epoch is not active: ${options.runnerEpoch}`);
374
382
  });
375
383
  }
376
384
 
377
- readWorkerMessage(message: WorkerMessage): WorkerResponse | undefined {
385
+ readRunnerMessage(message: WorkflowRunnerMessage): WorkflowRunnerResponse | undefined {
378
386
  const row = this.state.connection
379
387
  .prepare(
380
388
  `SELECT request_fingerprint AS clientRequestFingerprint, outcome,
381
389
  accepted_revision AS revision, result_hash AS resultHash, error_hash AS errorHash
382
390
  FROM worker_messages WHERE worker_epoch = ? AND message_id = ?`,
383
391
  )
384
- .get(message.workerEpoch, message.messageId);
385
- if (!isWorkerMessageRow(row)) return undefined;
386
- if (!row.clientRequestFingerprint.equals(workerMessageFingerprint(message))) {
392
+ .get(message.runnerEpoch, message.messageId);
393
+ if (!isRunnerMessageRow(row)) return undefined;
394
+ if (!row.clientRequestFingerprint.equals(runnerMessageFingerprint(message))) {
387
395
  return {
388
396
  schema: "pi-workflows.worker-response.v1",
389
397
  messageId: message.messageId,
390
398
  outcome: "rejected",
391
- error: "Worker message ID was reused with another payload",
399
+ error: "Runner message ID was reused with another payload",
392
400
  };
393
401
  }
394
- return workerMessageResponse(this.state, message.messageId, row);
402
+ return runnerMessageResponse(this.state, message.messageId, row);
395
403
  }
396
404
 
397
- recordWorkerMessage(message: WorkerMessage, response: WorkerResponse): WorkerResponse {
405
+ recordRunnerMessage(
406
+ message: WorkflowRunnerMessage,
407
+ response: WorkflowRunnerResponse,
408
+ ): WorkflowRunnerResponse {
398
409
  return this.state.transaction(() => {
399
- const existing = this.readWorkerMessage(message);
410
+ const existing = this.readRunnerMessage(message);
400
411
  if (existing !== undefined) return existing;
401
412
  const now = Date.now();
402
413
  const resultHash =
@@ -411,9 +422,9 @@ export class HostStateStore {
411
422
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
412
423
  )
413
424
  .run(
414
- message.workerEpoch,
425
+ message.runnerEpoch,
415
426
  message.messageId,
416
- workerMessageFingerprint(message),
427
+ runnerMessageFingerprint(message),
417
428
  response.outcome,
418
429
  response.revision ?? null,
419
430
  resultHash,
@@ -1055,16 +1066,16 @@ export class HostStateStore {
1055
1066
  });
1056
1067
  }
1057
1068
 
1058
- private hostRow(): HostRow {
1069
+ private serverRow(): ServerRow {
1059
1070
  const row = this.state.connection
1060
1071
  .prepare(
1061
- `SELECT epoch, host_id AS hostId, token_hash AS tokenHash, pid,
1072
+ `SELECT epoch, host_id AS serverId, token_hash AS tokenHash, pid,
1062
1073
  process_start_identity AS processStartIdentity, started_at AS startedAt,
1063
1074
  heartbeat_at AS heartbeatAt, expires_at AS expiresAt
1064
1075
  FROM workflow_host_state WHERE id = 1`,
1065
1076
  )
1066
1077
  .get();
1067
- if (!isHostRow(row)) throw new Error("Pi Workflows host state is missing");
1078
+ if (!isServerRow(row)) throw new Error("Pi Workflows server state is missing");
1068
1079
  return row;
1069
1080
  }
1070
1081
 
@@ -1151,16 +1162,16 @@ function conflictResponse(requestId: string, error: string): ClientResponse {
1151
1162
 
1152
1163
  function requireLeaseMs(value: number): void {
1153
1164
  if (!Number.isSafeInteger(value) || value <= 0)
1154
- throw new Error("Host lease duration must be positive");
1165
+ throw new Error("Server lease duration must be positive");
1155
1166
  }
1156
1167
 
1157
1168
  function iso(value: number | null): string | null {
1158
1169
  return value === null ? null : new Date(value).toISOString();
1159
1170
  }
1160
1171
 
1161
- type HostRow = {
1172
+ type ServerRow = {
1162
1173
  epoch: number;
1163
- hostId: string | null;
1174
+ serverId: string | null;
1164
1175
  tokenHash: Buffer | null;
1165
1176
  pid: number | null;
1166
1177
  processStartIdentity: string | null;
@@ -1196,9 +1207,9 @@ type SubmissionDetailRow = {
1196
1207
  receiptHash: Buffer | null;
1197
1208
  submittedAt: number;
1198
1209
  };
1199
- type WorkerMessageRow = {
1210
+ type WorkflowRunnerMessageRow = {
1200
1211
  clientRequestFingerprint: Buffer;
1201
- outcome: WorkerResponse["outcome"];
1212
+ outcome: WorkflowRunnerResponse["outcome"];
1202
1213
  revision: number | null;
1203
1214
  resultHash: Buffer | null;
1204
1215
  errorHash: Buffer | null;
@@ -1249,11 +1260,11 @@ type InteractiveRequestRow = {
1249
1260
  consumedAt: number | null;
1250
1261
  };
1251
1262
 
1252
- function isHostRow(value: unknown): value is HostRow {
1263
+ function isServerRow(value: unknown): value is ServerRow {
1253
1264
  return (
1254
1265
  isRecord(value) &&
1255
1266
  typeof value.epoch === "number" &&
1256
- nullableString(value.hostId) &&
1267
+ nullableString(value.serverId) &&
1257
1268
  (value.tokenHash === null || Buffer.isBuffer(value.tokenHash)) &&
1258
1269
  (value.pid === null || typeof value.pid === "number") &&
1259
1270
  nullableString(value.processStartIdentity) &&
@@ -1290,7 +1301,7 @@ function isRunIdRow(value: unknown): value is RunIdRow {
1290
1301
  return isRecord(value) && typeof value.runId === "string";
1291
1302
  }
1292
1303
 
1293
- function isWorkerMessageRow(value: unknown): value is WorkerMessageRow {
1304
+ function isRunnerMessageRow(value: unknown): value is WorkflowRunnerMessageRow {
1294
1305
  return (
1295
1306
  isRecord(value) &&
1296
1307
  Buffer.isBuffer(value.clientRequestFingerprint) &&
@@ -1401,15 +1412,15 @@ function isInteractiveRequestRow(value: unknown): value is InteractiveRequestRow
1401
1412
  );
1402
1413
  }
1403
1414
 
1404
- function workerMessageFingerprint(message: WorkerMessage): Buffer {
1415
+ function runnerMessageFingerprint(message: WorkflowRunnerMessage): Buffer {
1405
1416
  return createHash("sha256").update(canonicalJson(message)).digest();
1406
1417
  }
1407
1418
 
1408
- function workerMessageResponse(
1419
+ function runnerMessageResponse(
1409
1420
  state: StateDatabase,
1410
1421
  messageId: string,
1411
- row: WorkerMessageRow,
1412
- ): WorkerResponse {
1422
+ row: WorkflowRunnerMessageRow,
1423
+ ): WorkflowRunnerResponse {
1413
1424
  const result = row.resultHash === null ? undefined : state.readJson(row.resultHash);
1414
1425
  const error =
1415
1426
  row.errorHash === null ? undefined : state.readBlob(row.errorHash)?.content.toString("utf8");
@@ -11,10 +11,10 @@ import {
11
11
  type WorkflowSessionView,
12
12
  } from "../client/view.js";
13
13
  import type {
14
- SqliteControllerStore,
14
+ SqliteResourceManagerStore,
15
15
  WorkflowRunQueueRecord,
16
16
  WorkflowRunQueueViewRecord,
17
- } from "../controllers/sqlite.js";
17
+ } from "../resource-managers/sqlite.js";
18
18
  import type { StateDatabase } from "../state/database.js";
19
19
  import { canonicalJson, parseJson, type JsonValue } from "../state/json.js";
20
20
  import { WorkflowMessageStore, type WorkflowMessage } from "../state/workflow-messages.js";
@@ -27,7 +27,7 @@ import type {
27
27
  WorkflowTraceEvent,
28
28
  WorkflowUpdateRecord,
29
29
  } from "../workflows/types.js";
30
- import type { HostStateStore } from "./state.js";
30
+ import type { ServerStateStore } from "./state.js";
31
31
 
32
32
  export const WORKFLOW_PAGE_KINDS = [
33
33
  "steps",
@@ -63,7 +63,7 @@ const CONTENT_CACHE_BYTES = 64 * 1024 * 1024;
63
63
  const VIEW_CACHE_ITEMS = 64;
64
64
  const TERMINAL_VIEW_RETENTION_MS = 60_000;
65
65
 
66
- export class HostViewStore {
66
+ export class ServerViewStore {
67
67
  private readonly contentRecords = new Map<string, ContentRecord>();
68
68
  private readonly listCache = new Map<string, { revision: string; page: WorkflowRunListPage }>();
69
69
  private readonly runCache = new Map<string, { version: string; view: WorkflowRunView | null }>();
@@ -74,13 +74,13 @@ export class HostViewStore {
74
74
 
75
75
  constructor(
76
76
  private readonly state: StateDatabase,
77
- private readonly queue: SqliteControllerStore,
78
- private readonly hostState: HostStateStore,
77
+ private readonly queue: SqliteResourceManagerStore,
78
+ private readonly serverState: ServerStateStore,
79
79
  private readonly runs: WorkflowRunStore,
80
- private readonly hasLiveWorker: (runId: string) => boolean,
80
+ private readonly hasLiveRunner: (runId: string) => boolean,
81
81
  private readonly hasActiveSessionTurn: (targetSessionId: string) => boolean,
82
82
  ) {
83
- this.workflowMessages = hostState.workflowMessages;
83
+ this.workflowMessages = serverState.workflowMessages;
84
84
  }
85
85
 
86
86
  noteOriginActivityChange(): void {
@@ -324,7 +324,7 @@ export class HostViewStore {
324
324
  const retainedRunId =
325
325
  activeQueue === undefined ? this.retainedTerminalRunId(sessionId) : undefined;
326
326
  const runId = activeQueue?.runId ?? retainedRunId;
327
- const pending = this.hostState.listPendingInteractions(sessionId);
327
+ const pending = this.serverState.listPendingInteractions(sessionId);
328
328
  const pendingInteractions = byteBoundedForwardPage(pending, (request) =>
329
329
  this.projectRecordField(request.runId, request, "contract"),
330
330
  );
@@ -613,7 +613,7 @@ export class HostViewStore {
613
613
  }
614
614
 
615
615
  clearConnection(_connectionId: string): void {
616
- // Coordinator fencing is process-local in the host. Durable turn state is
616
+ // Coordinator fencing is process-local in the server. Durable turn state is
617
617
  // reconciled from the Pi branch after the next coordinator connects.
618
618
  }
619
619
 
@@ -792,7 +792,7 @@ export class HostViewStore {
792
792
  durableStatus: state?.status,
793
793
  paused: state?.paused === true,
794
794
  ambiguous: this.hasAmbiguousEffect(queue.runId),
795
- workerActive: this.hasLiveWorker(queue.runId),
795
+ runnerActive: this.hasLiveRunner(queue.runId),
796
796
  originTurnActive: this.hasActivity(queue.runId),
797
797
  pendingInteraction: this.hasPendingInteraction(queue.runId),
798
798
  errorMessage: state?.error ?? queue.errorMessage,
@@ -855,7 +855,7 @@ export class HostViewStore {
855
855
  row.runStatus,
856
856
  row.paused,
857
857
  this.workflowActivityRevision(),
858
- this.hasLiveWorker(runId),
858
+ this.hasLiveRunner(runId),
859
859
  this.hasActivity(runId),
860
860
  this.hasPendingInteraction(runId),
861
861
  this.hasAmbiguousEffect(runId),
@@ -888,7 +888,7 @@ export type WorkflowDisplayFacts = {
888
888
  durableStatus: WorkflowRunState["status"] | undefined;
889
889
  paused: boolean;
890
890
  ambiguous: boolean;
891
- workerActive: boolean;
891
+ runnerActive: boolean;
892
892
  originTurnActive: boolean;
893
893
  pendingInteraction: boolean;
894
894
  errorMessage: string | null;
@@ -902,9 +902,9 @@ export function reduceWorkflowDisplay(facts: WorkflowDisplayFacts): WorkflowDisp
902
902
  if (facts.ambiguous) {
903
903
  status = "ambiguous";
904
904
  reason = "An external effect needs explicit recovery.";
905
- } else if (facts.workerActive || facts.originTurnActive) {
905
+ } else if (facts.runnerActive || facts.originTurnActive) {
906
906
  status = "running";
907
- activity = facts.workerActive ? "supervised_worker" : "origin_turn";
907
+ activity = facts.runnerActive ? "supervised_runner" : "origin_turn";
908
908
  } else if (
909
909
  facts.durableStatus === "completed" ||
910
910
  facts.durableStatus === "failed" ||
@@ -0,0 +1,119 @@
1
+ import { createHash } from "node:crypto";
2
+ import type { StateDatabase } from "../state/database.js";
3
+ import { canonicalJson, parseJson, type JsonValue } from "../state/json.js";
4
+ import {
5
+ WORKFLOW_RUNNER_CONTENT_CHUNK_BYTES,
6
+ WORKFLOW_RUNNER_CONTENT_CHUNK_SCHEMA,
7
+ WORKFLOW_RUNNER_CONTENT_REFERENCE_SCHEMA,
8
+ encodeRunnerLine,
9
+ type WorkflowRunnerContentChunk,
10
+ type WorkflowRunnerContentReference,
11
+ type WorkflowRunnerResponse,
12
+ } from "./workflow-runner-protocol.js";
13
+
14
+ const CONTENT_MEDIA_TYPE = "application/json" as const;
15
+
16
+ export function boundRunnerResponse(
17
+ state: StateDatabase,
18
+ allowedDigests: Set<string>,
19
+ response: WorkflowRunnerResponse,
20
+ ): WorkflowRunnerResponse {
21
+ try {
22
+ encodeRunnerLine(response);
23
+ return response;
24
+ } catch {
25
+ if (response.result === undefined) return responseTooLarge(response.messageId);
26
+ try {
27
+ const content = Buffer.from(canonicalJson(response.result), "utf8");
28
+ const sha256 = state.putBlob(content, CONTENT_MEDIA_TYPE).toString("hex");
29
+ allowedDigests.add(sha256);
30
+ const referenced: WorkflowRunnerResponse = {
31
+ ...response,
32
+ result: {
33
+ schema: WORKFLOW_RUNNER_CONTENT_REFERENCE_SCHEMA,
34
+ sha256,
35
+ mediaType: CONTENT_MEDIA_TYPE,
36
+ bytes: content.byteLength,
37
+ },
38
+ };
39
+ encodeRunnerLine(referenced);
40
+ return referenced;
41
+ } catch {
42
+ return responseTooLarge(response.messageId);
43
+ }
44
+ }
45
+ }
46
+
47
+ export function readRunnerContentChunk(
48
+ state: StateDatabase,
49
+ allowedDigests: ReadonlySet<string>,
50
+ sha256: string,
51
+ offset: number,
52
+ ): WorkflowRunnerContentChunk {
53
+ if (!/^[0-9a-f]{64}$/u.test(sha256) || !allowedDigests.has(sha256)) {
54
+ throw new Error("Workflow runner content is unavailable");
55
+ }
56
+ if (!Number.isSafeInteger(offset) || offset < 0) {
57
+ throw new Error("Workflow runner content offset is invalid");
58
+ }
59
+ const stored = state.readBlob(Buffer.from(sha256, "hex"));
60
+ if (stored === undefined || stored.mediaType !== CONTENT_MEDIA_TYPE) {
61
+ throw new Error("Workflow runner content is unavailable");
62
+ }
63
+ if (offset > stored.byteLength) throw new Error("Workflow runner content offset is invalid");
64
+ const nextOffset = Math.min(offset + WORKFLOW_RUNNER_CONTENT_CHUNK_BYTES, stored.byteLength);
65
+ return {
66
+ schema: WORKFLOW_RUNNER_CONTENT_CHUNK_SCHEMA,
67
+ sha256,
68
+ mediaType: CONTENT_MEDIA_TYPE,
69
+ bytes: stored.byteLength,
70
+ offset,
71
+ nextOffset,
72
+ complete: nextOffset === stored.byteLength,
73
+ data: stored.content.subarray(offset, nextOffset).toString("base64url"),
74
+ };
75
+ }
76
+
77
+ export async function materializeRunnerContent(
78
+ reference: WorkflowRunnerContentReference,
79
+ readChunk: (offset: number) => Promise<WorkflowRunnerContentChunk>,
80
+ ): Promise<JsonValue> {
81
+ const parts: Buffer[] = [];
82
+ let offset = 0;
83
+ for (;;) {
84
+ const chunk = await readChunk(offset);
85
+ const bytes = Buffer.from(chunk.data, "base64url");
86
+ if (
87
+ chunk.sha256 !== reference.sha256 ||
88
+ chunk.mediaType !== reference.mediaType ||
89
+ chunk.bytes !== reference.bytes ||
90
+ chunk.offset !== offset ||
91
+ chunk.nextOffset !== offset + bytes.byteLength ||
92
+ chunk.nextOffset > reference.bytes ||
93
+ chunk.complete !== (chunk.nextOffset === reference.bytes)
94
+ ) {
95
+ throw new Error("Workflow runner content chunk is invalid");
96
+ }
97
+ parts.push(bytes);
98
+ offset = chunk.nextOffset;
99
+ if (chunk.complete) break;
100
+ if (bytes.byteLength === 0) throw new Error("Workflow runner content read made no progress");
101
+ }
102
+ const content = Buffer.concat(parts);
103
+ if (
104
+ content.byteLength !== reference.bytes ||
105
+ createHash("sha256").update(content).digest("hex") !== reference.sha256
106
+ ) {
107
+ throw new Error("Workflow runner content does not match its reference");
108
+ }
109
+ return parseJson(content.toString("utf8"));
110
+ }
111
+
112
+ function responseTooLarge(messageId: string): WorkflowRunnerResponse {
113
+ return {
114
+ schema: "pi-workflows.worker-response.v1",
115
+ messageId,
116
+ outcome: "rejected",
117
+ error: "Workflow runner response could not be transferred within the protocol limit",
118
+ };
119
+ }