@osolmaz/pi-workflows 0.16.4 → 0.16.6

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 (173) hide show
  1. package/README.md +5 -5
  2. package/dist/builtins/autoimplement.workflow.d.ts +5 -1
  3. package/dist/builtins/autoimplement.workflow.js +29 -10
  4. package/dist/builtins/autoimplement.workflow.js.map +1 -1
  5. package/dist/client/client.js +3 -1
  6. package/dist/client/client.js.map +1 -1
  7. package/dist/client/protocol.d.ts +1 -1
  8. package/dist/client/protocol.js +18 -1
  9. package/dist/client/protocol.js.map +1 -1
  10. package/dist/client/view.d.ts +7 -5
  11. package/dist/extension/index.d.ts +2 -1
  12. package/dist/extension/index.js +115 -165
  13. package/dist/extension/index.js.map +1 -1
  14. package/dist/extension/recorder.d.ts +1 -1
  15. package/dist/extension/recorder.js +2 -2
  16. package/dist/extension/recorder.js.map +1 -1
  17. package/dist/extension/step-message.d.ts +1 -1
  18. package/dist/extension/step-message.js +4 -4
  19. package/dist/extension/step-message.js.map +1 -1
  20. package/dist/extension/workflow-message-coordinator.d.ts +12 -1
  21. package/dist/extension/workflow-message-coordinator.js +65 -54
  22. package/dist/extension/workflow-message-coordinator.js.map +1 -1
  23. package/dist/resource-managers/index.d.ts +1 -2
  24. package/dist/resource-managers/index.js +1 -2
  25. package/dist/resource-managers/index.js.map +1 -1
  26. package/dist/resource-managers/sqlite.d.ts +2 -295
  27. package/dist/resource-managers/sqlite.js +71 -1528
  28. package/dist/resource-managers/sqlite.js.map +1 -1
  29. package/dist/resource-managers/types.d.ts +2 -7
  30. package/dist/server/resource-runner-entry.js +2 -6
  31. package/dist/server/resource-runner-entry.js.map +1 -1
  32. package/dist/server/rpc-bridge.js +1 -1
  33. package/dist/server/rpc-bridge.js.map +1 -1
  34. package/dist/server/rpc-executor.js +2 -4
  35. package/dist/server/rpc-executor.js.map +1 -1
  36. package/dist/server/server-entry.js +3 -0
  37. package/dist/server/server-entry.js.map +1 -1
  38. package/dist/server/server.d.ts +22 -8
  39. package/dist/server/server.js +498 -543
  40. package/dist/server/server.js.map +1 -1
  41. package/dist/server/state.d.ts +16 -29
  42. package/dist/server/state.js +105 -319
  43. package/dist/server/state.js.map +1 -1
  44. package/dist/server/view.d.ts +4 -5
  45. package/dist/server/view.js +51 -51
  46. package/dist/server/view.js.map +1 -1
  47. package/dist/server/workflow-runner-entry.d.ts +1 -1
  48. package/dist/server/workflow-runner-entry.js +1 -49
  49. package/dist/server/workflow-runner-entry.js.map +1 -1
  50. package/dist/server/workflow-runner-protocol.d.ts +2 -7
  51. package/dist/server/workflow-runner-protocol.js +5 -10
  52. package/dist/server/workflow-runner-protocol.js.map +1 -1
  53. package/dist/server/workflow-runner-store.d.ts +5 -6
  54. package/dist/server/workflow-runner-store.js +7 -14
  55. package/dist/server/workflow-runner-store.js.map +1 -1
  56. package/dist/state/attempt-time.d.ts +21 -0
  57. package/dist/state/attempt-time.js +95 -0
  58. package/dist/state/attempt-time.js.map +1 -0
  59. package/dist/state/json.d.ts +2 -1
  60. package/dist/state/json.js.map +1 -1
  61. package/dist/state/project-store.d.ts +39 -0
  62. package/dist/state/project-store.js +154 -0
  63. package/dist/state/project-store.js.map +1 -0
  64. package/dist/state/prune.d.ts +28 -0
  65. package/dist/state/prune.js +278 -83
  66. package/dist/state/prune.js.map +1 -1
  67. package/dist/state/schema.js +23 -23
  68. package/dist/state/workflow-messages.d.ts +1 -1
  69. package/dist/state/workflow-messages.js +3 -1
  70. package/dist/state/workflow-messages.js.map +1 -1
  71. package/dist/viewer/backup.js +5 -0
  72. package/dist/viewer/backup.js.map +1 -1
  73. package/dist/workflows/diagnostics.d.ts +8 -0
  74. package/dist/workflows/diagnostics.js +61 -0
  75. package/dist/workflows/diagnostics.js.map +1 -0
  76. package/dist/workflows/engine.d.ts +2 -15
  77. package/dist/workflows/engine.js +125 -245
  78. package/dist/workflows/engine.js.map +1 -1
  79. package/dist/workflows/human-decision.d.ts +3 -7
  80. package/dist/workflows/human-decision.js +2 -56
  81. package/dist/workflows/human-decision.js.map +1 -1
  82. package/dist/workflows/index.d.ts +2 -1
  83. package/dist/workflows/index.js +1 -0
  84. package/dist/workflows/index.js.map +1 -1
  85. package/dist/workflows/queue.d.ts +267 -0
  86. package/dist/workflows/queue.js +1248 -0
  87. package/dist/workflows/queue.js.map +1 -0
  88. package/dist/workflows/requests.d.ts +47 -0
  89. package/dist/workflows/requests.js +158 -0
  90. package/dist/workflows/requests.js.map +1 -0
  91. package/dist/workflows/schema.js +2 -4
  92. package/dist/workflows/schema.js.map +1 -1
  93. package/dist/workflows/store.d.ts +11 -16
  94. package/dist/workflows/store.js +174 -169
  95. package/dist/workflows/store.js.map +1 -1
  96. package/dist/workflows/tool-input.d.ts +6 -9
  97. package/dist/workflows/tool-input.js +15 -12
  98. package/dist/workflows/tool-input.js.map +1 -1
  99. package/dist/workflows/transitions.d.ts +38 -0
  100. package/dist/workflows/transitions.js +276 -0
  101. package/dist/workflows/transitions.js.map +1 -0
  102. package/dist/workflows/types.d.ts +9 -42
  103. package/dist/workflows/workflow-message-content.d.ts +7 -1
  104. package/dist/workflows/workflow-message-content.js +22 -4
  105. package/dist/workflows/workflow-message-content.js.map +1 -1
  106. package/docs/2026-08-25-workflow-follow-ups.md +5 -5
  107. package/docs/2026-09-02-unify-workflow-messages-plan.md +2 -0
  108. package/docs/2026-09-04-workflow-run-state-plan.md +2 -0
  109. package/docs/2026-09-06-durable-execution-plan.md +176 -0
  110. package/docs/DEFERRED_TURNS.md +84 -105
  111. package/docs/HUMAN_DECISIONS.md +17 -19
  112. package/docs/HUMAN_DECISION_PRESENTATIONS.md +1 -1
  113. package/docs/SQLITE_STATE.md +87 -30
  114. package/docs/WORKFLOW_SERVER.md +50 -11
  115. package/docs/WORKFLOW_STEP_MESSAGES.md +72 -54
  116. package/docs/plans/2026-08-04-controller-runtime-plan.md +2 -0
  117. package/docs/plans/2026-08-21-deferred-turn-intents-plan.md +2 -0
  118. package/docs/plans/2026-08-27-workflow-terminal-restart-plan.md +2 -0
  119. package/docs/plans/2026-09-04-automatic-state-retention-plan.md +283 -0
  120. package/docs/workflows.md +109 -44
  121. package/examples/workflows/autoresearch.workflow.ts +0 -2
  122. package/examples/workflows/branch.workflow.ts +0 -4
  123. package/examples/workflows/echo.workflow.ts +0 -2
  124. package/examples/workflows/live-settings.workflow.ts +0 -2
  125. package/examples/workflows/two-turn.workflow.ts +0 -2
  126. package/herdr-plugin.toml +1 -1
  127. package/package.json +1 -1
  128. package/protocol/fixtures/client-v1.json +1 -1
  129. package/skills/pi-workflows/SKILL.md +8 -7
  130. package/src/builtins/autoimplement.workflow.ts +31 -10
  131. package/src/client/client.ts +3 -1
  132. package/src/client/protocol.ts +21 -1
  133. package/src/client/view.ts +9 -5
  134. package/src/extension/index.ts +117 -189
  135. package/src/extension/recorder.ts +2 -2
  136. package/src/extension/step-message.ts +4 -4
  137. package/src/extension/workflow-message-coordinator.ts +78 -50
  138. package/src/resource-managers/index.ts +1 -9
  139. package/src/resource-managers/sqlite.ts +330 -2655
  140. package/src/resource-managers/types.ts +2 -3
  141. package/src/server/resource-runner-entry.ts +2 -7
  142. package/src/server/rpc-bridge.ts +1 -1
  143. package/src/server/rpc-executor.ts +4 -9
  144. package/src/server/server-entry.ts +3 -0
  145. package/src/server/server.ts +568 -644
  146. package/src/server/state.ts +122 -466
  147. package/src/server/view.ts +56 -60
  148. package/src/server/workflow-runner-entry.ts +2 -55
  149. package/src/server/workflow-runner-protocol.ts +5 -15
  150. package/src/server/workflow-runner-store.ts +12 -27
  151. package/src/state/attempt-time.ts +122 -0
  152. package/src/state/json.ts +2 -1
  153. package/src/state/project-store.ts +218 -0
  154. package/src/state/prune.ts +408 -95
  155. package/src/state/schema.ts +23 -23
  156. package/src/state/workflow-messages.ts +4 -2
  157. package/src/viewer/backup.ts +7 -0
  158. package/src/workflows/diagnostics.ts +78 -0
  159. package/src/workflows/engine.ts +133 -291
  160. package/src/workflows/human-decision.ts +3 -81
  161. package/src/workflows/index.ts +8 -2
  162. package/src/workflows/queue.ts +2091 -0
  163. package/src/workflows/requests.ts +283 -0
  164. package/src/workflows/schema.ts +2 -6
  165. package/src/workflows/store.ts +223 -231
  166. package/src/workflows/tool-input.ts +20 -12
  167. package/src/workflows/transitions.ts +330 -0
  168. package/src/workflows/types.ts +9 -46
  169. package/src/workflows/workflow-message-content.ts +28 -4
  170. package/dist/resource-managers/runtime.d.ts +0 -59
  171. package/dist/resource-managers/runtime.js +0 -423
  172. package/dist/resource-managers/runtime.js.map +0 -1
  173. package/src/resource-managers/runtime.ts +0 -576
@@ -0,0 +1,218 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { StateDatabase, workflowStatePath } from "./database.js";
5
+ import { recordViewerDeltas } from "./viewer.js";
6
+
7
+ type LeaseRow = {
8
+ generation: number;
9
+ ownerType: string | null;
10
+ ownerId: string | null;
11
+ tokenHash: Buffer | null;
12
+ expiresAt: number | null;
13
+ };
14
+
15
+ export function isRecord(value: unknown): value is Record<string, unknown> {
16
+ return typeof value === "object" && value !== null && !Array.isArray(value);
17
+ }
18
+
19
+ function isLeaseRow(value: unknown): value is LeaseRow {
20
+ return isRecord(value);
21
+ }
22
+
23
+ function isProjectRow(value: unknown): value is { projectId: string } {
24
+ return isRecord(value);
25
+ }
26
+
27
+ function isRevisionRow(value: unknown): value is { revision: number } {
28
+ return isRecord(value);
29
+ }
30
+
31
+ export function isSequenceRow(value: unknown): value is { seq: number } {
32
+ return isRecord(value);
33
+ }
34
+
35
+ function projectIdFor(canonicalPath: string): string {
36
+ return `project-${createHash("sha256").update(canonicalPath).digest("hex").slice(0, 40)}`;
37
+ }
38
+
39
+ function canonicalProjectPath(value: string): string {
40
+ try {
41
+ return fs.realpathSync.native(value);
42
+ } catch {
43
+ return path.resolve(value);
44
+ }
45
+ }
46
+
47
+ export function validTimestamp(value?: string): string {
48
+ const date = value === undefined ? new Date() : new Date(value);
49
+ if (!Number.isFinite(date.getTime())) throw new Error("Invalid timestamp");
50
+ return date.toISOString();
51
+ }
52
+
53
+ export function epoch(value: string): number {
54
+ return Date.parse(value);
55
+ }
56
+
57
+ /** Shared connection, project, and revision operations; domain stores own their rows. */
58
+ export abstract class ProjectStore {
59
+ readonly filePath: string;
60
+
61
+ readonly state: StateDatabase;
62
+
63
+ private readonly ownsState: boolean;
64
+
65
+ protected readonly projectId: string | null;
66
+
67
+ private closed = false;
68
+
69
+ constructor(
70
+ filePath: string = workflowStatePath(),
71
+ options: {
72
+ readOnly?: boolean;
73
+ projectPath?: string;
74
+ state?: StateDatabase;
75
+ /** Server-only global view. Project-scoped mutations still require projectPath. */
76
+ global?: boolean;
77
+ } = {},
78
+ ) {
79
+ this.ownsState = options.state === undefined;
80
+ this.state =
81
+ options.state ??
82
+ new StateDatabase({
83
+ filePath,
84
+ mode: options.readOnly === true ? "read-only" : "read-write",
85
+ checkLegacyState: filePath === workflowStatePath(),
86
+ });
87
+ this.filePath = this.state.filePath;
88
+ if (options.global === true) {
89
+ this.projectId = null;
90
+ } else {
91
+ const projectPath = options.projectPath === undefined ? process.cwd() : options.projectPath;
92
+ this.projectId =
93
+ this.state.mode === "read-only"
94
+ ? this.findProject(projectPath)
95
+ : this.ensureProject(projectPath);
96
+ }
97
+ }
98
+
99
+ close(): void {
100
+ if (this.closed) return;
101
+ this.closed = true;
102
+ if (this.ownsState) this.state.close();
103
+ }
104
+
105
+ private ensureProject(projectPath: string): string {
106
+ const canonicalPath = canonicalProjectPath(projectPath);
107
+ const projectId = projectIdFor(canonicalPath);
108
+ this.state.connection
109
+ .prepare(
110
+ `INSERT INTO projects(project_id, canonical_path, created_at)
111
+ VALUES (?, ?, ?) ON CONFLICT(project_id) DO NOTHING`,
112
+ )
113
+ .run(projectId, canonicalPath, Date.now());
114
+ return projectId;
115
+ }
116
+
117
+ private findProject(projectPath: string): string | null {
118
+ const row = this.state.connection
119
+ .prepare("SELECT project_id AS projectId FROM projects WHERE canonical_path = ?")
120
+ .get(canonicalProjectPath(projectPath));
121
+ return isProjectRow(row) ? row.projectId : null;
122
+ }
123
+
124
+ protected requireProjectId(): string {
125
+ if (this.projectId === null) throw new Error("Project is not registered in the state database");
126
+ return this.projectId;
127
+ }
128
+
129
+ protected requireLease(resourceId: string): LeaseRow {
130
+ const row = this.state.connection
131
+ .prepare(
132
+ `SELECT generation, owner_type AS ownerType, owner_id AS ownerId,
133
+ token_hash AS tokenHash, expires_at AS expiresAt
134
+ FROM leases WHERE resource_id = ?`,
135
+ )
136
+ .get(resourceId);
137
+ /* istanbul ignore if -- exact schema and internal query shape */
138
+ /* istanbul ignore if -- impossible after exact schema and transaction checks */
139
+ if (!isLeaseRow(row)) throw new Error(`Lease is missing: ${resourceId}`);
140
+ return row;
141
+ }
142
+
143
+ protected resourceRevision(resourceId: string): number {
144
+ const row = this.state.connection
145
+ .prepare("SELECT revision FROM resources WHERE resource_id = ?")
146
+ .get(resourceId);
147
+ /* istanbul ignore if -- exact schema and internal query shape */
148
+ /* istanbul ignore if -- impossible after exact schema and transaction checks */
149
+ if (!isRevisionRow(row)) throw new Error(`Resource is missing: ${resourceId}`);
150
+ return row.revision;
151
+ }
152
+
153
+ protected bumpResource(resourceId: string, expectedRevision: number, now: number): void {
154
+ const result = this.state.connection
155
+ .prepare(
156
+ "UPDATE resources SET revision = revision + 1, updated_at = ? WHERE resource_id = ? AND revision = ?",
157
+ )
158
+ .run(now, resourceId, expectedRevision);
159
+ if (result.changes !== 1) throw new Error("Resource revision conflict");
160
+ const run = this.state.connection
161
+ .prepare(
162
+ `SELECT r.run_id AS runId
163
+ FROM runs r JOIN viewer_runs v ON v.run_id = r.run_id
164
+ WHERE r.resource_id = ?`,
165
+ )
166
+ .get(resourceId);
167
+ if (isRecord(run) && typeof run.runId === "string") {
168
+ recordViewerDeltas(
169
+ this.state,
170
+ run.runId,
171
+ [
172
+ { targetType: "summary" },
173
+ { targetType: "graph" },
174
+ { targetType: "replay" },
175
+ { targetType: "inspector", targetKey: "run" },
176
+ ],
177
+ now,
178
+ );
179
+ }
180
+ }
181
+
182
+ protected insertEvent(
183
+ resourceId: string,
184
+ revision: number,
185
+ type: string,
186
+ actorType: string,
187
+ actorId: string | null,
188
+ payload: unknown,
189
+ now: number,
190
+ leaseGeneration?: number,
191
+ ): string {
192
+ const eventId = `event-${randomUUID()}`;
193
+ const payloadHash = this.state.putJson(payload, now);
194
+ this.state.connection
195
+ .prepare(
196
+ `INSERT INTO events(
197
+ event_id, resource_id, resource_revision, event_type, actor_type,
198
+ actor_id, lease_generation, payload_hash, recorded_at
199
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
200
+ )
201
+ .run(
202
+ eventId,
203
+ resourceId,
204
+ revision,
205
+ type,
206
+ actorType,
207
+ actorId,
208
+ leaseGeneration ?? null,
209
+ payloadHash,
210
+ now,
211
+ );
212
+ return eventId;
213
+ }
214
+
215
+ protected assertWritable(): void {
216
+ if (this.state.mode === "read-only") throw new Error("State store is read-only");
217
+ }
218
+ }