pi-extensible-workflows 3.2.0 → 3.3.0

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/dist/src/cli.js CHANGED
@@ -8,7 +8,8 @@ import { ProjectTrustStore, SessionManager, SettingsManager, createAgentSessionF
8
8
  import { Value } from "typebox/value";
9
9
  import { doctor, doctorExitCode, formatDoctorReport } from "./doctor.js";
10
10
  import { doctorCleanup, doctorCleanupExitCode, formatDoctorCleanupReport } from "./doctor-cleanup.js";
11
- import workflowExtension, { formatWorkflowProgress, truncateWorkflowProgress, workflowCatalog, workflowSettingsPath } from "./index.js";
11
+ import workflowExtension, { formatWorkflowProgress, loadAgentDefinitions, registeredWorkflowFunctions, truncateWorkflowProgress, workflowCatalog, workflowSettingsPath } from "./index.js";
12
+ import { portableEngineVersion, portablePiVersion, writePortableWorkflowBundle } from "./bundles.js";
12
13
  import { runSessionInspector, transcriptFileLines } from "./session-inspector.js";
13
14
  function object(value) { return typeof value === "object" && value !== null && !Array.isArray(value); }
14
15
  function has(value, key) { return Object.prototype.hasOwnProperty.call(value, key); }
@@ -193,7 +194,30 @@ function launcherHelpLines() {
193
194
  ];
194
195
  }
195
196
  function workflowUsage() { return [`Usage: pi-extensible-workflows run <workflow-name> [workflow arguments] | export <workflow-name> [--name <command>] [--output <path>] [--force]`, "", "Launcher options:", ...launcherHelpLines()].join("\n") + "\n"; }
196
- function exportUsage() { return [`Usage: pi-extensible-workflows export <workflow-name> [--name <command>] [--output <path>] [--force]`, "", "Launcher options:", ...launcherHelpLines()].join("\n") + "\n"; }
197
+ function exportUsage() { return [`Usage: pi-extensible-workflows export <workflow-name> [--name <command>] [--output <path>] [--force] [--bundle]`, "", "Launcher options:", ...launcherHelpLines()].join("\n") + "\n"; }
198
+ function bundleUsage() { return [`Usage: pi-extensible-workflows bundle <workflow-name> [--name <command>] [--output <directory>] [--force]`, "", "The bundle contains a launcher, manifest, workflow payload, and external-runtime setup instructions.", "Repeat --role, --alias, --tool, --command, or --environment to declare recipient requirements.", "Use --extension, --skill, --resource, and --dependency to copy selected payload resources."].join("\n") + "\n"; }
199
+ function parseInspectArgs(rawArgs) {
200
+ let sessionId;
201
+ let mode = "tui";
202
+ let failedOnly = false;
203
+ for (const arg of rawArgs) {
204
+ if (arg === "--json" || arg === "--summary") {
205
+ const next = arg === "--json" ? "json" : "summary";
206
+ if (mode !== "tui" && mode !== next)
207
+ throw new Error("inspect accepts only one output mode");
208
+ mode = next;
209
+ }
210
+ else if (arg === "--failed")
211
+ failedOnly = true;
212
+ else if (arg.startsWith("--"))
213
+ throw new Error(`Unknown inspect option: ${arg}`);
214
+ else if (sessionId !== undefined)
215
+ throw new Error(`Unexpected argument: ${arg}`);
216
+ else
217
+ sessionId = arg;
218
+ }
219
+ return { ...(sessionId ? { sessionId } : {}), mode: failedOnly && mode === "tui" ? "summary" : mode, failedOnly };
220
+ }
197
221
  export function parseDoctorCleanupArgs(rawArgs) {
198
222
  let olderThanDays = 90;
199
223
  let yes = false;
@@ -286,7 +310,7 @@ async function createWorkflowRuntime(options, shutdownHandlers = []) {
286
310
  cwd,
287
311
  agentDir,
288
312
  settingsManager,
289
- resourceLoaderOptions: {},
313
+ resourceLoaderOptions: { ...(options.skillPaths?.length ? { additionalSkillPaths: [...options.skillPaths] } : {}) },
290
314
  resourceLoaderReloadOptions: { resolveProjectTrust },
291
315
  });
292
316
  const extensions = services.resourceLoader.getExtensions();
@@ -303,7 +327,7 @@ async function createWorkflowRuntime(options, shutdownHandlers = []) {
303
327
  sendMessage() { },
304
328
  events: { emit() { } },
305
329
  };
306
- workflowExtension(headlessPi, homedir(), undefined, undefined, agentDir);
330
+ workflowExtension(headlessPi, homedir(), undefined, undefined, agentDir, options.skillPaths);
307
331
  const workflowTool = tools.find((tool) => object(tool) && tool.name === "workflow");
308
332
  if (!workflowTool)
309
333
  throw new Error("The workflow runtime could not be initialized");
@@ -356,28 +380,33 @@ function terminalProgressStyles(enabled) {
356
380
  }
357
381
  class CliProgress {
358
382
  stderr;
383
+ onRunId;
359
384
  #lastStable = "";
360
385
  #lines = 0;
361
386
  #frame = 0;
362
387
  #run;
388
+ #runId;
363
389
  #timer;
364
390
  #interactive;
365
391
  #styles;
366
- constructor(stderr, tty) {
392
+ constructor(stderr, tty, onRunId) {
367
393
  this.stderr = stderr;
394
+ this.onRunId = onRunId;
368
395
  this.#interactive = tty && process.env.NO_COLOR === undefined && process.env.TERM !== "dumb";
369
396
  this.#styles = terminalProgressStyles(this.#interactive);
370
397
  }
371
398
  update(run) {
372
- const stable = formatWorkflowProgress(run, "◇", this.#styles);
399
+ if (this.#runId !== run.id) {
400
+ this.#runId = run.id;
401
+ this.onRunId(run.id);
402
+ }
403
+ this.#run = run;
373
404
  if (!this.#interactive) {
374
- if (stable !== this.#lastStable) {
375
- this.#lastStable = stable;
376
- this.stderr(`${stable}\n`);
377
- }
405
+ this.#timer ??= setInterval(() => { this.render(); }, 1000);
406
+ this.#timer.unref();
407
+ this.render();
378
408
  return;
379
409
  }
380
- this.#run = run;
381
410
  this.#timer ??= setInterval(() => { this.render(); }, 80);
382
411
  this.#timer.unref();
383
412
  this.render();
@@ -385,6 +414,14 @@ class CliProgress {
385
414
  render() {
386
415
  if (!this.#run)
387
416
  return;
417
+ if (!this.#interactive) {
418
+ const stable = formatWorkflowProgress(this.#run, "◇", this.#styles);
419
+ if (stable !== this.#lastStable) {
420
+ this.#lastStable = stable;
421
+ this.stderr(`${stable}\n`);
422
+ }
423
+ return;
424
+ }
388
425
  const spinner = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"][this.#frame++ % 10] ?? "◇";
389
426
  const width = process.stderr.columns || 80;
390
427
  const text = truncateWorkflowProgress(formatWorkflowProgress(this.#run, spinner, this.#styles), width).join("\n");
@@ -406,18 +443,24 @@ class CliProgress {
406
443
  async function invokeWorkflow(fn, args, runtime, options, context) {
407
444
  if (!Value.Check(fn.input, args))
408
445
  throw new Error(`Invalid input for ${fn.name}`);
409
- const progress = new CliProgress(options.stderr, options.isTTY ?? process.stderr.isTTY);
446
+ let announcedRunId;
447
+ const announceRunId = (runId) => { if (announcedRunId === runId)
448
+ return; announcedRunId = runId; options.stderr(`Run ID: ${runId}\n`); };
449
+ const progress = new CliProgress(options.stderr, options.isTTY ?? process.stderr.isTTY, announceRunId);
410
450
  try {
411
451
  const result = await runtime.workflowTool.execute(randomUUID(), { workflow: fn.name, args, foreground: true }, options.signal, (update) => { if (object(update) && object(update.details) && object(update.details.run))
412
452
  progress.update(update.details.run); }, context);
413
453
  const details = object(result.details) ? result.details : {};
454
+ const runId = typeof details.runId === "string" ? details.runId : undefined;
455
+ if (runId)
456
+ announceRunId(runId);
414
457
  if (has(details, "value"))
415
- return details.value;
458
+ return { value: details.value, ...(runId ? { runId } : {}) };
416
459
  const first = result.content[0];
417
460
  if (!first || first.type !== "text")
418
461
  throw new Error("Workflow returned no result");
419
462
  try {
420
- return parseJsonInput(first.text);
463
+ return { value: parseJsonInput(first.text), ...(runId ? { runId } : {}) };
421
464
  }
422
465
  catch {
423
466
  throw new Error("Workflow returned invalid JSON");
@@ -471,14 +514,16 @@ async function runWorkflowCli(rawArgs, options) {
471
514
  return 0;
472
515
  }
473
516
  const input = parseWorkflowCliArgs(fn.input, args.slice(1));
474
- const value = await invokeWorkflow(fn, input, runtime, options, context);
475
- options.write(`${JSON.stringify(value)}\n`);
517
+ const result = await invokeWorkflow(fn, input, runtime, options, context);
518
+ options.write(`${JSON.stringify(result.value)}\n`);
476
519
  return 0;
477
520
  });
478
521
  }
479
522
  async function exportWorkflowCli(rawArgs, options) {
480
523
  const parsed = stripTrustOptions(rawArgs);
481
524
  const args = parsed.args;
525
+ if (args.includes("--bundle"))
526
+ return bundleWorkflowCli(args.filter((arg) => arg !== "--bundle"), options);
482
527
  if (!args.length || args[0] === "--help" || args[0] === "-h") {
483
528
  options.write(exportUsage());
484
529
  return args.length ? 0 : 1;
@@ -534,6 +579,80 @@ async function exportWorkflowCli(rawArgs, options) {
534
579
  return 0;
535
580
  });
536
581
  }
582
+ async function bundleWorkflowCli(rawArgs, options) {
583
+ const parsed = stripTrustOptions(rawArgs);
584
+ const args = parsed.args;
585
+ if (!args.length || args[0] === "--help" || args[0] === "-h") {
586
+ options.write(bundleUsage());
587
+ return args.length ? 0 : 1;
588
+ }
589
+ const workflowName = args[0];
590
+ return withWorkflowRuntime({ ...options, ...(parsed.trustOverride !== undefined ? { trustOverride: parsed.trustOverride } : {}) }, async (runtime) => {
591
+ let name;
592
+ let output;
593
+ let force = false;
594
+ const requirements = { roles: [], aliases: [], tools: [], commands: [], environment: [] };
595
+ const resources = { extensions: [], skills: [], static: [], dependencies: [] };
596
+ for (let index = 1; index < args.length; index += 1) {
597
+ const arg = args[index];
598
+ if (arg === "--force") {
599
+ force = true;
600
+ continue;
601
+ }
602
+ const equals = arg.indexOf("=");
603
+ const option = equals >= 0 ? arg.slice(0, equals) : arg;
604
+ const requirementOptions = { "--role": "roles", "--alias": "aliases", "--tool": "tools", "--command": "commands", "--environment": "environment" };
605
+ const resourceOptions = { "--extension": "extensions", "--skill": "skills", "--resource": "static", "--dependency": "dependencies" };
606
+ if (option === "--name" || option === "--output" || Object.prototype.hasOwnProperty.call(requirementOptions, option) || Object.prototype.hasOwnProperty.call(resourceOptions, option)) {
607
+ const value = equals >= 0 ? arg.slice(equals + 1) : args[++index];
608
+ if (!value)
609
+ throw new Error(`Missing value for ${option}`);
610
+ if (option === "--name")
611
+ name = value;
612
+ else if (option === "--output")
613
+ output = value;
614
+ else if (Object.prototype.hasOwnProperty.call(requirementOptions, option))
615
+ requirements[requirementOptions[option]].push(value);
616
+ else
617
+ resources[resourceOptions[option]].push(value);
618
+ continue;
619
+ }
620
+ if (arg === "--help" || arg === "-h") {
621
+ options.write(bundleUsage());
622
+ return 0;
623
+ }
624
+ throw new Error(`Unknown option: ${arg}`);
625
+ }
626
+ const fn = runtime.catalog.functions.find((candidate) => candidate.name === workflowName);
627
+ if (!fn)
628
+ throw new Error(`Unknown workflow function: ${workflowName}`);
629
+ const registered = registeredWorkflowFunctions()[workflowName];
630
+ if (!registered)
631
+ throw new Error(`Workflow ${workflowName} is not exportable because its registered implementation is unavailable`);
632
+ const definitions = requirements.roles.length ? loadAgentDefinitions(options.cwd ?? process.cwd(), options.agentDir ?? getAgentDir(), runtime.services.settingsManager.isProjectTrusted()) : {};
633
+ const roles = Object.fromEntries(requirements.roles.map((role) => {
634
+ if (!role || role === "." || role === ".." || role.includes("/") || role.includes("\\"))
635
+ throw new Error(`Invalid role name for bundle: ${role}`);
636
+ const definition = definitions[role];
637
+ if (!definition)
638
+ throw new Error(`Unknown role for bundle: ${role}`);
639
+ return [role, definition];
640
+ }));
641
+ const command = commandName(name ?? kebabCase(workflowName));
642
+ if (!command)
643
+ throw new Error("Command name must be a non-empty name without path separators");
644
+ const destination = output ?? join(homedir(), ".local", "share", "pi-extensible-workflows", "bundles", command);
645
+ const aliasTargets = Object.fromEntries(requirements.aliases.flatMap((name) => {
646
+ const target = runtime.catalog.modelAliases?.[name];
647
+ return typeof target === "string" ? [[name, target]] : [];
648
+ }));
649
+ const selectedResources = Object.values(resources).some((entries) => entries.length) ? resources : undefined;
650
+ writePortableWorkflowBundle({ destination, command, workflow: fn, functionSource: registered.run.toString(), requirements, aliasTargets, roles, ...(selectedResources ? { resources: selectedResources } : {}), piVersion: portablePiVersion(), engineVersion: portableEngineVersion(), force });
651
+ options.write(`Bundled ${workflowName} at ${destination}\n`);
652
+ options.write(`Run ${join(destination, command)} setup before launching the workflow.\n`);
653
+ return 0;
654
+ });
655
+ }
537
656
  export async function runCli(args, options = {}, write = (text) => { process.stdout.write(text); }) {
538
657
  const stderr = options.stderr ?? ((text) => { process.stderr.write(text); });
539
658
  if (args[0] === "doctor" && args.length === 1) {
@@ -558,9 +677,13 @@ export async function runCli(args, options = {}, write = (text) => { process.std
558
677
  return 1;
559
678
  }
560
679
  }
561
- if (args[0] === "inspect" && args.length <= 2) {
680
+ if (args[0] === "inspect") {
562
681
  try {
563
- await (options.inspect ?? runSessionInspector)(args[1]);
682
+ const parsed = parseInspectArgs(args.slice(1));
683
+ if (options.inspect)
684
+ await options.inspect(parsed.sessionId, parsed.mode, parsed.failedOnly);
685
+ else
686
+ await runSessionInspector(parsed.sessionId, parsed.mode, options.cwd ?? process.cwd(), undefined, write, parsed.failedOnly);
564
687
  return 0;
565
688
  }
566
689
  catch (error) {
@@ -581,9 +704,11 @@ export async function runCli(args, options = {}, write = (text) => { process.std
581
704
  return 1;
582
705
  }
583
706
  }
584
- if (args[0] === "run" || args[0] === "export") {
707
+ if (args[0] === "bundle" || args[0] === "run" || args[0] === "export") {
585
708
  try {
586
- const workflowOptions = { write, stderr, ...(options.cwd !== undefined ? { cwd: options.cwd } : {}), ...(options.agentDir !== undefined ? { agentDir: options.agentDir } : {}), ...(options.signal ? { signal: options.signal } : {}), ...(options.trustOverride !== undefined ? { trustOverride: options.trustOverride } : {}), ...(options.isTTY !== undefined ? { isTTY: options.isTTY } : {}) };
709
+ const workflowOptions = { write, stderr, ...(options.cwd !== undefined ? { cwd: options.cwd } : {}), ...(options.agentDir !== undefined ? { agentDir: options.agentDir } : {}), ...(options.signal ? { signal: options.signal } : {}), ...(options.trustOverride !== undefined ? { trustOverride: options.trustOverride } : {}), ...(options.isTTY !== undefined ? { isTTY: options.isTTY } : {}), ...(options.skillPaths?.length ? { skillPaths: [...options.skillPaths] } : {}) };
710
+ if (args[0] === "bundle")
711
+ return await bundleWorkflowCli(args.slice(1), workflowOptions);
587
712
  return args[0] === "run" ? await runWorkflowCli(args.slice(1), workflowOptions) : await exportWorkflowCli(args.slice(1), workflowOptions);
588
713
  }
589
714
  catch (error) {
@@ -591,7 +716,7 @@ export async function runCli(args, options = {}, write = (text) => { process.std
591
716
  return 1;
592
717
  }
593
718
  }
594
- write("Usage: pi-extensible-workflows doctor | inspect [session-id] | transcript <session-file> | run <workflow-name> [workflow arguments] | export <workflow-name> [--name <command>] [--output <path>] [--force]\n");
719
+ write("Usage: pi-extensible-workflows doctor | inspect [session-id] [--json|--summary] [--failed] | transcript <session-file> | bundle <workflow-name> [--name <command>] [--output <path>] [--force] | run <workflow-name> [workflow arguments] | export <workflow-name> [--name <command>] [--output <path>] [--force] [--bundle]\n");
595
720
  return 1;
596
721
  }
597
722
  if (process.argv[1] && import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href) {
@@ -10,7 +10,7 @@ import { acquireSessionLease, hasLiveSessionLease, projectSessionsDirectory, Run
10
10
  const TERMINAL_STATES = new Set(["completed", "failed", "stopped"]);
11
11
  const DAY_MS = 24 * 60 * 60 * 1000;
12
12
  const REQUIRED_RUN_FILES = ["workflow.js", "state.json", "snapshot.json", "journal.json", "ownership.json", "worktrees.json", "borrowed-worktrees.json", "system-prompts.json"];
13
- const OPTIONAL_RUN_FILES = new Set(["result.json"]);
13
+ const OPTIONAL_RUN_FILES = new Set(["result.json", "summary.json"]);
14
14
  const RUN_DIRECTORIES = new Set(["worktrees"]);
15
15
  const RUN_FILES = new Set([...REQUIRED_RUN_FILES, ...OPTIONAL_RUN_FILES]);
16
16
  const AGENT_STATES = new Set(["queued", "running", "waiting_for_child", "paused", "retrying", "completed", "failed", "cancelled"]);
@@ -65,7 +65,8 @@ function validateScheduledOptions(value, label) { if (!object(value) || typeof v
65
65
  } }
66
66
  function validateAgent(value, label) { if (!object(value) || typeof value.id !== "string" || !value.id || typeof value.name !== "string" || !value.name || typeof value.path !== "string" || !value.path || typeof value.state !== "string" || !AGENT_STATES.has(value.state))
67
67
  throw new Error(`${label} is invalid`); optionalString(value.label, `${label}.label`); optionalString(value.parentId, `${label}.parentId`); if (value.structuralPath !== undefined)
68
- stringList(value.structuralPath, `${label}.structuralPath`); optionalString(value.parentBreadcrumb, `${label}.parentBreadcrumb`); optionalString(value.worktreeOwner, `${label}.worktreeOwner`); optionalString(value.role, `${label}.role`); optionalString(value.requestedModel, `${label}.requestedModel`); model(value.model, `${label}.model`); stringList(value.tools, `${label}.tools`); nonNegativeInteger(value.attempts, `${label}.attempts`); if (value.attemptDetails !== undefined) {
68
+ stringList(value.structuralPath, `${label}.structuralPath`); optionalString(value.parentBreadcrumb, `${label}.parentBreadcrumb`); optionalString(value.worktreeOwner, `${label}.worktreeOwner`); optionalString(value.role, `${label}.role`); optionalString(value.requestedModel, `${label}.requestedModel`); model(value.model, `${label}.model`); stringList(value.tools, `${label}.tools`); nonNegativeInteger(value.attempts, `${label}.attempts`); if (value.lastEventAt !== undefined)
69
+ finiteNumber(value.lastEventAt, `${label}.lastEventAt`); if (value.attemptDetails !== undefined) {
69
70
  if (!Array.isArray(value.attemptDetails))
70
71
  throw new Error(`${label}.attemptDetails is invalid`);
71
72
  for (const [index, attempt] of value.attemptDetails.entries()) {
@@ -135,6 +136,7 @@ function validateRunRecord(run) {
135
136
  if (!object(session) || typeof session.sessionId !== "string" || !session.sessionId || typeof session.sessionFile !== "string" || !session.sessionFile)
136
137
  throw new Error(`nativeSessions[${String(index)}] is invalid`);
137
138
  optionalString(value.parentRunId, "Persisted parent run");
139
+ optionalString(value.failedAt, "Persisted failed path");
138
140
  if (value.retry !== undefined) {
139
141
  if (!object(value.retry) || typeof value.retry.sourceRunId !== "string" || !value.retry.sourceRunId || typeof value.retry.lineageRootRunId !== "string" || !value.retry.lineageRootRunId)
140
142
  throw new Error("Persisted retry provenance is invalid");
@@ -1,7 +1,7 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { dirname, join } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
- import { validateWorkflowLaunch, WorkflowError, WORKFLOW_TOOL_DESCRIPTION, WORKFLOW_TOOL_LABEL, WORKFLOW_TOOL_PARAMETERS, WORKFLOW_TOOL_PROMPT_SNIPPET } from "./index.js";
4
+ import { validateWorkflowLaunch, WorkflowError, WORKFLOW_RETRY_PARAMETERS, WORKFLOW_TOOL_DESCRIPTION, WORKFLOW_TOOL_LABEL, WORKFLOW_TOOL_PARAMETERS, WORKFLOW_TOOL_PROMPT_SNIPPET } from "./index.js";
5
5
  export const CAPTURE_IDENTITY = "pi-extensible-workflows-eval-capture-v1";
6
6
  export const CAPTURE_ERROR_PREFIX = `${CAPTURE_IDENTITY}:`;
7
7
  export function resolveWorkflowSkillPath() {
@@ -45,4 +45,19 @@ export default function evalCaptureExtension(pi) {
45
45
  }
46
46
  },
47
47
  });
48
+ pi.registerTool({
49
+ name: "workflow_retry",
50
+ label: "Workflow Retry",
51
+ description: "Capture a recovery selection without executing it",
52
+ parameters: WORKFLOW_RETRY_PARAMETERS,
53
+ async execute(_id, params) {
54
+ if (!params.runId.trim())
55
+ throw new WorkflowError("RESUME_INCOMPATIBLE", `${CAPTURE_ERROR_PREFIX}RESUME_INCOMPATIBLE: workflow_retry requires an explicit run ID`);
56
+ const selection = { tool: "workflow_retry", arguments: { runId: params.runId } };
57
+ return {
58
+ content: [{ type: "text", text: JSON.stringify({ captured: true, ...selection }) }],
59
+ details: { captured: true, captureIdentity: CAPTURE_IDENTITY, realWorkflowAgentsLaunched: 0, selection },
60
+ };
61
+ },
62
+ });
48
63
  }
@@ -35,7 +35,7 @@ export declare function formatWorkflowPreview(args: {
35
35
  }): string;
36
36
  export declare const WORKFLOW_TOOL_LABEL = "Workflow";
37
37
  export declare const WORKFLOW_TOOL_DESCRIPTION = "Run a deterministic JavaScript workflow with a named inline parallel-to-summary path by default";
38
- export declare const WORKFLOW_TOOL_PROMPT_SNIPPET = "Run a deterministic, resumable JavaScript workflow. Prefer a named inline script that fans out independent work with parallel(...), awaits the keyed results before interpolating them into one summarizing agent(...), and returns. Inline launches require an explicit non-empty name; registered function launches reject name and use workflow as the run name. Advanced controls include registered functions, outputSchema, budgets, checkpoints, worktrees, retry/resume, CLI export, and pipelines. Use workflow_retry with an explicit failed run ID; parentRunId only reuses named worktrees. Runs are in the background by default; completion arrives as a follow-up message. Set foreground: true when the caller must wait for the final value. Foreground results include the completed run ID. Recovery map: agent(..., { retries }) reruns one agent call in the same run for transient failures; workflow_retry({ runId }) replays a failed run into a child; workflow_resume({ runId, budget? }) continues a budget_exhausted run; parentRunId on a new launch only borrows named worktrees and never replays or resumes.";
38
+ export declare const WORKFLOW_TOOL_PROMPT_SNIPPET = "Run a deterministic, resumable JavaScript workflow. Prefer a named inline script that fans out independent work with parallel(...), awaits the keyed results before interpolating them into one summarizing agent(...), and returns. Inline launches require an explicit non-empty name; registered function launches reject name and use workflow as the run name. Advanced controls include registered functions, outputSchema, budgets, checkpoints, worktrees, retry/resume, CLI export, and pipelines. Use workflow_retry with an explicit failed run ID; parentRunId only reuses named worktrees. Runs are in the background by default; completion arrives as a follow-up message. Set foreground: true when the caller must wait for the final value. If a foreground call detaches before its result is accepted, its terminal success or failure is promoted to one follow-up message. Foreground results include the completed run ID. Recovery inherits the source launch mode; legacy snapshots without launchMode recover in the background. Set foreground: true or false on workflow_resume/workflow_retry to override it; foreground recovery waits for terminal value and run details, while background recovery returns immediately with a follow-up. Recovery map: agent(..., { retries }) reruns one agent call in the same run for transient failures; workflow_retry({ runId, foreground? }) replays a failed run into a child; workflow_resume({ runId, budget?, foreground? }) continues a budget_exhausted run; parentRunId on a new launch only borrows named worktrees and never replays or resumes.";
39
39
  export declare const WORKFLOW_TOOL_PARAMETERS: Type.TObject<{
40
40
  name: Type.TOptional<Type.TString>;
41
41
  description: Type.TOptional<Type.TString>;
@@ -49,6 +49,7 @@ export declare const WORKFLOW_TOOL_PARAMETERS: Type.TObject<{
49
49
  }>;
50
50
  export declare const WORKFLOW_RETRY_PARAMETERS: Type.TObject<{
51
51
  runId: Type.TString;
52
+ foreground: Type.TOptional<Type.TBoolean>;
52
53
  }>;
53
54
  export type WorkflowPhaseState = "not started" | "running" | "completed" | "failed" | "cancelled" | "interrupted" | "budget_exhausted";
54
55
  export interface WorkflowPhaseAgentCounts {
@@ -125,17 +126,19 @@ export declare function navigateWorkflowPhaseTree(tree: WorkflowPhaseTree, selec
125
126
  expandedNodeIds: ReadonlySet<string>;
126
127
  };
127
128
  export declare function preserveWorkflowPhaseSelection(model: WorkflowPhaseModel, selection: WorkflowPhaseSelection): WorkflowPhaseSelection;
128
- export declare function formatWorkflowProgress(run: PersistedRun, spinner?: string, styles?: WorkflowProgressStyles): string;
129
+ export declare function formatWorkflowProgress(run: PersistedRun, spinner?: string, styles?: WorkflowProgressStyles, now?: number): string;
129
130
  export declare function truncateWorkflowProgress(text: string, width: number): string[];
130
131
  export declare function formatBudgetStatus(run: Pick<PersistedRun, "budget" | "budgetVersion" | "usage" | "budgetEvents">): string[];
131
132
  export declare function agentBreadcrumbParts(agent: AgentRecord, byId: Map<string, AgentRecord>, includeStructuralPath?: boolean): string[];
132
133
  export declare function agentBreadcrumb(agent: AgentRecord, byId: Map<string, AgentRecord>, includeStructuralPath?: boolean): string;
133
- export declare function formatNavigatorDashboard(run: PersistedRun, checkpoints: readonly AwaitingCheckpoint[], worktrees: readonly WorktreeReference[]): string;
134
+ export declare function formatStalledDuration(durationMs: number): string;
135
+ export declare function formatNavigatorDashboard(run: PersistedRun, checkpoints: readonly AwaitingCheckpoint[], worktrees: readonly WorktreeReference[], now?: number): string;
134
136
  export declare function formatNavigatorRun(loaded: {
135
137
  run: PersistedRun;
136
138
  snapshot: Readonly<LaunchSnapshot>;
137
- }, checkpoints: readonly AwaitingCheckpoint[], worktrees: readonly WorktreeReference[]): string;
138
- export declare function formatWorkflowPhaseDashboard(run: PersistedRun, snapshot: Readonly<LaunchSnapshot>, width: number, selection?: WorkflowPhaseSelection, styles?: WorkflowProgressStyles): string[];
139
+ }, checkpoints: readonly AwaitingCheckpoint[], worktrees: readonly WorktreeReference[], now?: number): string;
140
+ export declare function formatWorkflowPhaseDashboard(run: PersistedRun, snapshot: Readonly<LaunchSnapshot>, width: number, selection?: WorkflowPhaseSelection, styles?: WorkflowProgressStyles, now?: number): string[];
139
141
  export declare function formatWorkflowFailureDiagnostics(diagnostic: WorkflowFailureDiagnostics): string;
140
- export default function workflowExtension(pi: ExtensionAPI, home?: string, clipboard?: typeof copyToClipboard, createSession?: SessionFactory, agentDir?: string): void;
142
+ export declare function formatWorkflowFailureDelivery(diagnostic: WorkflowFailureDiagnostics): string;
143
+ export default function workflowExtension(pi: ExtensionAPI, home?: string, clipboard?: typeof copyToClipboard, createSession?: SessionFactory, agentDir?: string, additionalSkillPaths?: readonly string[]): void;
141
144
  export {};