@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.
Files changed (135) hide show
  1. package/README.md +32 -14
  2. package/dist/builtins/autodoc.workflow.d.ts +58 -0
  3. package/dist/builtins/autodoc.workflow.js +266 -0
  4. package/dist/builtins/autodoc.workflow.js.map +1 -0
  5. package/dist/builtins/autoimplement.workflow.d.ts +212 -11
  6. package/dist/builtins/autoimplement.workflow.js +431 -22
  7. package/dist/builtins/autoimplement.workflow.js.map +1 -1
  8. package/dist/builtins/{autodevise.workflow.d.ts → autoplan.workflow.d.ts} +9 -9
  9. package/dist/builtins/{autodevise.workflow.js → autoplan.workflow.js} +20 -20
  10. package/dist/builtins/autoplan.workflow.js.map +1 -0
  11. package/dist/builtins/catalog.js +8 -4
  12. package/dist/builtins/catalog.js.map +1 -1
  13. package/dist/builtins/index.d.ts +6 -3
  14. package/dist/builtins/index.js +5 -2
  15. package/dist/builtins/index.js.map +1 -1
  16. package/dist/builtins/monitor.workflow.d.ts +4 -0
  17. package/dist/builtins/monitor.workflow.js +134 -9
  18. package/dist/builtins/monitor.workflow.js.map +1 -1
  19. package/dist/builtins/plan-approval.workflow.d.ts +83 -0
  20. package/dist/builtins/plan-approval.workflow.js +148 -0
  21. package/dist/builtins/plan-approval.workflow.js.map +1 -0
  22. package/dist/builtins/plan-presentation.d.ts +7 -0
  23. package/dist/builtins/plan-presentation.js +44 -0
  24. package/dist/builtins/plan-presentation.js.map +1 -0
  25. package/dist/extension/decision-channels.d.ts +132 -0
  26. package/dist/extension/decision-channels.js +1082 -0
  27. package/dist/extension/decision-channels.js.map +1 -0
  28. package/dist/extension/index.d.ts +1 -0
  29. package/dist/extension/index.js +426 -11
  30. package/dist/extension/index.js.map +1 -1
  31. package/dist/extension/widget.js +26 -2
  32. package/dist/extension/widget.js.map +1 -1
  33. package/dist/extension/workflow-tool.d.ts +1 -37
  34. package/dist/extension/workflow-tool.js +1 -42
  35. package/dist/extension/workflow-tool.js.map +1 -1
  36. package/dist/herdr/setup.d.ts +13 -1
  37. package/dist/herdr/setup.js +349 -36
  38. package/dist/herdr/setup.js.map +1 -1
  39. package/dist/host/rpc-bridge.js +4 -21
  40. package/dist/host/rpc-bridge.js.map +1 -1
  41. package/dist/render/graph-render.js +33 -8
  42. package/dist/render/graph-render.js.map +1 -1
  43. package/dist/viewer/cli.d.ts +1 -0
  44. package/dist/viewer/cli.js +21 -10
  45. package/dist/viewer/cli.js.map +1 -1
  46. package/dist/viewer/render.js +36 -2
  47. package/dist/viewer/render.js.map +1 -1
  48. package/dist/workflows/composition.js +15 -1
  49. package/dist/workflows/composition.js.map +1 -1
  50. package/dist/workflows/decision-presentation.d.ts +18 -0
  51. package/dist/workflows/decision-presentation.js +417 -0
  52. package/dist/workflows/decision-presentation.js.map +1 -0
  53. package/dist/workflows/engine.d.ts +2 -1
  54. package/dist/workflows/engine.js +86 -4
  55. package/dist/workflows/engine.js.map +1 -1
  56. package/dist/workflows/human-decision.d.ts +87 -0
  57. package/dist/workflows/human-decision.js +583 -0
  58. package/dist/workflows/human-decision.js.map +1 -0
  59. package/dist/workflows/index.d.ts +3 -1
  60. package/dist/workflows/index.js +2 -0
  61. package/dist/workflows/index.js.map +1 -1
  62. package/dist/workflows/schema.js +15 -0
  63. package/dist/workflows/schema.js.map +1 -1
  64. package/dist/workflows/store.js +7 -0
  65. package/dist/workflows/store.js.map +1 -1
  66. package/dist/workflows/tool-input.d.ts +72 -0
  67. package/dist/workflows/tool-input.js +141 -0
  68. package/dist/workflows/tool-input.js.map +1 -0
  69. package/dist/workflows/types.d.ts +223 -0
  70. package/docs/HUMAN_DECISIONS.md +371 -0
  71. package/docs/HUMAN_DECISION_PRESENTATIONS.md +323 -0
  72. package/docs/MONITOR.md +17 -6
  73. package/docs/WORKFLOW_COMPOSITION.md +13 -6
  74. package/docs/plans/2026-08-19-human-decision-gates-plan.md +296 -0
  75. package/docs/plans/2026-08-19-human-decision-presentations-plan.md +172 -0
  76. package/docs/plans/2026-08-19-provider-compatible-workflow-tool-schema-plan.md +45 -0
  77. package/docs/plans/2026-08-19-workflow-composition-plan.md +25 -17
  78. package/docs/plans/2026-08-20-autoimplement-blocker-challenge-plan.md +138 -0
  79. package/docs/plans/2026-08-20-herdr-plugin-sync-plan.md +104 -0
  80. package/docs/run-bundles.md +23 -2
  81. package/docs/workflows.md +47 -5
  82. package/examples/workflows/approved-plan.workflow.ts +58 -0
  83. package/examples/workflows/autoplan.workflow.ts +1 -0
  84. package/examples/workflows/human-decision.workflow.ts +62 -0
  85. package/herdr-plugin.toml +1 -1
  86. package/package.json +2 -1
  87. package/schemas/decision-presentation-v1.schema.json +83 -0
  88. package/schemas/human-decision-accepted-v1.schema.json +44 -0
  89. package/schemas/human-decision-accepted-v2.schema.json +50 -0
  90. package/schemas/human-decision-answer-attempt-v1.schema.json +37 -0
  91. package/schemas/human-decision-cancellation-v1.schema.json +15 -0
  92. package/schemas/human-decision-continuation-v1.schema.json +16 -0
  93. package/schemas/human-decision-delivery-v1.schema.json +28 -0
  94. package/schemas/human-decision-delivery-v2.schema.json +36 -0
  95. package/schemas/human-decision-receipt-v1.schema.json +33 -0
  96. package/schemas/human-decision-receipt-v2.schema.json +39 -0
  97. package/schemas/human-decision-request-v1.schema.json +59 -0
  98. package/schemas/human-decision-request-v2.schema.json +69 -0
  99. package/schemas/human-decision-resolution-v1.schema.json +27 -0
  100. package/schemas/human-decision-resolution-v2.schema.json +27 -0
  101. package/schemas/human-decision-settlement-v1.schema.json +28 -0
  102. package/skills/autodoc/SKILL.md +43 -0
  103. package/skills/autoimplement/SKILL.md +58 -0
  104. package/skills/autoimplement/agents/openai.yaml +4 -0
  105. package/skills/autoplan/SKILL.md +25 -0
  106. package/skills/monitor/SKILL.md +3 -1
  107. package/skills/pi-workflows/SKILL.md +3 -3
  108. package/src/builtins/autodoc.workflow.ts +325 -0
  109. package/src/builtins/autoimplement.workflow.ts +493 -23
  110. package/src/builtins/{autodevise.workflow.ts → autoplan.workflow.ts} +32 -32
  111. package/src/builtins/catalog.ts +8 -4
  112. package/src/builtins/index.ts +25 -6
  113. package/src/builtins/monitor.workflow.ts +162 -8
  114. package/src/builtins/plan-approval.workflow.ts +190 -0
  115. package/src/builtins/plan-presentation.ts +57 -0
  116. package/src/extension/decision-channels.ts +1533 -0
  117. package/src/extension/index.ts +499 -9
  118. package/src/extension/widget.ts +39 -2
  119. package/src/extension/workflow-tool.ts +5 -81
  120. package/src/herdr/setup.ts +429 -39
  121. package/src/host/rpc-bridge.ts +7 -30
  122. package/src/render/graph-render.ts +40 -3
  123. package/src/viewer/cli.ts +22 -10
  124. package/src/viewer/render.ts +56 -3
  125. package/src/workflows/composition.ts +16 -1
  126. package/src/workflows/decision-presentation.ts +501 -0
  127. package/src/workflows/engine.ts +108 -4
  128. package/src/workflows/human-decision.ts +819 -0
  129. package/src/workflows/index.ts +69 -0
  130. package/src/workflows/schema.ts +17 -0
  131. package/src/workflows/store.ts +8 -0
  132. package/src/workflows/tool-input.ts +202 -0
  133. package/src/workflows/types.ts +265 -0
  134. package/dist/builtins/autodevise.workflow.js.map +0 -1
  135. package/examples/workflows/autodevise.workflow.ts +0 -1
@@ -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 { Type } from "typebox";
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: Type.Union([
23
- Type.Object(
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
- atLatestStep && state.currentNode === nodeId && state.statusDetail
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 { setupHerdrPlugin } from "../herdr/setup.js";
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 setup
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 Set up the bundled Herdr plugin.
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 setup action");
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 = setupHerdrPlugin(packageRoot());
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") {
@@ -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 { WorkflowRunStatus, WorkflowStepRecord } from "../workflows/types.js";
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
- suffix = ansi.yellow(" waiting");
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
- suffix = ansi.dim(` ${formatDuration(result.durationMs)}`);
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;