@osolmaz/pi-workflows 0.10.0 → 0.11.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 +32 -14
- package/dist/builtins/autodoc.workflow.d.ts +58 -0
- package/dist/builtins/autodoc.workflow.js +266 -0
- package/dist/builtins/autodoc.workflow.js.map +1 -0
- package/dist/builtins/autoimplement.workflow.d.ts +212 -11
- package/dist/builtins/autoimplement.workflow.js +431 -22
- package/dist/builtins/autoimplement.workflow.js.map +1 -1
- package/dist/builtins/{autodevise.workflow.d.ts → autoplan.workflow.d.ts} +9 -9
- package/dist/builtins/{autodevise.workflow.js → autoplan.workflow.js} +20 -20
- package/dist/builtins/autoplan.workflow.js.map +1 -0
- package/dist/builtins/catalog.js +8 -4
- package/dist/builtins/catalog.js.map +1 -1
- package/dist/builtins/index.d.ts +6 -3
- package/dist/builtins/index.js +5 -2
- package/dist/builtins/index.js.map +1 -1
- package/dist/builtins/monitor.workflow.d.ts +4 -0
- package/dist/builtins/monitor.workflow.js +134 -9
- package/dist/builtins/monitor.workflow.js.map +1 -1
- package/dist/builtins/plan-approval.workflow.d.ts +83 -0
- package/dist/builtins/plan-approval.workflow.js +148 -0
- package/dist/builtins/plan-approval.workflow.js.map +1 -0
- package/dist/builtins/plan-presentation.d.ts +7 -0
- package/dist/builtins/plan-presentation.js +44 -0
- package/dist/builtins/plan-presentation.js.map +1 -0
- package/dist/extension/decision-channels.d.ts +132 -0
- package/dist/extension/decision-channels.js +1082 -0
- package/dist/extension/decision-channels.js.map +1 -0
- package/dist/extension/index.d.ts +1 -0
- package/dist/extension/index.js +426 -11
- package/dist/extension/index.js.map +1 -1
- package/dist/extension/widget.js +26 -2
- package/dist/extension/widget.js.map +1 -1
- package/dist/extension/workflow-tool.d.ts +1 -37
- package/dist/extension/workflow-tool.js +1 -42
- package/dist/extension/workflow-tool.js.map +1 -1
- package/dist/herdr/setup.d.ts +13 -1
- package/dist/herdr/setup.js +349 -36
- package/dist/herdr/setup.js.map +1 -1
- package/dist/host/rpc-bridge.js +4 -21
- package/dist/host/rpc-bridge.js.map +1 -1
- package/dist/render/graph-render.js +33 -8
- package/dist/render/graph-render.js.map +1 -1
- package/dist/viewer/cli.d.ts +1 -0
- package/dist/viewer/cli.js +21 -10
- package/dist/viewer/cli.js.map +1 -1
- package/dist/viewer/render.js +36 -2
- package/dist/viewer/render.js.map +1 -1
- package/dist/workflows/composition.js +15 -1
- package/dist/workflows/composition.js.map +1 -1
- package/dist/workflows/decision-presentation.d.ts +18 -0
- package/dist/workflows/decision-presentation.js +417 -0
- package/dist/workflows/decision-presentation.js.map +1 -0
- package/dist/workflows/engine.d.ts +2 -1
- package/dist/workflows/engine.js +86 -4
- package/dist/workflows/engine.js.map +1 -1
- package/dist/workflows/human-decision.d.ts +87 -0
- package/dist/workflows/human-decision.js +583 -0
- package/dist/workflows/human-decision.js.map +1 -0
- package/dist/workflows/index.d.ts +3 -1
- package/dist/workflows/index.js +2 -0
- package/dist/workflows/index.js.map +1 -1
- package/dist/workflows/schema.js +15 -0
- package/dist/workflows/schema.js.map +1 -1
- package/dist/workflows/store.js +7 -0
- package/dist/workflows/store.js.map +1 -1
- package/dist/workflows/tool-input.d.ts +72 -0
- package/dist/workflows/tool-input.js +141 -0
- package/dist/workflows/tool-input.js.map +1 -0
- package/dist/workflows/types.d.ts +223 -0
- package/docs/HUMAN_DECISIONS.md +371 -0
- package/docs/HUMAN_DECISION_PRESENTATIONS.md +323 -0
- package/docs/MONITOR.md +17 -6
- package/docs/WORKFLOW_COMPOSITION.md +13 -6
- package/docs/plans/2026-08-19-human-decision-gates-plan.md +296 -0
- package/docs/plans/2026-08-19-human-decision-presentations-plan.md +172 -0
- package/docs/plans/2026-08-19-provider-compatible-workflow-tool-schema-plan.md +45 -0
- package/docs/plans/2026-08-19-workflow-composition-plan.md +25 -17
- package/docs/plans/2026-08-20-autoimplement-blocker-challenge-plan.md +138 -0
- package/docs/plans/2026-08-20-herdr-plugin-sync-plan.md +104 -0
- package/docs/run-bundles.md +23 -2
- package/docs/workflows.md +47 -5
- package/examples/workflows/approved-plan.workflow.ts +58 -0
- package/examples/workflows/autoplan.workflow.ts +1 -0
- package/examples/workflows/human-decision.workflow.ts +62 -0
- package/herdr-plugin.toml +1 -1
- package/package.json +2 -1
- package/schemas/decision-presentation-v1.schema.json +83 -0
- package/schemas/human-decision-accepted-v1.schema.json +44 -0
- package/schemas/human-decision-accepted-v2.schema.json +50 -0
- package/schemas/human-decision-answer-attempt-v1.schema.json +37 -0
- package/schemas/human-decision-cancellation-v1.schema.json +15 -0
- package/schemas/human-decision-continuation-v1.schema.json +16 -0
- package/schemas/human-decision-delivery-v1.schema.json +28 -0
- package/schemas/human-decision-delivery-v2.schema.json +36 -0
- package/schemas/human-decision-receipt-v1.schema.json +33 -0
- package/schemas/human-decision-receipt-v2.schema.json +39 -0
- package/schemas/human-decision-request-v1.schema.json +59 -0
- package/schemas/human-decision-request-v2.schema.json +69 -0
- package/schemas/human-decision-resolution-v1.schema.json +27 -0
- package/schemas/human-decision-resolution-v2.schema.json +27 -0
- package/schemas/human-decision-settlement-v1.schema.json +28 -0
- package/skills/autodoc/SKILL.md +43 -0
- package/skills/autoimplement/SKILL.md +58 -0
- package/skills/autoimplement/agents/openai.yaml +4 -0
- package/skills/autoplan/SKILL.md +25 -0
- package/skills/monitor/SKILL.md +3 -1
- package/skills/pi-workflows/SKILL.md +3 -3
- package/src/builtins/autodoc.workflow.ts +325 -0
- package/src/builtins/autoimplement.workflow.ts +493 -23
- package/src/builtins/{autodevise.workflow.ts → autoplan.workflow.ts} +32 -32
- package/src/builtins/catalog.ts +8 -4
- package/src/builtins/index.ts +25 -6
- package/src/builtins/monitor.workflow.ts +162 -8
- package/src/builtins/plan-approval.workflow.ts +190 -0
- package/src/builtins/plan-presentation.ts +57 -0
- package/src/extension/decision-channels.ts +1533 -0
- package/src/extension/index.ts +499 -9
- package/src/extension/widget.ts +39 -2
- package/src/extension/workflow-tool.ts +5 -81
- package/src/herdr/setup.ts +429 -39
- package/src/host/rpc-bridge.ts +7 -30
- package/src/render/graph-render.ts +40 -3
- package/src/viewer/cli.ts +22 -10
- package/src/viewer/render.ts +56 -3
- package/src/workflows/composition.ts +16 -1
- package/src/workflows/decision-presentation.ts +501 -0
- package/src/workflows/engine.ts +108 -4
- package/src/workflows/human-decision.ts +819 -0
- package/src/workflows/index.ts +69 -0
- package/src/workflows/schema.ts +17 -0
- package/src/workflows/store.ts +8 -0
- package/src/workflows/tool-input.ts +202 -0
- package/src/workflows/types.ts +265 -0
- package/dist/builtins/autodevise.workflow.js.map +0 -1
- package/examples/workflows/autodevise.workflow.ts +0 -1
package/src/host/rpc-bridge.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { StringEnum } from "@earendil-works/pi-ai";
|
|
2
1
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
3
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
parseWorkflowSubmissionInput,
|
|
4
|
+
WorkflowSubmissionToolParameters,
|
|
5
|
+
} from "../workflows/tool-input.js";
|
|
4
6
|
|
|
5
7
|
export const RPC_SUBMISSION_PREFIX = "PI_WORKFLOWS_STEP_SUBMISSION ";
|
|
6
8
|
|
|
@@ -19,34 +21,9 @@ export default function piWorkflowsRpcBridge(pi: ExtensionAPI) {
|
|
|
19
21
|
"Only call this tool when a workflow step contract in the conversation asks you to.",
|
|
20
22
|
"Pass the exact step and attempt ids from the contract.",
|
|
21
23
|
].join(" "),
|
|
22
|
-
parameters:
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
action: StringEnum(["update"] as const),
|
|
26
|
-
step: Type.String(),
|
|
27
|
-
attempt: Type.String(),
|
|
28
|
-
update: Type.Object(
|
|
29
|
-
{
|
|
30
|
-
type: Type.String(),
|
|
31
|
-
key: Type.String(),
|
|
32
|
-
data: Type.Record(Type.String(), Type.Unknown()),
|
|
33
|
-
},
|
|
34
|
-
{ additionalProperties: false },
|
|
35
|
-
),
|
|
36
|
-
},
|
|
37
|
-
{ additionalProperties: false },
|
|
38
|
-
),
|
|
39
|
-
Type.Object(
|
|
40
|
-
{
|
|
41
|
-
action: StringEnum(["submit"] as const),
|
|
42
|
-
step: Type.String({ description: "Step id from the workflow step contract" }),
|
|
43
|
-
attempt: Type.String({ description: "Attempt id from the workflow step contract" }),
|
|
44
|
-
output: Type.Unknown({ description: "Step output matching the expected shape" }),
|
|
45
|
-
},
|
|
46
|
-
{ additionalProperties: false },
|
|
47
|
-
),
|
|
48
|
-
]),
|
|
49
|
-
async execute(toolCallId, params) {
|
|
24
|
+
parameters: WorkflowSubmissionToolParameters,
|
|
25
|
+
async execute(toolCallId, rawParams) {
|
|
26
|
+
const params = parseWorkflowSubmissionInput(rawParams);
|
|
50
27
|
process.stderr.write(
|
|
51
28
|
`${RPC_SUBMISSION_PREFIX}${JSON.stringify({ ...params, idempotencyKey: toolCallId })}\n`,
|
|
52
29
|
);
|
|
@@ -216,7 +216,7 @@ function cardMetrics(view: GraphView): CardMetrics {
|
|
|
216
216
|
measure(`${nodeTypeBadge("checkpoint")} ${STATUS_GLYPHS[status]} ${STATUS_LABELS[status]}`);
|
|
217
217
|
}
|
|
218
218
|
for (const [nodeId, node] of Object.entries(snapshot.nodes)) {
|
|
219
|
-
measure(sanitizeText(nodeId));
|
|
219
|
+
measure(sanitizeText(hierarchicalNodeLabel(nodeId, node)));
|
|
220
220
|
measure(nodeTypeBadge(node.nodeType, node.actionExecution));
|
|
221
221
|
const labels = nodeBranchLabels(view, nodeId);
|
|
222
222
|
branchRows = Math.max(branchRows, labels.length);
|
|
@@ -305,14 +305,51 @@ function renderCellText(
|
|
|
305
305
|
const durationMs = Date.parse(attempt.finishedAt) - Date.parse(attempt.startedAt);
|
|
306
306
|
elapsed = formatDuration(durationMs);
|
|
307
307
|
}
|
|
308
|
+
const human = node?.humanDecision;
|
|
309
|
+
const waitingSchema =
|
|
310
|
+
state.waitingOn === nodeId &&
|
|
311
|
+
state.finalOutput !== null &&
|
|
312
|
+
typeof state.finalOutput === "object"
|
|
313
|
+
? (state.finalOutput as { schema?: unknown }).schema
|
|
314
|
+
: undefined;
|
|
315
|
+
const waitingRequest =
|
|
316
|
+
waitingSchema === "pi-workflows.human-decision-request.v1" ||
|
|
317
|
+
waitingSchema === "pi-workflows.human-decision-request.v2"
|
|
318
|
+
? (state.finalOutput as {
|
|
319
|
+
audience?: unknown;
|
|
320
|
+
presentationDigest?: unknown;
|
|
321
|
+
presentation?: { summary?: unknown };
|
|
322
|
+
})
|
|
323
|
+
: undefined;
|
|
324
|
+
const selected =
|
|
325
|
+
human !== undefined &&
|
|
326
|
+
state.humanDecision !== undefined &&
|
|
327
|
+
state.humanDecision.nodeId === nodeId
|
|
328
|
+
? human.choices[state.humanDecision.response.choice]
|
|
329
|
+
: undefined;
|
|
330
|
+
const humanDetail =
|
|
331
|
+
human === undefined
|
|
332
|
+
? undefined
|
|
333
|
+
: state.waitingOn === nodeId
|
|
334
|
+
? `human decision · ${sanitizeText(typeof waitingRequest?.audience === "string" ? waitingRequest.audience : human.audience)}${typeof waitingRequest?.presentation?.summary === "string" ? ` · ${sanitizeText(waitingRequest.presentation.summary)}` : ""} · ${Object.values(
|
|
335
|
+
human.choices,
|
|
336
|
+
)
|
|
337
|
+
.map((choice) => sanitizeText(choice.label))
|
|
338
|
+
.join(
|
|
339
|
+
" / ",
|
|
340
|
+
)}${typeof waitingRequest?.presentationDigest === "string" ? ` · ${waitingRequest.presentationDigest.slice(7, 19)}` : ""}`
|
|
341
|
+
: selected !== undefined
|
|
342
|
+
? `human: ${sanitizeText(selected.label)}`
|
|
343
|
+
: undefined;
|
|
308
344
|
const detail =
|
|
309
|
-
|
|
345
|
+
humanDetail ??
|
|
346
|
+
(atLatestStep && state.currentNode === nodeId && state.statusDetail
|
|
310
347
|
? sanitizeText(state.statusDetail)
|
|
311
348
|
: node?.statusDetail
|
|
312
349
|
? sanitizeText(node.statusDetail)
|
|
313
350
|
: node?.summary
|
|
314
351
|
? sanitizeText(node.summary)
|
|
315
|
-
: "";
|
|
352
|
+
: "");
|
|
316
353
|
const branchLines = Array.from({ length: metrics.branchRows }, (_, index) =>
|
|
317
354
|
labels[index] ? `◇ ${labels[index]}` : "",
|
|
318
355
|
);
|
package/src/viewer/cli.ts
CHANGED
|
@@ -4,7 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
5
|
import { SqliteControllerStore } from "../controllers/sqlite.js";
|
|
6
6
|
import { projectControllerStoreBaseDir } from "../controllers/store.js";
|
|
7
|
-
import {
|
|
7
|
+
import { syncHerdrPlugin } from "../herdr/setup.js";
|
|
8
8
|
import { sanitizeText } from "../render/ansi.js";
|
|
9
9
|
import { listRunBundles, readRunBundle, workflowRunsBaseDir } from "../workflows/store.js";
|
|
10
10
|
import {
|
|
@@ -24,7 +24,8 @@ Usage:
|
|
|
24
24
|
pi-workflows controllers [--controller-dir <dir>]
|
|
25
25
|
pi-workflows controller <controller> <key> [--controller-dir <dir>]
|
|
26
26
|
pi-workflows host [--project <dir>] [-- <extra pi args>]
|
|
27
|
-
pi-workflows herdr
|
|
27
|
+
pi-workflows herdr sync [--json]
|
|
28
|
+
pi-workflows herdr setup [--json]
|
|
28
29
|
|
|
29
30
|
Commands:
|
|
30
31
|
view Open the live workflow TUI. With --once, print a snapshot.
|
|
@@ -32,13 +33,14 @@ Commands:
|
|
|
32
33
|
controllers List durable controller resources.
|
|
33
34
|
controller Show one resource, its effects, child workflows, and events.
|
|
34
35
|
host Run the always-on workflow host in the foreground.
|
|
35
|
-
herdr
|
|
36
|
+
herdr Synchronize the bundled Herdr plugin. setup is an alias for sync.
|
|
36
37
|
|
|
37
38
|
Options:
|
|
38
39
|
--dir <runsDir> Runs directory (default: ~/.pi/agent/workflows/runs)
|
|
39
40
|
--controller-dir <dir> Controller directory (default: project-scoped local store)
|
|
40
41
|
--once Render once without the interactive TUI
|
|
41
42
|
--project <dir> Project directory for the host (default: cwd)
|
|
43
|
+
--json Print a versioned JSON result for herdr sync
|
|
42
44
|
`;
|
|
43
45
|
|
|
44
46
|
export type CliArgs = {
|
|
@@ -50,6 +52,7 @@ export type CliArgs = {
|
|
|
50
52
|
dir: string;
|
|
51
53
|
controllerDir: string;
|
|
52
54
|
once: boolean;
|
|
55
|
+
json: boolean;
|
|
53
56
|
project?: string | undefined;
|
|
54
57
|
piArgs?: string[] | undefined;
|
|
55
58
|
};
|
|
@@ -60,6 +63,7 @@ export function parseCliArgs(argv: string[]): CliArgs {
|
|
|
60
63
|
let dir = workflowRunsBaseDir();
|
|
61
64
|
let controllerDir = projectControllerStoreBaseDir(process.cwd());
|
|
62
65
|
let once = false;
|
|
66
|
+
let json = false;
|
|
63
67
|
const positionals: string[] = [];
|
|
64
68
|
let project: string | undefined;
|
|
65
69
|
const piArgs: string[] = [];
|
|
@@ -74,8 +78,10 @@ export function parseCliArgs(argv: string[]): CliArgs {
|
|
|
74
78
|
project = requiredValue(args, "--project");
|
|
75
79
|
} else if (arg === "--once") {
|
|
76
80
|
once = true;
|
|
81
|
+
} else if (arg === "--json") {
|
|
82
|
+
json = true;
|
|
77
83
|
} else if (arg === "--help" || arg === "-h") {
|
|
78
|
-
return { command: "help", dir, controllerDir, once };
|
|
84
|
+
return { command: "help", dir, controllerDir, once, json };
|
|
79
85
|
} else if (arg === "--") {
|
|
80
86
|
piArgs.push(...args.splice(0));
|
|
81
87
|
} else if (arg.startsWith("-")) {
|
|
@@ -85,8 +91,12 @@ export function parseCliArgs(argv: string[]): CliArgs {
|
|
|
85
91
|
}
|
|
86
92
|
}
|
|
87
93
|
|
|
94
|
+
if (command !== "herdr" && json) {
|
|
95
|
+
throw new Error("--json is available only for herdr sync");
|
|
96
|
+
}
|
|
97
|
+
|
|
88
98
|
if (command === "host") {
|
|
89
|
-
return { command, dir, controllerDir, once, project, piArgs };
|
|
99
|
+
return { command, dir, controllerDir, once, json, project, piArgs };
|
|
90
100
|
}
|
|
91
101
|
|
|
92
102
|
if (command === "controller") {
|
|
@@ -100,13 +110,14 @@ export function parseCliArgs(argv: string[]): CliArgs {
|
|
|
100
110
|
dir,
|
|
101
111
|
controllerDir,
|
|
102
112
|
once,
|
|
113
|
+
json,
|
|
103
114
|
};
|
|
104
115
|
}
|
|
105
116
|
if (command === "herdr") {
|
|
106
|
-
if (positionals.length !== 1 || positionals[0] !== "setup") {
|
|
107
|
-
throw new Error("herdr requires the
|
|
117
|
+
if (positionals.length !== 1 || (positionals[0] !== "sync" && positionals[0] !== "setup")) {
|
|
118
|
+
throw new Error("herdr requires the sync action");
|
|
108
119
|
}
|
|
109
|
-
return { command, herdrAction: positionals[0], dir, controllerDir, once };
|
|
120
|
+
return { command, herdrAction: positionals[0], dir, controllerDir, once, json };
|
|
110
121
|
}
|
|
111
122
|
if (positionals.length > 1) {
|
|
112
123
|
throw new Error(`Unexpected argument: ${positionals[1]}`);
|
|
@@ -117,6 +128,7 @@ export function parseCliArgs(argv: string[]): CliArgs {
|
|
|
117
128
|
dir,
|
|
118
129
|
controllerDir,
|
|
119
130
|
once,
|
|
131
|
+
json,
|
|
120
132
|
};
|
|
121
133
|
}
|
|
122
134
|
|
|
@@ -240,8 +252,8 @@ export async function main(argv: string[] = process.argv.slice(2)): Promise<numb
|
|
|
240
252
|
return await runHost(args.project ?? process.cwd(), args.piArgs);
|
|
241
253
|
}
|
|
242
254
|
if (args.command === "herdr") {
|
|
243
|
-
const result =
|
|
244
|
-
process.stdout.write(`${result.message}\n`);
|
|
255
|
+
const result = syncHerdrPlugin(packageRoot());
|
|
256
|
+
process.stdout.write(args.json ? `${JSON.stringify(result)}\n` : `${result.message}\n`);
|
|
245
257
|
return 0;
|
|
246
258
|
}
|
|
247
259
|
if (args.command === "view") {
|
package/src/viewer/render.ts
CHANGED
|
@@ -2,13 +2,22 @@ import { ansi, fitWidth, sanitizeText } from "../render/ansi.js";
|
|
|
2
2
|
import { formatDuration, runElapsedMs } from "../render/format.js";
|
|
3
3
|
import { renderGraphLines } from "../render/graph-render.js";
|
|
4
4
|
import { decodeValueWith } from "../workflows/artifacts.js";
|
|
5
|
+
import {
|
|
6
|
+
decisionDocumentSegments,
|
|
7
|
+
decisionPresentationFingerprint,
|
|
8
|
+
humanDecisionChannelRequest,
|
|
9
|
+
} from "../workflows/decision-presentation.js";
|
|
5
10
|
import {
|
|
6
11
|
formatProgressLine,
|
|
7
12
|
progressRecordsFromTrace,
|
|
8
13
|
progressTracksFromRecords,
|
|
9
14
|
} from "../workflows/progress.js";
|
|
10
15
|
import type { LoadedRunBundle } from "../workflows/store.js";
|
|
11
|
-
import type {
|
|
16
|
+
import type {
|
|
17
|
+
HumanDecisionRequest,
|
|
18
|
+
WorkflowRunStatus,
|
|
19
|
+
WorkflowStepRecord,
|
|
20
|
+
} from "../workflows/types.js";
|
|
12
21
|
|
|
13
22
|
export { formatDuration, runElapsedMs };
|
|
14
23
|
|
|
@@ -132,10 +141,31 @@ function nodeStatusLine(bundle: LoadedRunBundle, nodeId: string, width: number,
|
|
|
132
141
|
suffix = ansi.cyan(` running ${formatDuration(now.getTime() - startedAt)}${detail}`);
|
|
133
142
|
} else if (state.waitingOn === nodeId) {
|
|
134
143
|
glyph = ansi.yellow("⏸");
|
|
135
|
-
|
|
144
|
+
const human = bundle.snapshot?.nodes[nodeId]?.humanDecision;
|
|
145
|
+
const request = humanDecisionRequest(state.finalOutput);
|
|
146
|
+
const requestAudience = request?.audience ?? human?.audience;
|
|
147
|
+
suffix = ansi.yellow(
|
|
148
|
+
human === undefined
|
|
149
|
+
? " waiting"
|
|
150
|
+
: ` waiting for human · ${sanitizeText(requestAudience ?? "operator")}${request?.schema === "pi-workflows.human-decision-request.v2" ? ` · ${sanitizeText(request.presentation.summary)}` : ""} · ${Object.values(
|
|
151
|
+
human.choices,
|
|
152
|
+
)
|
|
153
|
+
.map((choice) => sanitizeText(choice.label))
|
|
154
|
+
.join(
|
|
155
|
+
" / ",
|
|
156
|
+
)}${request?.schema === "pi-workflows.human-decision-request.v2" ? ` · ${request.presentationDigest.slice(7, 19)}` : ""}`,
|
|
157
|
+
);
|
|
136
158
|
} else if (result) {
|
|
137
159
|
glyph = result.outcome === "ok" ? ansi.green("✓") : ansi.red("✗");
|
|
138
|
-
|
|
160
|
+
const human = bundle.snapshot?.nodes[nodeId]?.humanDecision;
|
|
161
|
+
const accepted = state.humanDecision;
|
|
162
|
+
const selected =
|
|
163
|
+
human !== undefined && accepted !== undefined && accepted.nodeId === nodeId
|
|
164
|
+
? human.choices[accepted.response.choice]
|
|
165
|
+
: undefined;
|
|
166
|
+
suffix = ansi.dim(
|
|
167
|
+
` ${formatDuration(result.durationMs)}${selected === undefined ? "" : ` · human: ${sanitizeText(selected.label)}`}`,
|
|
168
|
+
);
|
|
139
169
|
}
|
|
140
170
|
return fitWidth(` ${glyph} ${nodeId} ${ansi.dim(`[${nodeType}]`)}${suffix}`, width);
|
|
141
171
|
}
|
|
@@ -143,6 +173,20 @@ function nodeStatusLine(bundle: LoadedRunBundle, nodeId: string, width: number,
|
|
|
143
173
|
/** Pretty-printed JSON body of the selected step for the inspector pane. */
|
|
144
174
|
function inspectorLines(step: WorkflowStepRecord, width: number): string[] {
|
|
145
175
|
const lines: string[] = [];
|
|
176
|
+
const request = step.error === undefined ? humanDecisionRequest(step.output) : null;
|
|
177
|
+
if (request !== null) {
|
|
178
|
+
const channelRequest = humanDecisionChannelRequest(request);
|
|
179
|
+
for (const segment of decisionDocumentSegments(channelRequest)) {
|
|
180
|
+
for (const raw of segment.text.split("\n")) {
|
|
181
|
+
lines.push(fitWidth(` ${sanitizeText(raw)}`, width));
|
|
182
|
+
}
|
|
183
|
+
lines.push("");
|
|
184
|
+
}
|
|
185
|
+
lines.push(
|
|
186
|
+
fitWidth(ansi.dim(` decision ${decisionPresentationFingerprint(channelRequest)}`), width),
|
|
187
|
+
);
|
|
188
|
+
return lines;
|
|
189
|
+
}
|
|
146
190
|
const body = step.error !== undefined ? step.error : withArtifactPlaceholders(step.output);
|
|
147
191
|
const rendered =
|
|
148
192
|
typeof body === "string" && step.error !== undefined ? body : JSON.stringify(body, null, 2);
|
|
@@ -163,6 +207,15 @@ function inspectorLines(step: WorkflowStepRecord, width: number): string[] {
|
|
|
163
207
|
return lines;
|
|
164
208
|
}
|
|
165
209
|
|
|
210
|
+
function humanDecisionRequest(value: unknown): HumanDecisionRequest | null {
|
|
211
|
+
if (value === null || typeof value !== "object") return null;
|
|
212
|
+
const schema = (value as { schema?: unknown }).schema;
|
|
213
|
+
return schema === "pi-workflows.human-decision-request.v1" ||
|
|
214
|
+
schema === "pi-workflows.human-decision-request.v2"
|
|
215
|
+
? (value as HumanDecisionRequest)
|
|
216
|
+
: null;
|
|
217
|
+
}
|
|
218
|
+
|
|
166
219
|
/**
|
|
167
220
|
* Full-run detail view: header, graph pane, step timeline, inspector.
|
|
168
221
|
* `scroll` shifts the viewport down over the full body; `selectedStepIndex`
|
|
@@ -477,7 +477,9 @@ function wrapNode(
|
|
|
477
477
|
message: (context) => (node as NotifyNodeDefinition).message(project(context)),
|
|
478
478
|
...(node.kind !== undefined ? { kind: node.kind } : {}),
|
|
479
479
|
};
|
|
480
|
-
case "checkpoint":
|
|
480
|
+
case "checkpoint": {
|
|
481
|
+
const human = node.humanDecision;
|
|
482
|
+
const audience = human?.audience;
|
|
481
483
|
return {
|
|
482
484
|
...common,
|
|
483
485
|
nodeType: "checkpoint",
|
|
@@ -485,7 +487,20 @@ function wrapNode(
|
|
|
485
487
|
...(node.run !== undefined
|
|
486
488
|
? { run: (context: WorkflowNodeContext) => node.run?.(project(context)) }
|
|
487
489
|
: {}),
|
|
490
|
+
...(human !== undefined
|
|
491
|
+
? {
|
|
492
|
+
humanDecision: {
|
|
493
|
+
audience:
|
|
494
|
+
typeof audience === "function"
|
|
495
|
+
? (context: WorkflowNodeContext) => audience(project(context))
|
|
496
|
+
: audience!,
|
|
497
|
+
choices: human.choices,
|
|
498
|
+
request: (context: WorkflowNodeContext) => human.request(project(context)),
|
|
499
|
+
},
|
|
500
|
+
}
|
|
501
|
+
: {}),
|
|
488
502
|
} as CheckpointNodeDefinition;
|
|
503
|
+
}
|
|
489
504
|
case "action":
|
|
490
505
|
if ("exec" in node) {
|
|
491
506
|
const shell = node as ShellActionNodeDefinition;
|