@osolmaz/pi-workflows 0.16.0 → 0.16.1
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 +18 -2
- package/dist/channels/adapter-entry.d.ts +3 -0
- package/dist/channels/adapter-entry.js +203 -0
- package/dist/channels/adapter-entry.js.map +1 -0
- package/dist/channels/config.d.ts +43 -0
- package/dist/channels/config.js +234 -0
- package/dist/channels/config.js.map +1 -0
- package/dist/channels/protocol.d.ts +94 -0
- package/dist/channels/protocol.js +219 -0
- package/dist/channels/protocol.js.map +1 -0
- package/dist/channels/telegram.d.ts +46 -0
- package/dist/channels/telegram.js +285 -0
- package/dist/channels/telegram.js.map +1 -0
- package/dist/client/client.d.ts +1 -0
- package/dist/client/client.js +5 -1
- package/dist/client/client.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/protocol.d.ts +1 -1
- package/dist/client/protocol.js +11 -5
- package/dist/client/protocol.js.map +1 -1
- package/dist/client/view.d.ts +42 -16
- package/dist/client/view.js.map +1 -1
- package/dist/controllers/index.d.ts +1 -1
- package/dist/controllers/index.js.map +1 -1
- package/dist/controllers/sqlite.d.ts +12 -165
- package/dist/controllers/sqlite.js +124 -435
- package/dist/controllers/sqlite.js.map +1 -1
- package/dist/extension/index.d.ts +23 -2
- package/dist/extension/index.js +619 -535
- package/dist/extension/index.js.map +1 -1
- package/dist/extension/recorder.d.ts +95 -0
- package/dist/extension/recorder.js +530 -0
- package/dist/extension/recorder.js.map +1 -0
- package/dist/extension/remote-recorder-store.d.ts +25 -0
- package/dist/extension/remote-recorder-store.js +81 -0
- package/dist/extension/remote-recorder-store.js.map +1 -0
- package/dist/extension/session-events.d.ts +134 -0
- package/dist/extension/session-events.js +60 -0
- package/dist/extension/session-events.js.map +1 -0
- package/dist/extension/session-view.d.ts +2 -0
- package/dist/extension/session-view.js +60 -2
- package/dist/extension/session-view.js.map +1 -1
- package/dist/extension/step-message.d.ts +6 -4
- package/dist/extension/step-message.js +12 -6
- package/dist/extension/step-message.js.map +1 -1
- package/dist/extension/widget.d.ts +2 -2
- package/dist/extension/widget.js +28 -18
- package/dist/extension/widget.js.map +1 -1
- package/dist/extension/workflow-message-coordinator.d.ts +27 -0
- package/dist/extension/workflow-message-coordinator.js +294 -0
- package/dist/extension/workflow-message-coordinator.js.map +1 -0
- package/dist/host/channel-effects.d.ts +72 -0
- package/dist/host/channel-effects.js +271 -0
- package/dist/host/channel-effects.js.map +1 -0
- package/dist/host/channel-supervisor.d.ts +21 -0
- package/dist/host/channel-supervisor.js +54 -0
- package/dist/host/channel-supervisor.js.map +1 -0
- package/dist/host/runner.d.ts +43 -8
- package/dist/host/runner.js +1567 -331
- package/dist/host/runner.js.map +1 -1
- package/dist/host/state.d.ts +19 -16
- package/dist/host/state.js +251 -63
- package/dist/host/state.js.map +1 -1
- package/dist/host/view.d.ts +19 -9
- package/dist/host/view.js +222 -118
- package/dist/host/view.js.map +1 -1
- package/dist/state/prune.js +3 -25
- package/dist/state/prune.js.map +1 -1
- package/dist/state/schema.js +76 -97
- package/dist/state/schema.js.map +1 -1
- package/dist/state/workflow-messages.d.ts +96 -0
- package/dist/state/workflow-messages.js +335 -0
- package/dist/state/workflow-messages.js.map +1 -0
- package/dist/viewer/render.js +1 -1
- package/dist/viewer/render.js.map +1 -1
- package/dist/viewer/tui.d.ts +1 -1
- package/dist/viewer/tui.js +65 -8
- package/dist/viewer/tui.js.map +1 -1
- package/dist/workflows/human-decision.d.ts +2 -0
- package/dist/workflows/human-decision.js +8 -2
- package/dist/workflows/human-decision.js.map +1 -1
- package/dist/workflows/index.d.ts +1 -1
- package/dist/workflows/index.js.map +1 -1
- package/dist/workflows/settings.d.ts +1 -8
- package/dist/workflows/settings.js.map +1 -1
- package/dist/workflows/store.d.ts +5 -19
- package/dist/workflows/store.js +121 -354
- package/dist/workflows/store.js.map +1 -1
- package/dist/workflows/tool-input.d.ts +21 -0
- package/dist/workflows/tool-input.js +23 -1
- package/dist/workflows/tool-input.js.map +1 -1
- package/dist/workflows/types.d.ts +5 -5
- package/dist/workflows/workflow-message-content.d.ts +38 -0
- package/dist/workflows/workflow-message-content.js +157 -0
- package/dist/workflows/workflow-message-content.js.map +1 -0
- package/docs/2026-08-18-herdr-piw-plan.md +2 -1
- package/docs/2026-08-20-durable-workflow-launch-plan.md +1 -1
- package/docs/2026-08-25-workflow-follow-ups.md +26 -50
- package/docs/2026-08-30-out-of-process-workflow-host-plan.md +3 -3
- package/docs/2026-09-01-restore-session-delivery-controls-plan.md +4 -0
- package/docs/2026-09-01-unified-workflow-client-plan.md +5 -1
- package/docs/2026-09-02-installed-live-e2e-plan.md +15 -1
- package/docs/2026-09-02-unify-workflow-messages-plan.md +562 -0
- package/docs/DEFERRED_TURNS.md +66 -294
- package/docs/HUMAN_DECISIONS.md +29 -30
- package/docs/HUMAN_DECISION_PRESENTATIONS.md +4 -11
- package/docs/SQLITE_STATE.md +19 -15
- package/docs/WORKFLOW_HOST.md +93 -28
- package/docs/WORKFLOW_STEP_MESSAGES.md +166 -75
- package/docs/live-replay-protocol.md +8 -4
- package/docs/tui-viewer.md +3 -1
- package/docs/workflows.md +34 -27
- package/herdr-plugin.toml +1 -1
- package/package.json +1 -1
- package/plugins/herdr/viewer.mjs +14 -5
- package/src/channels/adapter-entry.ts +220 -0
- package/src/channels/config.ts +296 -0
- package/src/channels/protocol.ts +333 -0
- package/src/channels/telegram.ts +335 -0
- package/src/client/client.ts +6 -2
- package/src/client/index.ts +2 -1
- package/src/client/protocol.ts +11 -5
- package/src/client/view.ts +43 -18
- package/src/controllers/index.ts +0 -1
- package/src/controllers/sqlite.ts +164 -844
- package/src/extension/index.ts +693 -646
- package/src/extension/recorder.ts +687 -0
- package/src/extension/remote-recorder-store.ts +126 -0
- package/src/extension/session-events.ts +119 -0
- package/src/extension/session-view.ts +86 -3
- package/src/extension/step-message.ts +19 -9
- package/src/extension/widget.ts +37 -17
- package/src/extension/workflow-message-coordinator.ts +334 -0
- package/src/host/channel-effects.ts +465 -0
- package/src/host/channel-supervisor.ts +73 -0
- package/src/host/runner.ts +2120 -531
- package/src/host/state.ts +333 -94
- package/src/host/view.ts +249 -121
- package/src/state/prune.ts +3 -31
- package/src/state/schema.ts +76 -97
- package/src/state/workflow-messages.ts +548 -0
- package/src/viewer/render.ts +1 -5
- package/src/viewer/tui.ts +71 -6
- package/src/workflows/human-decision.ts +18 -2
- package/src/workflows/index.ts +0 -1
- package/src/workflows/settings.ts +1 -20
- package/src/workflows/store.ts +173 -489
- package/src/workflows/tool-input.ts +36 -1
- package/src/workflows/types.ts +5 -5
- package/src/workflows/workflow-message-content.ts +197 -0
- package/dist/extension/decision-channels.d.ts +0 -134
- package/dist/extension/decision-channels.js +0 -1307
- package/dist/extension/decision-channels.js.map +0 -1
- package/src/extension/decision-channels.ts +0 -1826
package/src/viewer/tui.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { WorkflowClient } from "../client/client.js";
|
|
2
2
|
import { materializeRunView } from "../client/materialize.js";
|
|
3
3
|
import { sanitizeText } from "../render/ansi.js";
|
|
4
|
+
import { formatDuration } from "../render/format.js";
|
|
4
5
|
import type { JsonValue } from "../state/json.js";
|
|
5
6
|
|
|
6
7
|
const ALT_SCREEN_ON = "\u001b[?1049h\u001b[?25l";
|
|
@@ -17,6 +18,7 @@ export async function runViewer(options: ViewerOptions): Promise<void> {
|
|
|
17
18
|
let value: JsonValue = options.runId === undefined ? [] : null;
|
|
18
19
|
let selected = 0;
|
|
19
20
|
let scroll = 0;
|
|
21
|
+
let selectedStepIndex: number | null = null;
|
|
20
22
|
let mode: "runs" | "run" = options.runId === undefined ? "runs" : "run";
|
|
21
23
|
let unsubscribe: (() => Promise<void>) | undefined;
|
|
22
24
|
let runMaterializationGeneration = 0;
|
|
@@ -30,6 +32,8 @@ export async function runViewer(options: ViewerOptions): Promise<void> {
|
|
|
30
32
|
height,
|
|
31
33
|
mode === "runs" ? selected : undefined,
|
|
32
34
|
scroll,
|
|
35
|
+
new Date(),
|
|
36
|
+
selectedStepIndex,
|
|
33
37
|
);
|
|
34
38
|
process.stdout.write(`${CLEAR}${lines.join("\n")}`);
|
|
35
39
|
};
|
|
@@ -39,6 +43,7 @@ export async function runViewer(options: ViewerOptions): Promise<void> {
|
|
|
39
43
|
await unsubscribe?.();
|
|
40
44
|
mode = "runs";
|
|
41
45
|
scroll = 0;
|
|
46
|
+
selectedStepIndex = null;
|
|
42
47
|
unsubscribe = await options.client.watchRuns((event) => {
|
|
43
48
|
value = event.payload;
|
|
44
49
|
selected = Math.min(selected, Math.max(0, arrayLength(value) - 1));
|
|
@@ -51,6 +56,7 @@ export async function runViewer(options: ViewerOptions): Promise<void> {
|
|
|
51
56
|
await unsubscribe?.();
|
|
52
57
|
mode = "run";
|
|
53
58
|
scroll = 0;
|
|
59
|
+
selectedStepIndex = null;
|
|
54
60
|
unsubscribe = await options.client.watchRun(runId, (event) => {
|
|
55
61
|
const generation = ++runMaterializationGeneration;
|
|
56
62
|
value = event.payload;
|
|
@@ -75,6 +81,8 @@ export async function runViewer(options: ViewerOptions): Promise<void> {
|
|
|
75
81
|
draw();
|
|
76
82
|
if (rawModeSupported) process.stdin.setRawMode(true);
|
|
77
83
|
process.stdin.resume();
|
|
84
|
+
const redrawTimer = setInterval(draw, 1_000);
|
|
85
|
+
redrawTimer.unref?.();
|
|
78
86
|
await new Promise<void>((resolve) => {
|
|
79
87
|
const onKey = (data: Buffer) => {
|
|
80
88
|
const key = data.toString("utf8");
|
|
@@ -101,6 +109,29 @@ export async function runViewer(options: ViewerOptions): Promise<void> {
|
|
|
101
109
|
} else if (key === "\u001b[6~") {
|
|
102
110
|
scroll += page;
|
|
103
111
|
draw();
|
|
112
|
+
} else if (key === "[") {
|
|
113
|
+
const total = runStepCount(value);
|
|
114
|
+
if (total > 0) {
|
|
115
|
+
selectedStepIndex = Math.max(0, (selectedStepIndex ?? total - 1) - 1);
|
|
116
|
+
scroll = 0;
|
|
117
|
+
draw();
|
|
118
|
+
}
|
|
119
|
+
} else if (key === "]") {
|
|
120
|
+
const total = runStepCount(value);
|
|
121
|
+
if (total > 0) {
|
|
122
|
+
const next = (selectedStepIndex ?? total - 1) + 1;
|
|
123
|
+
selectedStepIndex = next >= total - 1 ? null : next;
|
|
124
|
+
scroll = 0;
|
|
125
|
+
draw();
|
|
126
|
+
}
|
|
127
|
+
} else if (key === "\u001b[F" || key === "G" || key === "L") {
|
|
128
|
+
selectedStepIndex = null;
|
|
129
|
+
scroll = 0;
|
|
130
|
+
draw();
|
|
131
|
+
} else if (key === "\u001b[H" || key === "g") {
|
|
132
|
+
selectedStepIndex = runStepCount(value) > 0 ? 0 : null;
|
|
133
|
+
scroll = 0;
|
|
134
|
+
draw();
|
|
104
135
|
}
|
|
105
136
|
} else if (mode === "runs") {
|
|
106
137
|
if (key === "\u001b[A" || key === "k") {
|
|
@@ -117,6 +148,7 @@ export async function runViewer(options: ViewerOptions): Promise<void> {
|
|
|
117
148
|
};
|
|
118
149
|
process.stdin.on("data", onKey);
|
|
119
150
|
});
|
|
151
|
+
clearInterval(redrawTimer);
|
|
120
152
|
} finally {
|
|
121
153
|
runMaterializationGeneration += 1;
|
|
122
154
|
await unsubscribe?.();
|
|
@@ -132,6 +164,8 @@ export function renderClientView(
|
|
|
132
164
|
height: number,
|
|
133
165
|
selected: number | undefined,
|
|
134
166
|
scroll: number,
|
|
167
|
+
now: Date = new Date(),
|
|
168
|
+
selectedStepIndex: number | null = null,
|
|
135
169
|
): string[] {
|
|
136
170
|
if (Array.isArray(value)) {
|
|
137
171
|
if (value.length === 0) return ["No workflow runs found."];
|
|
@@ -146,12 +180,12 @@ export function renderClientView(
|
|
|
146
180
|
return truncate(`${prefix}${summary(item)}`, width);
|
|
147
181
|
});
|
|
148
182
|
}
|
|
149
|
-
const lines = renderRun(value);
|
|
183
|
+
const lines = renderRun(value, now, selectedStepIndex);
|
|
150
184
|
const start = Math.min(scroll, Math.max(0, lines.length - height));
|
|
151
185
|
return lines.slice(start, start + height).map((line) => truncate(line, width));
|
|
152
186
|
}
|
|
153
187
|
|
|
154
|
-
function renderRun(value: JsonValue): string[] {
|
|
188
|
+
function renderRun(value: JsonValue, now: Date, selectedStepIndex: number | null): string[] {
|
|
155
189
|
if (!isRecord(value) || value.schema !== "pi-workflows.run-view.v1") {
|
|
156
190
|
return JSON.stringify(value, null, 2).split("\n");
|
|
157
191
|
}
|
|
@@ -166,20 +200,46 @@ function renderRun(value: JsonValue): string[] {
|
|
|
166
200
|
: "unknown";
|
|
167
201
|
const runId = typeof value.runId === "string" ? sanitizeText(value.runId) : "unknown";
|
|
168
202
|
const status = typeof display.status === "string" ? display.status : "unknown";
|
|
203
|
+
const steps = Array.isArray(state.steps) ? state.steps : [];
|
|
204
|
+
const selected =
|
|
205
|
+
steps.length === 0
|
|
206
|
+
? -1
|
|
207
|
+
: Math.min(Math.max(0, selectedStepIndex ?? steps.length - 1), steps.length - 1);
|
|
208
|
+
const startedAt = typeof manifest.startedAt === "string" ? Date.parse(manifest.startedAt) : NaN;
|
|
209
|
+
const finishedAt =
|
|
210
|
+
typeof manifest.finishedAt === "string" ? Date.parse(manifest.finishedAt) : NaN;
|
|
211
|
+
const elapsed = Number.isFinite(startedAt)
|
|
212
|
+
? ` · elapsed ${formatDuration(Math.max(0, (Number.isFinite(finishedAt) ? finishedAt : now.getTime()) - startedAt))}`
|
|
213
|
+
: "";
|
|
214
|
+
const position =
|
|
215
|
+
selectedStepIndex === null || selected < 0 ? "" : ` · step ${selected + 1}/${steps.length}`;
|
|
169
216
|
const lines = [
|
|
170
217
|
`workflow ${workflowName}`,
|
|
171
|
-
`${statusGlyph(status)} ${sanitizeText(status)} · run ${runId}`,
|
|
218
|
+
`${statusGlyph(status)} ${sanitizeText(status)} · run ${runId}${elapsed}${position}`,
|
|
172
219
|
];
|
|
173
220
|
if (typeof display.reason === "string") lines.push(`reason: ${sanitizeText(display.reason)}`);
|
|
174
221
|
|
|
175
|
-
const steps = Array.isArray(state.steps) ? state.steps : [];
|
|
176
222
|
if (steps.length > 0) {
|
|
177
223
|
lines.push("", "steps");
|
|
178
|
-
for (const step of steps) {
|
|
224
|
+
for (const [index, step] of steps.entries()) {
|
|
179
225
|
if (!isRecord(step)) continue;
|
|
180
226
|
const nodeId = typeof step.nodeId === "string" ? sanitizeText(step.nodeId) : "unknown";
|
|
181
227
|
const outcome = typeof step.outcome === "string" ? step.outcome : "unknown";
|
|
182
|
-
lines.push(
|
|
228
|
+
lines.push(
|
|
229
|
+
`${index === selected ? ">" : " "} ${stepGlyph(outcome)} ${nodeId} · ${sanitizeText(outcome)}`,
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
const inspected = steps[selected];
|
|
233
|
+
if (isRecord(inspected)) {
|
|
234
|
+
const nodeId =
|
|
235
|
+
typeof inspected.nodeId === "string" ? sanitizeText(inspected.nodeId) : "unknown";
|
|
236
|
+
const output = Object.hasOwn(inspected, "error") ? inspected.error : inspected.output;
|
|
237
|
+
lines.push("", `step output — ${nodeId}`);
|
|
238
|
+
lines.push(
|
|
239
|
+
...JSON.stringify(output ?? null, null, 2)
|
|
240
|
+
.split("\n")
|
|
241
|
+
.map((line) => ` ${sanitizeText(line)}`),
|
|
242
|
+
);
|
|
183
243
|
}
|
|
184
244
|
}
|
|
185
245
|
if (status === "completed" && Object.hasOwn(state, "finalOutput")) {
|
|
@@ -218,6 +278,11 @@ function selectedRunId(value: JsonValue, index: number): string | undefined {
|
|
|
218
278
|
return isRecord(item) && typeof item.runId === "string" ? item.runId : undefined;
|
|
219
279
|
}
|
|
220
280
|
|
|
281
|
+
function runStepCount(value: JsonValue): number {
|
|
282
|
+
if (!isRecord(value) || !isRecord(value.state) || !Array.isArray(value.state.steps)) return 0;
|
|
283
|
+
return value.state.steps.length;
|
|
284
|
+
}
|
|
285
|
+
|
|
221
286
|
function arrayLength(value: JsonValue): number {
|
|
222
287
|
return Array.isArray(value) ? value.length : 0;
|
|
223
288
|
}
|
|
@@ -468,6 +468,14 @@ export class HumanDecisionStore {
|
|
|
468
468
|
channel: string,
|
|
469
469
|
value: HumanDecisionDeliveryRecord,
|
|
470
470
|
): Promise<"created" | "adopted"> {
|
|
471
|
+
return this.recordDeliverySync(request, channel, value);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
recordDeliverySync(
|
|
475
|
+
request: HumanDecisionRequest | HumanDecisionChannelRequest,
|
|
476
|
+
channel: string,
|
|
477
|
+
value: HumanDecisionDeliveryRecord,
|
|
478
|
+
): "created" | "adopted" {
|
|
471
479
|
return this.recordChannelMessage(request.decisionId, channel, "delivery", value);
|
|
472
480
|
}
|
|
473
481
|
|
|
@@ -570,6 +578,14 @@ export class HumanDecisionStore {
|
|
|
570
578
|
channel: string,
|
|
571
579
|
value: HumanDecisionSettlementRecord,
|
|
572
580
|
): Promise<"created" | "adopted"> {
|
|
581
|
+
return this.recordSettlementSync(decisionId, channel, value);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
recordSettlementSync(
|
|
585
|
+
decisionId: string,
|
|
586
|
+
channel: string,
|
|
587
|
+
value: HumanDecisionSettlementRecord,
|
|
588
|
+
): "created" | "adopted" {
|
|
573
589
|
return this.recordChannelMessage(decisionId, channel, "settlement", value);
|
|
574
590
|
}
|
|
575
591
|
|
|
@@ -1066,7 +1082,7 @@ export class HumanDecisionStore {
|
|
|
1066
1082
|
purpose: "delivery" | "settlement",
|
|
1067
1083
|
value: T,
|
|
1068
1084
|
): "created" | "adopted" {
|
|
1069
|
-
const safeChannel =
|
|
1085
|
+
const safeChannel = requireChannelId(channel, "Human decision channel");
|
|
1070
1086
|
assertId(value.attemptId, `${purpose} attempt id`);
|
|
1071
1087
|
return this.state.transaction(() => {
|
|
1072
1088
|
if (this.readRequestRow(decisionId) === undefined) {
|
|
@@ -1120,7 +1136,7 @@ export class HumanDecisionStore {
|
|
|
1120
1136
|
channel: string,
|
|
1121
1137
|
purpose: "delivery" | "settlement",
|
|
1122
1138
|
): T[] {
|
|
1123
|
-
const channelId = channelIdFor(
|
|
1139
|
+
const channelId = channelIdFor(requireChannelId(channel, "Human decision channel"));
|
|
1124
1140
|
const rows = this.state.connection
|
|
1125
1141
|
.prepare(
|
|
1126
1142
|
`SELECT content_hash AS contentHash
|
package/src/workflows/index.ts
CHANGED
|
@@ -101,20 +101,7 @@ export type WorkflowSettingsChangeResult = {
|
|
|
101
101
|
adopted: boolean;
|
|
102
102
|
};
|
|
103
103
|
|
|
104
|
-
export type WorkflowFollowUpState =
|
|
105
|
-
| "queued"
|
|
106
|
-
| "pending_presentation"
|
|
107
|
-
| "ready"
|
|
108
|
-
| "sent"
|
|
109
|
-
| "removed"
|
|
110
|
-
| "cancelled";
|
|
111
|
-
|
|
112
|
-
export type WorkflowPresentationState =
|
|
113
|
-
| "none"
|
|
114
|
-
| "not-needed"
|
|
115
|
-
| "pending"
|
|
116
|
-
| "settled"
|
|
117
|
-
| "unavailable";
|
|
104
|
+
export type WorkflowFollowUpState = "queued" | "removed" | "cancelled";
|
|
118
105
|
|
|
119
106
|
export type WorkflowFollowUpRecord = {
|
|
120
107
|
followUpId: string;
|
|
@@ -126,20 +113,14 @@ export type WorkflowFollowUpRecord = {
|
|
|
126
113
|
source: string;
|
|
127
114
|
prompt: string;
|
|
128
115
|
state: WorkflowFollowUpState;
|
|
129
|
-
sessionEntryId?: string;
|
|
130
116
|
reason?: string;
|
|
131
117
|
createdAt: string;
|
|
132
118
|
updatedAt: string;
|
|
133
|
-
sentAt?: string;
|
|
134
119
|
};
|
|
135
120
|
|
|
136
121
|
export type WorkflowFollowUpQueueRecord = {
|
|
137
122
|
runId: string;
|
|
138
123
|
originSessionId?: string;
|
|
139
|
-
presentationState: WorkflowPresentationState;
|
|
140
|
-
presentationEntryId?: string;
|
|
141
|
-
presentationAssistantEntryId?: string;
|
|
142
|
-
presentationReason?: string;
|
|
143
124
|
followUps: WorkflowFollowUpRecord[];
|
|
144
125
|
};
|
|
145
126
|
|