@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.
- package/README.md +5 -5
- package/dist/builtins/autoimplement.workflow.d.ts +5 -1
- package/dist/builtins/autoimplement.workflow.js +29 -10
- package/dist/builtins/autoimplement.workflow.js.map +1 -1
- package/dist/client/client.js +3 -1
- package/dist/client/client.js.map +1 -1
- package/dist/client/protocol.d.ts +1 -1
- package/dist/client/protocol.js +18 -1
- package/dist/client/protocol.js.map +1 -1
- package/dist/client/view.d.ts +7 -5
- package/dist/extension/index.d.ts +2 -1
- package/dist/extension/index.js +115 -165
- package/dist/extension/index.js.map +1 -1
- package/dist/extension/recorder.d.ts +1 -1
- package/dist/extension/recorder.js +2 -2
- package/dist/extension/recorder.js.map +1 -1
- package/dist/extension/step-message.d.ts +1 -1
- package/dist/extension/step-message.js +4 -4
- package/dist/extension/step-message.js.map +1 -1
- package/dist/extension/workflow-message-coordinator.d.ts +12 -1
- package/dist/extension/workflow-message-coordinator.js +65 -54
- package/dist/extension/workflow-message-coordinator.js.map +1 -1
- package/dist/resource-managers/index.d.ts +1 -2
- package/dist/resource-managers/index.js +1 -2
- package/dist/resource-managers/index.js.map +1 -1
- package/dist/resource-managers/sqlite.d.ts +2 -295
- package/dist/resource-managers/sqlite.js +71 -1528
- package/dist/resource-managers/sqlite.js.map +1 -1
- package/dist/resource-managers/types.d.ts +2 -7
- package/dist/server/resource-runner-entry.js +2 -6
- package/dist/server/resource-runner-entry.js.map +1 -1
- package/dist/server/rpc-bridge.js +1 -1
- package/dist/server/rpc-bridge.js.map +1 -1
- package/dist/server/rpc-executor.js +2 -4
- package/dist/server/rpc-executor.js.map +1 -1
- package/dist/server/server-entry.js +3 -0
- package/dist/server/server-entry.js.map +1 -1
- package/dist/server/server.d.ts +22 -8
- package/dist/server/server.js +498 -543
- package/dist/server/server.js.map +1 -1
- package/dist/server/state.d.ts +16 -29
- package/dist/server/state.js +105 -319
- package/dist/server/state.js.map +1 -1
- package/dist/server/view.d.ts +4 -5
- package/dist/server/view.js +51 -51
- package/dist/server/view.js.map +1 -1
- package/dist/server/workflow-runner-entry.d.ts +1 -1
- package/dist/server/workflow-runner-entry.js +1 -49
- package/dist/server/workflow-runner-entry.js.map +1 -1
- package/dist/server/workflow-runner-protocol.d.ts +2 -7
- package/dist/server/workflow-runner-protocol.js +5 -10
- package/dist/server/workflow-runner-protocol.js.map +1 -1
- package/dist/server/workflow-runner-store.d.ts +5 -6
- package/dist/server/workflow-runner-store.js +7 -14
- package/dist/server/workflow-runner-store.js.map +1 -1
- package/dist/state/attempt-time.d.ts +21 -0
- package/dist/state/attempt-time.js +95 -0
- package/dist/state/attempt-time.js.map +1 -0
- package/dist/state/json.d.ts +2 -1
- package/dist/state/json.js.map +1 -1
- package/dist/state/project-store.d.ts +39 -0
- package/dist/state/project-store.js +154 -0
- package/dist/state/project-store.js.map +1 -0
- package/dist/state/prune.d.ts +28 -0
- package/dist/state/prune.js +278 -83
- package/dist/state/prune.js.map +1 -1
- package/dist/state/schema.js +23 -23
- package/dist/state/workflow-messages.d.ts +1 -1
- package/dist/state/workflow-messages.js +3 -1
- package/dist/state/workflow-messages.js.map +1 -1
- package/dist/viewer/backup.js +5 -0
- package/dist/viewer/backup.js.map +1 -1
- package/dist/workflows/diagnostics.d.ts +8 -0
- package/dist/workflows/diagnostics.js +61 -0
- package/dist/workflows/diagnostics.js.map +1 -0
- package/dist/workflows/engine.d.ts +2 -15
- package/dist/workflows/engine.js +125 -245
- package/dist/workflows/engine.js.map +1 -1
- package/dist/workflows/human-decision.d.ts +3 -7
- package/dist/workflows/human-decision.js +2 -56
- package/dist/workflows/human-decision.js.map +1 -1
- package/dist/workflows/index.d.ts +2 -1
- package/dist/workflows/index.js +1 -0
- package/dist/workflows/index.js.map +1 -1
- package/dist/workflows/queue.d.ts +267 -0
- package/dist/workflows/queue.js +1248 -0
- package/dist/workflows/queue.js.map +1 -0
- package/dist/workflows/requests.d.ts +47 -0
- package/dist/workflows/requests.js +158 -0
- package/dist/workflows/requests.js.map +1 -0
- package/dist/workflows/schema.js +2 -4
- package/dist/workflows/schema.js.map +1 -1
- package/dist/workflows/store.d.ts +11 -16
- package/dist/workflows/store.js +174 -169
- package/dist/workflows/store.js.map +1 -1
- package/dist/workflows/tool-input.d.ts +6 -9
- package/dist/workflows/tool-input.js +15 -12
- package/dist/workflows/tool-input.js.map +1 -1
- package/dist/workflows/transitions.d.ts +38 -0
- package/dist/workflows/transitions.js +276 -0
- package/dist/workflows/transitions.js.map +1 -0
- package/dist/workflows/types.d.ts +9 -42
- package/dist/workflows/workflow-message-content.d.ts +7 -1
- package/dist/workflows/workflow-message-content.js +22 -4
- package/dist/workflows/workflow-message-content.js.map +1 -1
- package/docs/2026-08-25-workflow-follow-ups.md +5 -5
- package/docs/2026-09-02-unify-workflow-messages-plan.md +2 -0
- package/docs/2026-09-04-workflow-run-state-plan.md +2 -0
- package/docs/2026-09-06-durable-execution-plan.md +176 -0
- package/docs/DEFERRED_TURNS.md +84 -105
- package/docs/HUMAN_DECISIONS.md +17 -19
- package/docs/HUMAN_DECISION_PRESENTATIONS.md +1 -1
- package/docs/SQLITE_STATE.md +87 -30
- package/docs/WORKFLOW_SERVER.md +50 -11
- package/docs/WORKFLOW_STEP_MESSAGES.md +72 -54
- package/docs/plans/2026-08-04-controller-runtime-plan.md +2 -0
- package/docs/plans/2026-08-21-deferred-turn-intents-plan.md +2 -0
- package/docs/plans/2026-08-27-workflow-terminal-restart-plan.md +2 -0
- package/docs/plans/2026-09-04-automatic-state-retention-plan.md +283 -0
- package/docs/workflows.md +109 -44
- package/examples/workflows/autoresearch.workflow.ts +0 -2
- package/examples/workflows/branch.workflow.ts +0 -4
- package/examples/workflows/echo.workflow.ts +0 -2
- package/examples/workflows/live-settings.workflow.ts +0 -2
- package/examples/workflows/two-turn.workflow.ts +0 -2
- package/herdr-plugin.toml +1 -1
- package/package.json +1 -1
- package/protocol/fixtures/client-v1.json +1 -1
- package/skills/pi-workflows/SKILL.md +8 -7
- package/src/builtins/autoimplement.workflow.ts +31 -10
- package/src/client/client.ts +3 -1
- package/src/client/protocol.ts +21 -1
- package/src/client/view.ts +9 -5
- package/src/extension/index.ts +117 -189
- package/src/extension/recorder.ts +2 -2
- package/src/extension/step-message.ts +4 -4
- package/src/extension/workflow-message-coordinator.ts +78 -50
- package/src/resource-managers/index.ts +1 -9
- package/src/resource-managers/sqlite.ts +330 -2655
- package/src/resource-managers/types.ts +2 -3
- package/src/server/resource-runner-entry.ts +2 -7
- package/src/server/rpc-bridge.ts +1 -1
- package/src/server/rpc-executor.ts +4 -9
- package/src/server/server-entry.ts +3 -0
- package/src/server/server.ts +568 -644
- package/src/server/state.ts +122 -466
- package/src/server/view.ts +56 -60
- package/src/server/workflow-runner-entry.ts +2 -55
- package/src/server/workflow-runner-protocol.ts +5 -15
- package/src/server/workflow-runner-store.ts +12 -27
- package/src/state/attempt-time.ts +122 -0
- package/src/state/json.ts +2 -1
- package/src/state/project-store.ts +218 -0
- package/src/state/prune.ts +408 -95
- package/src/state/schema.ts +23 -23
- package/src/state/workflow-messages.ts +4 -2
- package/src/viewer/backup.ts +7 -0
- package/src/workflows/diagnostics.ts +78 -0
- package/src/workflows/engine.ts +133 -291
- package/src/workflows/human-decision.ts +3 -81
- package/src/workflows/index.ts +8 -2
- package/src/workflows/queue.ts +2091 -0
- package/src/workflows/requests.ts +283 -0
- package/src/workflows/schema.ts +2 -6
- package/src/workflows/store.ts +223 -231
- package/src/workflows/tool-input.ts +20 -12
- package/src/workflows/transitions.ts +330 -0
- package/src/workflows/types.ts +9 -46
- package/src/workflows/workflow-message-content.ts +28 -4
- package/dist/resource-managers/runtime.d.ts +0 -59
- package/dist/resource-managers/runtime.js +0 -423
- package/dist/resource-managers/runtime.js.map +0 -1
- 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
|
+
}
|