pi-extensible-workflows 3.3.0 → 3.4.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/dist/src/cli.js CHANGED
@@ -386,6 +386,8 @@ class CliProgress {
386
386
  #frame = 0;
387
387
  #run;
388
388
  #runId;
389
+ #runtimeStartedAt = 0;
390
+ #runtimeBaseMs = 0;
389
391
  #timer;
390
392
  #interactive;
391
393
  #styles;
@@ -399,6 +401,12 @@ class CliProgress {
399
401
  if (this.#runId !== run.id) {
400
402
  this.#runId = run.id;
401
403
  this.onRunId(run.id);
404
+ this.#runtimeStartedAt = Date.now();
405
+ this.#runtimeBaseMs = run.usage?.durationMs ?? 0;
406
+ }
407
+ else if (this.#run && this.#run.state !== "running" && run.state === "running") {
408
+ this.#runtimeStartedAt = Date.now();
409
+ this.#runtimeBaseMs = run.usage?.durationMs ?? 0;
402
410
  }
403
411
  this.#run = run;
404
412
  if (!this.#interactive) {
@@ -414,8 +422,9 @@ class CliProgress {
414
422
  render() {
415
423
  if (!this.#run)
416
424
  return;
425
+ const run = this.#run.state !== "running" ? this.#run : { ...this.#run, usage: { ...(this.#run.usage ?? { tokens: 0, costUsd: 0, durationMs: 0, agentLaunches: 0 }), durationMs: Math.max(this.#run.usage?.durationMs ?? 0, this.#runtimeBaseMs + Date.now() - this.#runtimeStartedAt) } };
417
426
  if (!this.#interactive) {
418
- const stable = formatWorkflowProgress(this.#run, "◇", this.#styles);
427
+ const stable = formatWorkflowProgress(run, "◇", this.#styles);
419
428
  if (stable !== this.#lastStable) {
420
429
  this.#lastStable = stable;
421
430
  this.stderr(`${stable}\n`);
@@ -424,7 +433,7 @@ class CliProgress {
424
433
  }
425
434
  const spinner = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"][this.#frame++ % 10] ?? "◇";
426
435
  const width = process.stderr.columns || 80;
427
- const text = truncateWorkflowProgress(formatWorkflowProgress(this.#run, spinner, this.#styles), width).join("\n");
436
+ const text = truncateWorkflowProgress(formatWorkflowProgress(run, spinner, this.#styles), width).join("\n");
428
437
  this.stderr(`${this.#lines ? `\x1b[${String(this.#lines)}A` : ""}${this.#lines ? "" : "\x1b[?25l"}\x1b[0J${text}\n`);
429
438
  this.#lines = text.split("\n").length;
430
439
  }
@@ -63,36 +63,70 @@ function validateScheduledOptions(value, label) { if (!object(value) || typeof v
63
63
  optionalString(value.agentIdentity.parentBreadcrumb, `${label}.agentIdentity.parentBreadcrumb`);
64
64
  optionalString(value.agentIdentity.worktreeOwner, `${label}.agentIdentity.worktreeOwner`);
65
65
  } }
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
- 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.lastEventAt !== undefined)
69
- finiteNumber(value.lastEventAt, `${label}.lastEventAt`); if (value.attemptDetails !== undefined) {
70
- if (!Array.isArray(value.attemptDetails))
71
- throw new Error(`${label}.attemptDetails is invalid`);
72
- for (const [index, attempt] of value.attemptDetails.entries()) {
73
- const at = `${label}.attemptDetails[${String(index)}]`;
74
- if (!object(attempt) || !Number.isSafeInteger(attempt.attempt) || Number(attempt.attempt) < 1 || typeof attempt.sessionId !== "string" || !attempt.sessionId || typeof attempt.sessionFile !== "string" || !attempt.sessionFile)
75
- throw new Error(`${at} is invalid`);
76
- accounting(attempt.accounting, `${at}.accounting`);
77
- if (attempt.error !== undefined && (!object(attempt.error) || typeof attempt.error.code !== "string" || typeof attempt.error.message !== "string"))
78
- throw new Error(`${at}.error is invalid`);
79
- if (attempt.setup !== undefined) {
80
- if (!object(attempt.setup) || !Array.isArray(attempt.setup.hookNames) || attempt.setup.hookNames.some((name) => typeof name !== "string") || typeof attempt.setup.cwd !== "string")
81
- throw new Error(`${at}.setup is invalid`);
82
- model(attempt.setup.model, `${at}.setup.model`);
83
- stringList(attempt.setup.tools, `${at}.setup.tools`);
84
- resourceExclusions(attempt.setup.disabledAgentResources, `${at}.setup.disabledAgentResources`);
66
+ function validateSessionReference(value, label) {
67
+ if (!object(value) || typeof value.transport !== "string" || !value.transport || typeof value.sessionId !== "string" || !value.sessionId || (value.locator !== undefined && !jsonValue(value.locator)))
68
+ throw new Error(`${label} is invalid`);
69
+ }
70
+ function validateAgentSetup(value, label) {
71
+ if (!object(value))
72
+ throw new Error(`${label} is invalid`);
73
+ stringList(value.hookNames, `${label}.hookNames`);
74
+ model(value.model, `${label}.model`);
75
+ stringList(value.tools, `${label}.tools`);
76
+ if (typeof value.cwd !== "string" || !value.cwd)
77
+ throw new Error(`${label}.cwd is invalid`);
78
+ resourceExclusions(value.disabledAgentResources, `${label}.disabledAgentResources`);
79
+ }
80
+ function validateAgent(value, label) {
81
+ 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))
82
+ throw new Error(`${label} is invalid`);
83
+ optionalString(value.systemPrompt, `${label}.systemPrompt`);
84
+ optionalString(value.prompt, `${label}.prompt`);
85
+ optionalString(value.label, `${label}.label`);
86
+ optionalString(value.parentId, `${label}.parentId`);
87
+ if (value.structuralPath !== undefined)
88
+ stringList(value.structuralPath, `${label}.structuralPath`);
89
+ optionalString(value.parentBreadcrumb, `${label}.parentBreadcrumb`);
90
+ optionalString(value.worktreeOwner, `${label}.worktreeOwner`);
91
+ optionalString(value.role, `${label}.role`);
92
+ optionalString(value.requestedModel, `${label}.requestedModel`);
93
+ model(value.model, `${label}.model`);
94
+ stringList(value.tools, `${label}.tools`);
95
+ nonNegativeInteger(value.attempts, `${label}.attempts`);
96
+ if (value.attemptDetails !== undefined) {
97
+ if (!Array.isArray(value.attemptDetails))
98
+ throw new Error(`${label}.attemptDetails is invalid`);
99
+ for (const [index, attempt] of value.attemptDetails.entries()) {
100
+ const at = `${label}.attemptDetails[${String(index)}]`;
101
+ if (!object(attempt) || !Number.isSafeInteger(attempt.attempt) || Number(attempt.attempt) < 1 || typeof attempt.transport !== "string" || !attempt.transport || Object.hasOwn(attempt, "sessionId") || Object.hasOwn(attempt, "sessionFile"))
102
+ throw new Error(`${at} is invalid`);
103
+ validateAgentSetup(attempt.setup, `${at}.setup`);
104
+ if (attempt.session !== undefined) {
105
+ validateSessionReference(attempt.session, `${at}.session`);
106
+ if (attempt.session.transport !== attempt.transport)
107
+ throw new Error(`${at}.session transport does not match attempt transport`);
108
+ }
109
+ accounting(attempt.accounting, `${at}.accounting`);
110
+ if (attempt.error !== undefined && (!object(attempt.error) || typeof attempt.error.code !== "string" || typeof attempt.error.message !== "string"))
111
+ throw new Error(`${at}.error is invalid`);
85
112
  }
86
113
  }
87
- } if (value.accounting !== undefined)
88
- accounting(value.accounting, `${label}.accounting`); if (value.toolCalls !== undefined) {
89
- if (!Array.isArray(value.toolCalls))
90
- throw new Error(`${label}.toolCalls is invalid`);
91
- for (const call of value.toolCalls)
92
- if (!object(call) || typeof call.id !== "string" || typeof call.name !== "string" || !["running", "completed", "failed"].includes(call.state))
114
+ if (value.accounting !== undefined)
115
+ accounting(value.accounting, `${label}.accounting`);
116
+ if (value.toolCalls !== undefined) {
117
+ if (!Array.isArray(value.toolCalls))
93
118
  throw new Error(`${label}.toolCalls is invalid`);
94
- } if (value.activity !== undefined && (!object(value.activity) || !["reasoning", "tool", "text"].includes(value.activity.kind) || typeof value.activity.text !== "string"))
95
- throw new Error(`${label}.activity is invalid`); }
119
+ for (const [index, call] of value.toolCalls.entries())
120
+ if (!object(call) || typeof call.id !== "string" || !call.id || typeof call.name !== "string" || !call.name || !["running", "completed", "failed"].includes(call.state))
121
+ throw new Error(`${label}.toolCalls[${String(index)}] is invalid`);
122
+ }
123
+ if (value.activity !== undefined) {
124
+ if (!object(value.activity) || !["reasoning", "tool", "text"].includes(value.activity.kind) || typeof value.activity.text !== "string")
125
+ throw new Error(`${label}.activity is invalid`);
126
+ }
127
+ if (value.lastEventAt !== undefined)
128
+ finiteNumber(value.lastEventAt, `${label}.lastEventAt`);
129
+ }
96
130
  function validateUsage(value, label) { if (!object(value))
97
131
  throw new Error(`${label} is invalid`); for (const key of ["tokens", "costUsd", "durationMs", "agentLaunches"])
98
132
  finiteNumber(value[key], `${label}.${key}`); }
@@ -107,7 +141,7 @@ function validateBudgetEvents(value) { if (value === undefined)
107
141
  } }
108
142
  function validateRunRecord(run) {
109
143
  const value = run;
110
- if (!object(value) || typeof value.id !== "string" || !value.id || typeof value.workflowName !== "string" || !value.workflowName || typeof value.cwd !== "string" || !value.cwd || typeof value.sessionId !== "string" || !value.sessionId || !RUN_STATES.includes(value.state) || !Array.isArray(value.agents) || !Array.isArray(value.nativeSessions))
144
+ if (!object(value) || typeof value.id !== "string" || !value.id || typeof value.workflowName !== "string" || !value.workflowName || typeof value.cwd !== "string" || !value.cwd || typeof value.sessionId !== "string" || !value.sessionId || !RUN_STATES.includes(value.state) || !Array.isArray(value.agents) || !Array.isArray(value.agentSessions) || Object.hasOwn(value, "nativeSessions"))
111
145
  throw new Error("Persisted run state is invalid");
112
146
  const agents = value.agents;
113
147
  agents.forEach((agent, index) => { validateAgent(agent, `agents[${String(index)}]`); });
@@ -132,9 +166,9 @@ function validateRunRecord(run) {
132
166
  parent = typeof parentAgent?.parentId === "string" ? parentAgent.parentId : undefined;
133
167
  }
134
168
  }
135
- for (const [index, session] of value.nativeSessions.entries())
136
- if (!object(session) || typeof session.sessionId !== "string" || !session.sessionId || typeof session.sessionFile !== "string" || !session.sessionFile)
137
- throw new Error(`nativeSessions[${String(index)}] is invalid`);
169
+ const sessions = value.agentSessions;
170
+ for (const [index, session] of sessions.entries())
171
+ validateSessionReference(session, `agentSessions[${String(index)}]`);
138
172
  optionalString(value.parentRunId, "Persisted parent run");
139
173
  optionalString(value.failedAt, "Persisted failed path");
140
174
  if (value.retry !== undefined) {
@@ -267,6 +301,7 @@ async function validateRunArtifacts(store, workflowScript, state) {
267
301
  throw new Error(`${label} is invalid`);
268
302
  ownershipIds.add(record.id);
269
303
  optionalString(record.parentId, `${label}.parentId`);
304
+ optionalString(record.prompt, `${label}.prompt`);
270
305
  validateScheduledOptions(record.options, `${label}.options`);
271
306
  }
272
307
  for (const record of ownership)
@@ -12,6 +12,6 @@ export declare function agentWorktree(identity: AgentIdentity): {
12
12
  };
13
13
  export declare function executeShellCommand(command: string, options: ShellOptions, signal: AbortSignal, cwd?: string): Promise<ShellResult>;
14
14
  export declare function runWorkflow(script: string, args?: JsonValue, bridge?: WorkflowBridge, signal?: AbortSignal): WorkflowExecution;
15
- export declare function persistActiveAgentAttempt(store: RunStore, id: string, active: Pick<AgentAttempt, "attempt" | "sessionId" | "sessionFile" | "setup">): Promise<void>;
15
+ export declare function persistActiveAgentAttempt(store: RunStore, id: string, active: AgentAttempt): Promise<void>;
16
16
  export declare function persistAgentAttempts(store: RunStore, id: string, attempts: readonly AgentAttempt[]): Promise<void>;
17
17
  export type { AgentIdentity, ShellIdentity, WorkflowBridge, WorkflowExecution } from "./types.js";
@@ -9,6 +9,7 @@ import { asWorkflowError, errorText, fail, isWorkflowAuthored, jsonValue, markWo
9
9
  import { instrumentWorkflow, validateAgentOptions, validateShellCommand, validateShellOptions } from "./validation.js";
10
10
  export const RPC_LIMIT_BYTES = 10 * 1024 * 1024;
11
11
  export const HEARTBEAT_TIMEOUT_MS = 5000;
12
+ const HEARTBEAT_INTERVAL_MS = 1000;
12
13
  const OUTCOME_ERRORS = new Set(["AGENT_TIMEOUT", "AGENT_FAILED", "RESULT_INVALID"]);
13
14
  const WORK_RESULT_BRAND = "__workResult";
14
15
  const childSource = String.raw `
@@ -58,7 +59,7 @@ process.on("message", raw => {
58
59
  if (message.ok) request.resolve(message.value);
59
60
  else request.reject(workflowError(message.error));
60
61
  });
61
- const heartbeat = setInterval(() => send({ type: "heartbeat" }), 1000);
62
+ const heartbeat = setInterval(() => send({ type: "heartbeat" }), ${HEARTBEAT_INTERVAL_MS});
62
63
  send({ type: "heartbeat" });
63
64
  const BRAND = "${WORK_RESULT_BRAND}";
64
65
  const workError = (code, message) => Object.assign(new Error(message), { code });
@@ -458,7 +459,20 @@ export function runWorkflow(script, args = null, bridge = {}, signal) {
458
459
  const controller = new AbortController();
459
460
  let settled = false;
460
461
  let rejectResult = () => undefined;
461
- let watchdog = setTimeout(() => { stop("WORKER_UNRESPONSIVE", "Workflow worker missed its five-second heartbeat"); }, HEARTBEAT_TIMEOUT_MS);
462
+ let lastHeartbeatAt = performance.now();
463
+ let lastWatchdogCheckAt = lastHeartbeatAt;
464
+ const watchdog = setInterval(() => {
465
+ const now = performance.now();
466
+ // A check delayed enough to bridge the normal heartbeat margin makes elapsed time unreliable.
467
+ const watchdogCheckWasDelayed = now - lastWatchdogCheckAt >= HEARTBEAT_TIMEOUT_MS - HEARTBEAT_INTERVAL_MS;
468
+ lastWatchdogCheckAt = now;
469
+ if (watchdogCheckWasDelayed) {
470
+ lastHeartbeatAt = now;
471
+ return;
472
+ }
473
+ if (now - lastHeartbeatAt >= HEARTBEAT_TIMEOUT_MS)
474
+ stop("WORKER_UNRESPONSIVE", "Workflow worker missed its five-second heartbeat");
475
+ }, HEARTBEAT_INTERVAL_MS);
462
476
  const result = new Promise((resolve, reject) => {
463
477
  rejectResult = reject;
464
478
  child.on("message", (raw) => {
@@ -469,8 +483,7 @@ export function runWorkflow(script, args = null, bridge = {}, signal) {
469
483
  if (!jsonValue(message))
470
484
  fail("RPC_LIMIT_EXCEEDED", "Worker RPC must contain JSON-compatible values");
471
485
  if (message.type === "heartbeat") {
472
- clearTimeout(watchdog);
473
- watchdog = setTimeout(() => { stop("WORKER_UNRESPONSIVE", "Workflow worker missed its five-second heartbeat"); }, HEARTBEAT_TIMEOUT_MS);
486
+ lastHeartbeatAt = performance.now();
474
487
  return;
475
488
  }
476
489
  if (message.type === "result") {
@@ -502,7 +515,7 @@ export function runWorkflow(script, args = null, bridge = {}, signal) {
502
515
  child.kill("SIGKILL"); }, 1000).unref();
503
516
  }
504
517
  }
505
- function finish() { settled = true; clearTimeout(watchdog); signal?.removeEventListener("abort", cancel); killChild(); rmSync(childDir, { recursive: true, force: true }); }
518
+ function finish() { settled = true; clearInterval(watchdog); signal?.removeEventListener("abort", cancel); killChild(); rmSync(childDir, { recursive: true, force: true }); }
506
519
  function stop(code, message) { if (settled)
507
520
  return; controller.abort(); finish(); rejectResult(new WorkflowError(code, message)); }
508
521
  function branded(result) { return { ...result, [WORK_RESULT_BRAND]: true }; }
@@ -612,8 +625,8 @@ export function runWorkflow(script, args = null, bridge = {}, signal) {
612
625
  signal?.addEventListener("abort", cancel, { once: true });
613
626
  return { result, cancel };
614
627
  }
615
- function nativeSessionReference(attempt) {
616
- return { sessionId: attempt.sessionId, sessionFile: attempt.sessionFile };
628
+ function attemptSummary(attempt, accounting = attempt.accounting) {
629
+ return { attempt: attempt.attempt, transport: attempt.transport, ...(attempt.session ? { session: attempt.session } : {}), ...(attempt.error ? { error: attempt.error } : {}), accounting, setup: attempt.setup };
617
630
  }
618
631
  export async function persistActiveAgentAttempt(store, id, active) {
619
632
  await store.updateState((run) => {
@@ -621,9 +634,11 @@ export async function persistActiveAgentAttempt(store, id, active) {
621
634
  if (!agent)
622
635
  throw new WorkflowError("INTERNAL_ERROR", `Missing production ownership record: ${id}`);
623
636
  const accounting = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0 };
624
- const details = [...(agent.attemptDetails ?? []).filter((candidate) => candidate.attempt !== active.attempt), { ...active, accounting }];
625
- const nativeSessions = run.nativeSessions.some(({ sessionId }) => sessionId === active.sessionId) ? run.nativeSessions : [...run.nativeSessions, nativeSessionReference(active)];
626
- return { ...run, agents: run.agents.map((candidate) => candidate.id === id ? { ...candidate, attempts: Math.max(candidate.attempts, active.attempt), attemptDetails: details } : candidate), nativeSessions };
637
+ const detail = attemptSummary(active, accounting);
638
+ const details = [...(agent.attemptDetails ?? []).filter((candidate) => candidate.attempt !== active.attempt), detail];
639
+ const session = active.session;
640
+ const agentSessions = session && !run.agentSessions.some(({ transport, sessionId }) => transport === session.transport && sessionId === session.sessionId) ? [...run.agentSessions, session] : run.agentSessions;
641
+ return { ...run, agents: run.agents.map((candidate) => candidate.id === id ? { ...candidate, attempts: Math.max(candidate.attempts, active.attempt), attemptDetails: details } : candidate), agentSessions };
627
642
  });
628
643
  }
629
644
  export async function persistAgentAttempts(store, id, attempts) {
@@ -632,8 +647,9 @@ export async function persistAgentAttempts(store, id, attempts) {
632
647
  if (!agent)
633
648
  throw new WorkflowError("INTERNAL_ERROR", `Missing production ownership record: ${id}`);
634
649
  const total = attempts.reduce((sum, attempt) => ({ input: sum.input + attempt.accounting.input, output: sum.output + attempt.accounting.output, cacheRead: sum.cacheRead + attempt.accounting.cacheRead, cacheWrite: sum.cacheWrite + attempt.accounting.cacheWrite, cost: sum.cost + attempt.accounting.cost }), { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0 });
635
- const attemptDetails = attempts.map(({ attempt, sessionId, sessionFile, error, accounting, setup }) => ({ attempt, sessionId, sessionFile, ...(error ? { error } : {}), accounting, ...(setup ? { setup } : {}) }));
636
- const sessionIds = new Set(attempts.map(({ sessionId }) => sessionId));
637
- return { ...run, agents: run.agents.map((candidate) => candidate.id === id ? { ...candidate, attempts: attempts.length, attemptDetails, accounting: total } : candidate), nativeSessions: [...run.nativeSessions.filter(({ sessionId }) => !sessionIds.has(sessionId)), ...attempts.map((attempt) => nativeSessionReference(attempt))] };
650
+ const attemptDetails = attempts.map((attempt) => attemptSummary(attempt));
651
+ const sessions = attempts.map((attempt) => attempt.session).filter((session) => session !== undefined);
652
+ const sessionKeys = new Set(sessions.map(({ transport, sessionId }) => `${transport}:${sessionId}`));
653
+ return { ...run, agents: run.agents.map((candidate) => candidate.id === id ? { ...candidate, attempts: attempts.length, attemptDetails, accounting: total } : candidate), agentSessions: [...run.agentSessions.filter(({ transport, sessionId }) => !sessionKeys.has(`${transport}:${sessionId}`)), ...sessions] };
638
654
  });
639
655
  }
@@ -1,8 +1,7 @@
1
1
  import { Type } from "@earendil-works/pi-ai";
2
2
  import { copyToClipboard, type ExtensionAPI } from "@earendil-works/pi-coding-agent";
3
- import { type SessionFactory } from "./agent-execution.js";
4
3
  import type { AwaitingCheckpoint, PersistedRun, WorktreeReference } from "./persistence.js";
5
- import { type AgentRecord, type LaunchSnapshot, type RunState, type WorkflowFailureDiagnostics } from "./types.js";
4
+ import { type AgentRecord, type AgentTransport, type LaunchSnapshot, type RunState, type WorkflowFailureDiagnostics } from "./types.js";
6
5
  export interface WorkflowProgressStyles {
7
6
  accent(text: string): string;
8
7
  success(text: string): string;
@@ -34,12 +33,13 @@ export declare function formatWorkflowPreview(args: {
34
33
  description?: unknown;
35
34
  }): string;
36
35
  export declare const WORKFLOW_TOOL_LABEL = "Workflow";
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. 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.";
36
+ export declare const WORKFLOW_TOOL_DESCRIPTION = "Run a deterministic JavaScript workflow with a named inline or file-backed parallel-to-summary path by default";
37
+ 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 and file-backed launches require a non-empty name; registered function launches may use name as an optional run label and otherwise 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
38
  export declare const WORKFLOW_TOOL_PARAMETERS: Type.TObject<{
40
39
  name: Type.TOptional<Type.TString>;
41
40
  description: Type.TOptional<Type.TString>;
42
41
  script: Type.TOptional<Type.TString>;
42
+ scriptPath: Type.TOptional<Type.TString>;
43
43
  workflow: Type.TOptional<Type.TString>;
44
44
  args: Type.TOptional<Type.TUnknown>;
45
45
  foreground: Type.TOptional<Type.TBoolean>;
@@ -140,5 +140,5 @@ export declare function formatNavigatorRun(loaded: {
140
140
  export declare function formatWorkflowPhaseDashboard(run: PersistedRun, snapshot: Readonly<LaunchSnapshot>, width: number, selection?: WorkflowPhaseSelection, styles?: WorkflowProgressStyles, now?: number): string[];
141
141
  export declare function formatWorkflowFailureDiagnostics(diagnostic: WorkflowFailureDiagnostics): string;
142
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;
143
+ export default function workflowExtension(pi: ExtensionAPI, home?: string, clipboard?: typeof copyToClipboard, transport?: AgentTransport, agentDir?: string, additionalSkillPaths?: readonly string[]): void;
144
144
  export {};