@osolmaz/pi-workflows 0.16.2 → 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 (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 +5 -5
  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
@@ -0,0 +1,45 @@
1
+ import type { JsonValue } from "../state/json.js";
2
+
3
+ export type ParsedResourceManagerArgs =
4
+ | { kind: "list" }
5
+ | { kind: "get"; resourceManager: string; key: string }
6
+ | { kind: "apply"; resourceManager: string; key: string; spec: JsonValue }
7
+ | { kind: "reconcile"; resourceManager: string; key: string }
8
+ | { kind: "delete"; resourceManager: string; key: string };
9
+
10
+ const RESOURCE_MANAGER_USAGE =
11
+ "Usage: /resource-manager [list|get <resource-manager> <key>|apply <resource-manager> <key> <json>|reconcile <resource-manager> <key>|delete <resource-manager> <key>]";
12
+
13
+ export function parseResourceManagerArgs(args: string): ParsedResourceManagerArgs {
14
+ const trimmed = args.trim();
15
+ if (trimmed.length === 0 || trimmed === "list") return { kind: "list" };
16
+
17
+ const apply = trimmed.match(/^apply\s+(\S+)\s+(\S+)\s+([\s\S]+)$/u);
18
+ if (apply !== null) {
19
+ let spec: JsonValue;
20
+ try {
21
+ spec = JSON.parse(apply[3] as string) as JsonValue;
22
+ } catch (error) {
23
+ throw new Error(
24
+ `Invalid resource manager spec JSON: ${error instanceof Error ? error.message : String(error)}`,
25
+ );
26
+ }
27
+ return {
28
+ kind: "apply",
29
+ resourceManager: apply[1] as string,
30
+ key: apply[2] as string,
31
+ spec,
32
+ };
33
+ }
34
+
35
+ const operation = trimmed.match(/^(get|reconcile|delete)\s+(\S+)\s+(\S+)$/u);
36
+ if (operation !== null) {
37
+ return {
38
+ kind: operation[1] as "get" | "reconcile" | "delete",
39
+ resourceManager: operation[2] as string,
40
+ key: operation[3] as string,
41
+ };
42
+ }
43
+
44
+ throw new Error(RESOURCE_MANAGER_USAGE);
45
+ }
@@ -20,7 +20,7 @@ type QueuedSessionDelivery = {
20
20
  const SESSION_ENTRY_CONFIRMATION_MS = 10_000;
21
21
 
22
22
  /**
23
- * Delivers at most one host-owned message into a Pi session at a time.
23
+ * Delivers at most one server-owned message into a Pi session at a time.
24
24
  *
25
25
  * Pi's public sendMessage API does not return the saved session entry ID. Keep
26
26
  * the claimed delivery in memory until that entry becomes observable. Polling
@@ -54,7 +54,7 @@ export class SessionDeliveryCoordinator {
54
54
  return;
55
55
  }
56
56
 
57
- // Remember the host claim before the final idle check. If Pi starts a
57
+ // Remember the server claim before the final idle check. If Pi starts a
58
58
  // turn while the claim request is in flight, a later poll can use this
59
59
  // exact still-live claim instead of making a conflicting second claim.
60
60
  this.claimed = delivery;
@@ -10,7 +10,7 @@ import { buildWidgetView } from "./widget.js";
10
10
  const WIDGET_KEY = "pi-workflows";
11
11
  const WIDGET_SCROLL_STEP = 3;
12
12
 
13
- /** Client-backed projection of the host-owned run into the origin Pi session. */
13
+ /** Client-backed projection of the server-owned run into the origin Pi session. */
14
14
  export class SessionWorkflowView {
15
15
  private session: WorkflowSessionView | null = null;
16
16
  private scroll: number | null = null;
@@ -25,7 +25,7 @@ type PendingTurn = {
25
25
  end: { stopReason: WorkflowTurnStopReason; responseSessionEntryId: string | null } | null;
26
26
  };
27
27
 
28
- /** Adds every host-owned workflow message to one Pi session through one public API path. */
28
+ /** Adds every server-owned workflow message to one Pi session through one public API path. */
29
29
  export class WorkflowMessageCoordinator {
30
30
  private readonly queued = new Set<string>();
31
31
  private readonly closedTurnMessages = new Set<string>();
@@ -296,7 +296,7 @@ export class WorkflowMessageCoordinator {
296
296
  },
297
297
  });
298
298
  if (response.outcome !== "accepted" && response.outcome !== "adopted") {
299
- throw new Error(response.error ?? "Workflow host rejected the Pi branch report");
299
+ throw new Error(response.error ?? "Workflow server rejected the Pi branch report");
300
300
  }
301
301
  for (const entry of entries) {
302
302
  const message = messageById(view, entry.workflowMessageId);
@@ -364,7 +364,7 @@ async function reportTurn(
364
364
  payload: report as unknown as JsonValue,
365
365
  });
366
366
  if (response.outcome !== "accepted" && response.outcome !== "adopted") {
367
- throw new Error(response.error ?? "Workflow host rejected the model-turn report");
367
+ throw new Error(response.error ?? "Workflow server rejected the model-turn report");
368
368
  }
369
369
  const receipt = response.receipt;
370
370
  if (
@@ -372,12 +372,12 @@ async function reportTurn(
372
372
  receipt.schema !== WORKFLOW_TURN_REPORT_RECEIPT_SCHEMA ||
373
373
  !["active", "settled", "absent"].includes(String(receipt.ownership))
374
374
  ) {
375
- throw new Error("Workflow host returned an invalid model-turn receipt");
375
+ throw new Error("Workflow server returned an invalid model-turn receipt");
376
376
  }
377
377
  const ownership = receipt.ownership as WorkflowTurnReportReceipt["ownership"];
378
378
  if (ownership === "absent") {
379
379
  if (receipt.turn !== null) {
380
- throw new Error("Workflow host returned an invalid model-turn receipt");
380
+ throw new Error("Workflow server returned an invalid model-turn receipt");
381
381
  }
382
382
  } else if (
383
383
  !isRecord(receipt.turn) ||
@@ -388,7 +388,7 @@ async function reportTurn(
388
388
  receipt.turn.targetSessionId !== report.targetSessionId ||
389
389
  receipt.turn.state !== (ownership === "active" ? "started" : "ended")
390
390
  ) {
391
- throw new Error("Workflow host returned an invalid model-turn receipt");
391
+ throw new Error("Workflow server returned an invalid model-turn receipt");
392
392
  }
393
393
  return receipt as unknown as WorkflowTurnReportReceipt;
394
394
  }
@@ -1,16 +1,16 @@
1
1
  import type {
2
- ControllerCondition,
3
- ControllerConditionInput,
4
- ControllerConditionStatus,
5
- ControllerResourceStatus,
6
- ControllerStatusPatch,
2
+ ManagedResourceCondition,
3
+ ManagedResourceConditionInput,
4
+ ManagedResourceConditionStatus,
5
+ ManagedResourceStatus,
6
+ ResourceManagerStatusPatch,
7
7
  } from "./types.js";
8
8
 
9
9
  export function conditionTrue(
10
10
  type: string,
11
11
  reason: string,
12
12
  message?: string,
13
- ): ControllerConditionInput {
13
+ ): ManagedResourceConditionInput {
14
14
  return condition(type, true, reason, message);
15
15
  }
16
16
 
@@ -18,7 +18,7 @@ export function conditionFalse(
18
18
  type: string,
19
19
  reason: string,
20
20
  message?: string,
21
- ): ControllerConditionInput {
21
+ ): ManagedResourceConditionInput {
22
22
  return condition(type, false, reason, message);
23
23
  }
24
24
 
@@ -26,23 +26,23 @@ export function conditionUnknown(
26
26
  type: string,
27
27
  reason: string,
28
28
  message?: string,
29
- ): ControllerConditionInput {
29
+ ): ManagedResourceConditionInput {
30
30
  return condition(type, "unknown", reason, message);
31
31
  }
32
32
 
33
33
  export function applyStatusPatch<TStatus>(
34
- current: ControllerResourceStatus<TStatus>,
35
- patch: ControllerStatusPatch<TStatus> | undefined,
34
+ current: ManagedResourceStatus<TStatus>,
35
+ patch: ResourceManagerStatusPatch<TStatus> | undefined,
36
36
  generation: number,
37
37
  now: string,
38
- ): ControllerResourceStatus<TStatus> {
38
+ ): ManagedResourceStatus<TStatus> {
39
39
  return {
40
40
  observedGeneration: generation,
41
41
  conditions: mergeConditions(current.conditions, patch?.conditions ?? [], generation, now),
42
- controllerStatus:
43
- patch !== undefined && Object.hasOwn(patch, "controllerStatus")
44
- ? (patch.controllerStatus as TStatus)
45
- : current.controllerStatus,
42
+ resourceManagerStatus:
43
+ patch !== undefined && Object.hasOwn(patch, "resourceManagerStatus")
44
+ ? (patch.resourceManagerStatus as TStatus)
45
+ : current.resourceManagerStatus,
46
46
  ...(patch?.workflowRun === null
47
47
  ? {}
48
48
  : patch?.workflowRun !== undefined
@@ -54,11 +54,11 @@ export function applyStatusPatch<TStatus>(
54
54
  }
55
55
 
56
56
  export function mergeConditions(
57
- current: ControllerCondition[],
58
- updates: ControllerConditionInput[],
57
+ current: ManagedResourceCondition[],
58
+ updates: ManagedResourceConditionInput[],
59
59
  generation: number,
60
60
  now: string,
61
- ): ControllerCondition[] {
61
+ ): ManagedResourceCondition[] {
62
62
  const byType = new Map(current.map((item) => [item.type, item]));
63
63
  const seen = new Set<string>();
64
64
  for (const update of updates) {
@@ -68,7 +68,7 @@ export function mergeConditions(
68
68
  }
69
69
  seen.add(update.type);
70
70
  const previous = byType.get(update.type);
71
- const next: ControllerCondition = {
71
+ const next: ManagedResourceCondition = {
72
72
  ...update,
73
73
  observedGeneration: generation,
74
74
  lastTransitionTime:
@@ -83,11 +83,11 @@ export function mergeConditions(
83
83
 
84
84
  function condition(
85
85
  type: string,
86
- status: ControllerConditionStatus,
86
+ status: ManagedResourceConditionStatus,
87
87
  reason: string,
88
88
  message: string | undefined,
89
- ): ControllerConditionInput {
90
- const value: ControllerConditionInput = {
89
+ ): ManagedResourceConditionInput {
90
+ const value: ManagedResourceConditionInput = {
91
91
  type,
92
92
  status,
93
93
  reason,
@@ -97,7 +97,7 @@ function condition(
97
97
  return value;
98
98
  }
99
99
 
100
- function validateCondition(value: ControllerConditionInput): void {
100
+ function validateCondition(value: ManagedResourceConditionInput): void {
101
101
  if (value.type.trim().length === 0) {
102
102
  throw new Error("Condition type must not be empty");
103
103
  }
@@ -0,0 +1,71 @@
1
+ import type {
2
+ AnyResourceManagerDefinition,
3
+ ResourceManagerDefinition,
4
+ ManagedResource,
5
+ } from "./types.js";
6
+
7
+ const RESOURCE_MANAGER_DEFINITION_BRAND = Symbol.for("pi-workflows.resource-manager-definition");
8
+ const RESOURCE_MANAGER_NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
9
+
10
+ export function defineResourceManager<TSpec, TStatus>(
11
+ definition: ResourceManagerDefinition<TSpec, TStatus>,
12
+ ): ResourceManagerDefinition<TSpec, TStatus> {
13
+ assertValidResourceManagerDefinition(definition);
14
+ if (isResourceManagerDefinition(definition)) {
15
+ return definition;
16
+ }
17
+ Object.defineProperty(definition, RESOURCE_MANAGER_DEFINITION_BRAND, {
18
+ value: true,
19
+ enumerable: false,
20
+ configurable: false,
21
+ writable: false,
22
+ });
23
+ return definition;
24
+ }
25
+
26
+ export function isResourceManagerDefinition(value: unknown): value is AnyResourceManagerDefinition {
27
+ return (
28
+ value !== null &&
29
+ typeof value === "object" &&
30
+ (value as Record<PropertyKey, unknown>)[RESOURCE_MANAGER_DEFINITION_BRAND] === true
31
+ );
32
+ }
33
+
34
+ export function assertValidResourceManagerDefinition<TSpec, TStatus>(
35
+ definition: ResourceManagerDefinition<TSpec, TStatus>,
36
+ ): void {
37
+ if (definition === null || typeof definition !== "object") {
38
+ throw new Error("Invalid resource manager definition: expected an object");
39
+ }
40
+ if (!RESOURCE_MANAGER_NAME_PATTERN.test(definition.name)) {
41
+ throw new Error(
42
+ `Invalid resource manager definition: name ${JSON.stringify(definition.name)} must match ${RESOURCE_MANAGER_NAME_PATTERN.source}`,
43
+ );
44
+ }
45
+ if (typeof definition.initialStatus !== "function") {
46
+ throw new Error("Invalid resource manager definition: initialStatus must be a function");
47
+ }
48
+ if (typeof definition.reconcile !== "function") {
49
+ throw new Error("Invalid resource manager definition: reconcile must be a function");
50
+ }
51
+ if (
52
+ definition.timeoutMs !== undefined &&
53
+ (!Number.isSafeInteger(definition.timeoutMs) || definition.timeoutMs <= 0)
54
+ ) {
55
+ throw new Error(
56
+ "Invalid resource manager definition: timeoutMs must be a positive safe integer",
57
+ );
58
+ }
59
+ }
60
+
61
+ export function asAnyResourceManagerDefinition<TSpec, TStatus>(
62
+ definition: ResourceManagerDefinition<TSpec, TStatus>,
63
+ ): AnyResourceManagerDefinition {
64
+ return definition as unknown as ResourceManagerDefinition<unknown, unknown>;
65
+ }
66
+
67
+ export function asTypedResource<TSpec, TStatus>(
68
+ resource: ManagedResource,
69
+ ): ManagedResource<TSpec, TStatus> {
70
+ return resource as ManagedResource<TSpec, TStatus>;
71
+ }
@@ -1,9 +1,9 @@
1
1
  import { jsonFingerprint } from "./json.js";
2
- import type { ControllerStore } from "./store.js";
2
+ import type { ResourceManagerStore } from "./store.js";
3
3
  import type {
4
- ControllerEffects,
5
- ControllerQueueClaim,
6
- ControllerResource,
4
+ ResourceManagerEffects,
5
+ ResourceManagerQueueClaim,
6
+ ManagedResource,
7
7
  EffectApplication,
8
8
  EffectDefinition,
9
9
  EffectObservation,
@@ -11,13 +11,13 @@ import type {
11
11
  JsonObject,
12
12
  } from "./types.js";
13
13
 
14
- export class ControllerEffectService implements ControllerEffects {
14
+ export class ResourceManagerEffectService implements ResourceManagerEffects {
15
15
  private used = false;
16
16
 
17
17
  constructor(
18
- private readonly store: ControllerStore,
19
- private readonly resource: ControllerResource,
20
- private readonly claim: ControllerQueueClaim,
18
+ private readonly store: ResourceManagerStore,
19
+ private readonly resource: ManagedResource,
20
+ private readonly claim: ResourceManagerQueueClaim,
21
21
  private readonly signal: AbortSignal,
22
22
  ) {}
23
23
 
@@ -115,7 +115,7 @@ export class ControllerEffectService implements ControllerEffects {
115
115
 
116
116
  private recordEvent(type: string, effectKey: string, extra: JsonObject = {}): void {
117
117
  this.store.recordEvent({
118
- controller: this.resource.metadata.controller,
118
+ resourceManager: this.resource.metadata.resourceManager,
119
119
  key: this.resource.metadata.key,
120
120
  claim: this.claim,
121
121
  type,
@@ -0,0 +1,27 @@
1
+ export class ManagedResourceConflictError extends Error {
2
+ constructor(resourceManager: string, key: string) {
3
+ super(`Managed resource ${resourceManager}/${key} changed during reconciliation`);
4
+ this.name = "ManagedResourceConflictError";
5
+ }
6
+ }
7
+
8
+ export class ManagedResourceNotFoundError extends Error {
9
+ constructor(resourceManager: string, key: string) {
10
+ super(`Managed resource not found: ${resourceManager}/${key}`);
11
+ this.name = "ManagedResourceNotFoundError";
12
+ }
13
+ }
14
+
15
+ export class EffectRequestConflictError extends Error {
16
+ constructor(key: string) {
17
+ super(`Effect key ${JSON.stringify(key)} was reused with a different request`);
18
+ this.name = "EffectRequestConflictError";
19
+ }
20
+ }
21
+
22
+ export class WorkflowRequestConflictError extends Error {
23
+ constructor(key: string) {
24
+ super(`Workflow request key ${JSON.stringify(key)} was reused with different input`);
25
+ this.name = "WorkflowRequestConflictError";
26
+ }
27
+ }
@@ -0,0 +1,88 @@
1
+ export {
2
+ assertValidResourceManagerDefinition,
3
+ defineResourceManager,
4
+ isResourceManagerDefinition,
5
+ } from "./definition.js";
6
+ export {
7
+ applyStatusPatch,
8
+ conditionFalse,
9
+ conditionTrue,
10
+ conditionUnknown,
11
+ mergeConditions,
12
+ } from "./conditions.js";
13
+ export {
14
+ EffectRequestConflictError,
15
+ ManagedResourceConflictError,
16
+ ManagedResourceNotFoundError,
17
+ WorkflowRequestConflictError,
18
+ } from "./errors.js";
19
+ export { ResourceManagerEffectService } from "./effects.js";
20
+ export { canonicalJson, jsonFingerprint } from "./json.js";
21
+ export { ResourceManagerRuntime, type ResourceManagerRuntimeOptions } from "./runtime.js";
22
+ export { createResultHelpers, requeue, requeueAfter, settled } from "./results.js";
23
+ export {
24
+ SqliteResourceManagerStore,
25
+ type RunEventRecord,
26
+ type WorkflowRunClaimOptions,
27
+ type WorkflowRunPreparationResult,
28
+ type WorkflowRunQueueRecord,
29
+ type WorkflowRunReservationOptions,
30
+ } from "./sqlite.js";
31
+ export {
32
+ type ResourceManagerStore,
33
+ type EffectReservation,
34
+ type QueueItem,
35
+ type QueueRequeueOptions,
36
+ type WorkflowRecordUpdate,
37
+ type WorkflowReservation,
38
+ } from "./store.js";
39
+ export {
40
+ resourceManagerFileStem,
41
+ resourceManagerSearchDirs,
42
+ discoverResourceManagers,
43
+ loadResourceManagerFile,
44
+ loadDiscoveredResourceManagers,
45
+ type DiscoveredResourceManager,
46
+ } from "./loader.js";
47
+ export {
48
+ ResourceManagerWorkflowCoordinator,
49
+ type ResourceManagerWorkflowControlRequest,
50
+ type ResourceManagerWorkflowScheduler,
51
+ type WorkflowSchedulerRequest,
52
+ type WorkflowSchedulerResult,
53
+ } from "./workflows.js";
54
+ export type {
55
+ AnyResourceManagerDefinition,
56
+ ChildWorkflowRecord,
57
+ ChildWorkflowReference,
58
+ ChildWorkflowRequest,
59
+ ChildWorkflowState,
60
+ ManagedResourceCondition,
61
+ ManagedResourceConditionInput,
62
+ ManagedResourceConditionStatus,
63
+ ResourceManagerDefinition,
64
+ ResourceManagerEffects,
65
+ ResourceManagerFollowUpResult,
66
+ ResourceManagerQueueFollowUpRequest,
67
+ ResourceManagerRemoveFollowUpRequest,
68
+ ResourceManagerSettingsChangeRequest,
69
+ ResourceManagerSettingsChangeResult,
70
+ ResourceManagerEvent,
71
+ ResourceManagerQueueClaim,
72
+ ManagedResource,
73
+ ManagedResourceRef,
74
+ ManagedResourceStatus,
75
+ ResourceManagerStatusPatch,
76
+ ResourceManagerWorkflows,
77
+ EffectApplication,
78
+ EffectDefinition,
79
+ EffectObservation,
80
+ EffectRecord,
81
+ EffectState,
82
+ JsonObject,
83
+ JsonPrimitive,
84
+ JsonValue,
85
+ MaybePromise,
86
+ ReconcileContext,
87
+ ReconcileResult,
88
+ } from "./types.js";
@@ -0,0 +1,111 @@
1
+ import fs from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { fileURLToPath, pathToFileURL } from "node:url";
5
+ import { createJiti } from "jiti";
6
+ import { isResourceManagerDefinition } from "./definition.js";
7
+ import type { AnyResourceManagerDefinition } from "./types.js";
8
+
9
+ const RESOURCE_MANAGER_FILE_SUFFIXES = [
10
+ ".resource-manager.ts",
11
+ ".resource-manager.js",
12
+ ".resource-manager.mts",
13
+ ".resource-manager.mjs",
14
+ ];
15
+
16
+ export type DiscoveredResourceManager = {
17
+ name: string;
18
+ path: string;
19
+ source: "project" | "global";
20
+ };
21
+
22
+ export function resourceManagerSearchDirs(options: {
23
+ cwd: string;
24
+ homeDir?: string;
25
+ }): { dir: string; source: DiscoveredResourceManager["source"] }[] {
26
+ const homeDir = options.homeDir ?? os.homedir();
27
+ return [
28
+ { dir: path.join(options.cwd, ".pi", "resource-managers"), source: "project" },
29
+ { dir: path.join(homeDir, ".pi", "agent", "resource-managers"), source: "global" },
30
+ ];
31
+ }
32
+
33
+ export function resourceManagerFileStem(filePath: string): string {
34
+ const base = path.basename(filePath);
35
+ const suffix = RESOURCE_MANAGER_FILE_SUFFIXES.find((candidate) => base.endsWith(candidate));
36
+ return suffix === undefined ? base : base.slice(0, -suffix.length);
37
+ }
38
+
39
+ const RESOURCE_MANAGER_ENTRY = path.join(path.dirname(fileURLToPath(import.meta.url)), "index");
40
+ const WORKFLOW_ENTRY = path.join(
41
+ path.dirname(fileURLToPath(import.meta.url)),
42
+ "../workflows/index",
43
+ );
44
+
45
+ export async function loadResourceManagerFile(
46
+ filePath: string,
47
+ ): Promise<AnyResourceManagerDefinition> {
48
+ const absolutePath = path.resolve(filePath);
49
+ const jiti = createJiti(pathToFileURL(absolutePath).href, {
50
+ interopDefault: true,
51
+ moduleCache: false,
52
+ alias: {
53
+ "@osolmaz/pi-workflows/resource-managers": RESOURCE_MANAGER_ENTRY,
54
+ "@osolmaz/pi-workflows": WORKFLOW_ENTRY,
55
+ },
56
+ });
57
+ const loaded = (await jiti.import(absolutePath, { default: true })) as unknown;
58
+ if (!isResourceManagerDefinition(loaded)) {
59
+ throw new Error(
60
+ `ResourceManager module must default-export defineResourceManager(...): ${absolutePath}`,
61
+ );
62
+ }
63
+ return loaded;
64
+ }
65
+
66
+ export async function discoverResourceManagers(options: {
67
+ cwd: string;
68
+ homeDir?: string;
69
+ }): Promise<DiscoveredResourceManager[]> {
70
+ const discovered: DiscoveredResourceManager[] = [];
71
+ const seen = new Set<string>();
72
+ for (const { dir, source } of resourceManagerSearchDirs(options)) {
73
+ for (const filePath of await listResourceManagerFiles(dir)) {
74
+ const name = resourceManagerFileStem(filePath);
75
+ if (seen.has(name)) {
76
+ continue;
77
+ }
78
+ seen.add(name);
79
+ discovered.push({ name, path: filePath, source });
80
+ }
81
+ }
82
+ return discovered;
83
+ }
84
+
85
+ export async function loadDiscoveredResourceManagers(options: {
86
+ cwd: string;
87
+ homeDir?: string;
88
+ }): Promise<AnyResourceManagerDefinition[]> {
89
+ return await Promise.all(
90
+ (await discoverResourceManagers(options)).map(
91
+ async (entry) => await loadResourceManagerFile(entry.path),
92
+ ),
93
+ );
94
+ }
95
+
96
+ async function listResourceManagerFiles(dir: string): Promise<string[]> {
97
+ let entries: import("node:fs").Dirent[];
98
+ try {
99
+ entries = await fs.readdir(dir, { withFileTypes: true });
100
+ } catch {
101
+ return [];
102
+ }
103
+ return entries
104
+ .filter(
105
+ (entry) =>
106
+ entry.isFile() &&
107
+ RESOURCE_MANAGER_FILE_SUFFIXES.some((suffix) => entry.name.endsWith(suffix)),
108
+ )
109
+ .map((entry) => path.join(dir, entry.name))
110
+ .sort();
111
+ }
@@ -1,4 +1,4 @@
1
- import type { ControllerStatusPatch, ReconcileContext, ReconcileResult } from "./types.js";
1
+ import type { ResourceManagerStatusPatch, ReconcileContext, ReconcileResult } from "./types.js";
2
2
 
3
3
  export function createResultHelpers<TStatus>(): Pick<
4
4
  ReconcileContext<TStatus>,
@@ -27,20 +27,20 @@ export function createResultHelpers<TStatus>(): Pick<
27
27
  }
28
28
 
29
29
  export function settled<TStatus>(
30
- status?: ControllerStatusPatch<TStatus>,
30
+ status?: ResourceManagerStatusPatch<TStatus>,
31
31
  ): ReconcileResult<TStatus> {
32
32
  return createResultHelpers<TStatus>().settled(status);
33
33
  }
34
34
 
35
35
  export function requeue<TStatus>(
36
- status?: ControllerStatusPatch<TStatus>,
36
+ status?: ResourceManagerStatusPatch<TStatus>,
37
37
  ): ReconcileResult<TStatus> {
38
38
  return createResultHelpers<TStatus>().requeue(status);
39
39
  }
40
40
 
41
41
  export function requeueAfter<TStatus>(
42
42
  afterMs: number,
43
- status?: ControllerStatusPatch<TStatus>,
43
+ status?: ResourceManagerStatusPatch<TStatus>,
44
44
  ): ReconcileResult<TStatus> {
45
45
  return createResultHelpers<TStatus>().requeueAfter(afterMs, status);
46
46
  }