@marcoscale98/pi-extensible-workflows 5.19.1-fork.1 → 6.2.1-fork.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/CHANGELOG.md +77 -0
- package/README.md +2 -2
- package/dist/src/agent-execution.d.ts +43 -10
- package/dist/src/agent-execution.js +365 -127
- package/dist/src/background-widget.d.ts +0 -1
- package/dist/src/background-widget.js +4 -9
- package/dist/src/decoders.d.ts +2 -3
- package/dist/src/decoders.js +33 -37
- package/dist/src/execution.d.ts +2 -0
- package/dist/src/execution.js +5 -5
- package/dist/src/host-delivery.js +4 -8
- package/dist/src/host-navigator.js +5 -2
- package/dist/src/host-phases.d.ts +1 -0
- package/dist/src/host-recovery.d.ts +6 -6
- package/dist/src/host-recovery.js +14 -22
- package/dist/src/host-runtime.js +56 -5
- package/dist/src/host-view.d.ts +1 -1
- package/dist/src/host-view.js +17 -15
- package/dist/src/host.js +62 -87
- package/dist/src/index.d.ts +1 -2
- package/dist/src/index.js +1523 -1630
- package/dist/src/index.js.map +3 -3
- package/dist/src/paths.d.ts +7 -6
- package/dist/src/paths.js +24 -15
- package/dist/src/persistence.d.ts +1 -0
- package/dist/src/persistence.js +1 -0
- package/dist/src/pi-runtime-runner.js +4 -1
- package/dist/src/registry.d.ts +9 -7
- package/dist/src/registry.js +28 -70
- package/dist/src/runtime/agent-runner.d.ts +2 -0
- package/dist/src/session-lease.d.ts +1 -1
- package/dist/src/session-lease.js +45 -11
- package/dist/src/settings.d.ts +6 -3
- package/dist/src/settings.js +36 -47
- package/dist/src/store.d.ts +3 -1
- package/dist/src/store.js +17 -1
- package/dist/src/trajectory.d.ts +0 -1
- package/dist/src/trajectory.js +2 -3
- package/dist/src/types.d.ts +100 -44
- package/dist/src/types.js +4 -7
- package/dist/src/utils.d.ts +3 -2
- package/dist/src/utils.js +20 -10
- package/dist/src/validation.d.ts +8 -2
- package/dist/src/validation.js +122 -86
- package/dist/starter/index.js +8 -9
- package/dist/starter/index.js.map +1 -1
- package/dist/starter/review-loop.js +8 -7
- package/dist/subagents/index.js +654 -814
- package/dist/subagents/index.js.map +3 -3
- package/dist/subagents/src/contracts.d.ts +4 -3
- package/dist/subagents/src/contracts.js +2 -2
- package/dist/subagents/src/index.d.ts +1 -1
- package/dist/subagents/src/index.js +1 -2
- package/dist/subagents/src/manager.js +49 -70
- package/dist/subagents/src/navigator.js +3 -14
- package/dist/subagents/src/view.d.ts +0 -1
- package/dist/subagents/src/view.js +2 -16
- package/dist/subagents/src/worktree.js +1 -1
- package/dist/trajectory/assets/index.html +8 -8
- package/dist/trajectory/index.js +487 -891
- package/dist/trajectory/index.js.map +3 -3
- package/dist/trajectory/src/assets/index.html +8 -8
- package/dist/trajectory/src/index.js +126 -86
- package/dist/trajectory/src/server.js +264 -25
- package/dist/trajectory/src/server.js.map +3 -3
- package/dist/trajectory/src/startup-mutex.d.ts +17 -0
- package/dist/trajectory/src/startup-mutex.js +172 -0
- package/examples/workflow-extension-template/README.md +4 -7
- package/examples/workflow-extension-template/extension.test.mjs +4 -8
- package/examples/workflow-extension-template/index.js +0 -3
- package/package.json +5 -5
- package/skills/pi-extensible-workflows/SKILL.md +10 -8
- package/src/agent-execution.ts +339 -127
- package/src/background-widget.ts +4 -10
- package/src/decoders.ts +31 -37
- package/src/execution.ts +5 -5
- package/src/host-delivery.ts +4 -8
- package/src/host-navigator.ts +4 -2
- package/src/host-phases.ts +1 -1
- package/src/host-recovery.ts +20 -26
- package/src/host-runtime.ts +44 -6
- package/src/host-view.ts +18 -11
- package/src/host.ts +62 -113
- package/src/index.ts +1 -2
- package/src/paths.ts +18 -12
- package/src/persistence.ts +1 -0
- package/src/pi-runtime-runner.ts +3 -1
- package/src/registry.ts +27 -63
- package/src/runtime/agent-runner.ts +2 -0
- package/src/session-lease.ts +34 -6
- package/src/settings.ts +35 -47
- package/src/store.ts +18 -2
- package/src/trajectory.ts +2 -4
- package/src/types.ts +71 -30
- package/src/utils.ts +18 -9
- package/src/validation.ts +102 -79
- package/starter/index.ts +0 -1
- package/starter/review-loop.ts +8 -7
- package/starter/roles/developer.md +0 -1
- package/starter/roles/oracle.md +0 -1
- package/starter/roles/researcher.md +0 -1
- package/starter/roles/reviewer.md +0 -1
- package/starter/roles/scout.md +0 -1
- package/subagents/README.md +8 -14
- package/subagents/src/contracts.ts +3 -4
- package/subagents/src/index.ts +2 -3
- package/subagents/src/manager.ts +45 -125
- package/subagents/src/navigator.ts +4 -13
- package/subagents/src/view.ts +4 -14
- package/subagents/src/worktree.ts +1 -1
- package/trajectory/src/assets/index.html +8 -8
- package/trajectory/src/index.ts +106 -82
- package/trajectory/src/server.ts +44 -8
- package/trajectory/src/startup-mutex.ts +127 -0
- package/dist/src/roles.d.ts +0 -70
- package/dist/src/roles.js +0 -326
- package/dist/starter/roles/developer.md +0 -6
- package/dist/starter/roles/oracle.md +0 -18
- package/dist/starter/roles/researcher.md +0 -20
- package/dist/starter/roles/reviewer.md +0 -19
- package/dist/starter/roles/scout.md +0 -18
- package/examples/workflow-extension-template/roles/reviewer.md +0 -4
- package/src/roles.ts +0 -327
package/dist/src/host-runtime.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
-
import {
|
|
2
|
+
import { FUNCTION_HANDLE_PREFIX } from "./execution.js";
|
|
3
|
+
import { deepFreeze, errorCode, errorText, fail, isWorkflowErrorCode, jsonDigest, jsonValue, object } from "./utils.js";
|
|
3
4
|
import { validateAgentOptions, validateShellOptions, workflowPrompt } from "./validation.js";
|
|
4
5
|
import { HARD_TERMINAL_RUN_STATES, WORKFLOW_AGENT_STATE_CHANGED_EVENT, WORKFLOW_BUDGET_EVENT, WORKFLOW_CHECKPOINT_STATE_CHANGED_EVENT, WORKFLOW_PHASE_CHANGED_EVENT, WORKFLOW_RUN_COMPLETED_EVENT, WORKFLOW_RUN_FAILED_EVENT, WORKFLOW_RUN_RESUMED_EVENT, WORKFLOW_RUN_STARTED_EVENT, WORKFLOW_RUN_STATE_CHANGED_EVENT, WORKFLOW_WORKTREE_CREATED_EVENT, WorkflowError } from "./types.js";
|
|
5
6
|
import { structuralPath as operationPath } from "./persistence.js";
|
|
@@ -130,7 +131,7 @@ export class WorkflowEventPublisher {
|
|
|
130
131
|
await this.#publish(store, metadata, WORKFLOW_RUN_FAILED_EVENT, { error: safeEventError(error) });
|
|
131
132
|
}
|
|
132
133
|
async agentState(store, metadata, previous, agent) {
|
|
133
|
-
await this.#publish(store, metadata, WORKFLOW_AGENT_STATE_CHANGED_EVENT, { agentId: agent.id, displayLabel: agent.label ?? agent.name,
|
|
134
|
+
await this.#publish(store, metadata, WORKFLOW_AGENT_STATE_CHANGED_EVENT, { agentId: agent.id, displayLabel: agent.label ?? agent.name, structuralPath: [...(agent.structuralPath ?? [])], ...(agent.parentId ? { parentId: agent.parentId } : {}), ...(agent.parentBreadcrumb ? { parentBreadcrumb: agent.parentBreadcrumb } : {}), ...(agent.worktreeOwner ? { worktreeOwner: agent.worktreeOwner } : {}), ...(previous ? { previousState: previous.state } : {}), state: agent.state, attempt: agent.attempts });
|
|
134
135
|
}
|
|
135
136
|
async agentStates(store, metadata, previous, current) {
|
|
136
137
|
const previousById = new Map(previous.map((agent) => [agent.id, agent]));
|
|
@@ -285,6 +286,7 @@ export function nextNamedOccurrence(counters, label) {
|
|
|
285
286
|
function functionBreadcrumb(name, occurrence) { return occurrence === 1 ? name : `${name} #${String(occurrence)}`; }
|
|
286
287
|
export function withWorkflowFunctions(bridge, store, runContext, registry, settings = Object.freeze({})) {
|
|
287
288
|
const functionAgentOccurrences = new Map();
|
|
289
|
+
const functionHandles = new Set();
|
|
288
290
|
const functionShellOccurrences = new Map();
|
|
289
291
|
const functionInvokeOccurrences = new Map();
|
|
290
292
|
const functionInvokeBreadcrumbOccurrences = new Map();
|
|
@@ -307,6 +309,7 @@ export function withWorkflowFunctions(bridge, store, runContext, registry, setti
|
|
|
307
309
|
// Own rejection immediately; Promise.all below still propagates it after the function completes.
|
|
308
310
|
void sideEffect.catch(() => undefined);
|
|
309
311
|
};
|
|
312
|
+
let closed = false;
|
|
310
313
|
const parentBreadcrumb = breadcrumb ?? functionBreadcrumb(name, identity.occurrence);
|
|
311
314
|
const context = {
|
|
312
315
|
settings: deepFreeze(structuredClone(settings)),
|
|
@@ -327,7 +330,7 @@ export function withWorkflowFunctions(bridge, store, runContext, registry, setti
|
|
|
327
330
|
const nestedIdentity = { path: nestedPath, structuralPath: [...inherited], occurrence, ...(scopedWorktreeOwner ? { worktreeOwner: scopedWorktreeOwner } : {}) };
|
|
328
331
|
return invokeFunction(targetName, targetInput, signal, nestedIdentity, `${parentBreadcrumb} > ${functionBreadcrumb(breadcrumbName, breadcrumbOccurrence)}`);
|
|
329
332
|
},
|
|
330
|
-
agent: async (prompt, options) => {
|
|
333
|
+
agent: Object.assign(async (prompt, options) => {
|
|
331
334
|
if (!bridge.agent || typeof prompt !== "string")
|
|
332
335
|
fail("AGENT_FAILED", "No agent bridge is available");
|
|
333
336
|
const validatedOptions = validateAgentOptions(options === undefined ? {} : options);
|
|
@@ -337,7 +340,55 @@ export function withWorkflowFunctions(bridge, store, runContext, registry, setti
|
|
|
337
340
|
const occurrence = (functionAgentOccurrences.get(key) ?? 0) + 1;
|
|
338
341
|
functionAgentOccurrences.set(key, occurrence);
|
|
339
342
|
return bridge.agent(prompt, validatedOptions, signal, { structuralPath: [...inherited], callSite: `function:${path}`, occurrence, parentBreadcrumb, ...(scopedWorktreeOwner ? { worktreeOwner: scopedWorktreeOwner } : {}) });
|
|
340
|
-
},
|
|
343
|
+
}, {
|
|
344
|
+
create: (...created) => {
|
|
345
|
+
if (created.length !== 1 || !object(created[0]))
|
|
346
|
+
fail("INVALID_METADATA", "agent.create requires one options object");
|
|
347
|
+
const { name, ...rest } = created[0];
|
|
348
|
+
if (typeof name !== "string" || !name.trim())
|
|
349
|
+
fail("INVALID_METADATA", "agent.create requires a stable explicit name");
|
|
350
|
+
const config = deepFreeze(structuredClone(validateAgentOptions(rest)));
|
|
351
|
+
const scopedStructuralPath = [...(inheritedHostAgentPath.getStore() ?? [])];
|
|
352
|
+
const scopedWorktreeOwner = inheritedHostWorktreeOwner.getStore() ?? worktreeOwner;
|
|
353
|
+
const scope = JSON.stringify([scopedStructuralPath, scopedWorktreeOwner ?? null]);
|
|
354
|
+
// The registered-function path makes repeated and nested invocations distinct; scope separates branches and worktrees. The digest bounds the journal and turn-copy file names.
|
|
355
|
+
const handle = operationPath(FUNCTION_HANDLE_PREFIX, jsonDigest([path, scopedStructuralPath, scopedWorktreeOwner ?? null]), name);
|
|
356
|
+
if (functionHandles.has(handle))
|
|
357
|
+
fail("INVALID_METADATA", `Duplicate agent handle name: ${name}`);
|
|
358
|
+
functionHandles.add(handle);
|
|
359
|
+
let turn = 0;
|
|
360
|
+
let inflight = false;
|
|
361
|
+
// Not a nested promise chain: the caller owns the one returned promise, and inflight clears in finally on every outcome.
|
|
362
|
+
const send = async (...sent) => {
|
|
363
|
+
if (!bridge.agent)
|
|
364
|
+
fail("AGENT_FAILED", "No agent bridge is available");
|
|
365
|
+
if (sent.length < 1 || sent.length > 2)
|
|
366
|
+
fail("INVALID_METADATA", "send requires a prompt and optional options");
|
|
367
|
+
if (closed)
|
|
368
|
+
fail("INVALID_METADATA", `Agent handle outlived the function invocation that created it: ${name}`);
|
|
369
|
+
if (typeof sent[0] !== "string")
|
|
370
|
+
fail("INVALID_METADATA", "send prompt must be a string");
|
|
371
|
+
const sendOptions = sent[1] === undefined ? {} : sent[1];
|
|
372
|
+
if (!object(sendOptions) || Object.keys(sendOptions).some((key) => key !== "outputSchema" && key !== "timeoutMs"))
|
|
373
|
+
fail("INVALID_METADATA", "send options must contain only outputSchema and timeoutMs");
|
|
374
|
+
const sendStructuralPath = [...(inheritedHostAgentPath.getStore() ?? [])];
|
|
375
|
+
if (JSON.stringify([sendStructuralPath, inheritedHostWorktreeOwner.getStore() ?? worktreeOwner ?? null]) !== scope)
|
|
376
|
+
fail("INVALID_METADATA", `Agent handle sends must run in the scope that created the handle: ${name}`);
|
|
377
|
+
if (inflight)
|
|
378
|
+
fail("INVALID_METADATA", "Concurrent send(...) calls on one agent handle are unsupported; await each turn");
|
|
379
|
+
const validatedOptions = validateAgentOptions({ ...config, ...sendOptions });
|
|
380
|
+
inflight = true;
|
|
381
|
+
turn += 1;
|
|
382
|
+
try {
|
|
383
|
+
return await bridge.agent(sent[0], validatedOptions, signal, { structuralPath: sendStructuralPath, callSite: `function:${path}:handle:${name}`, occurrence: turn, handle, turn, parentBreadcrumb, ...(scopedWorktreeOwner ? { worktreeOwner: scopedWorktreeOwner } : {}) });
|
|
384
|
+
}
|
|
385
|
+
finally {
|
|
386
|
+
inflight = false;
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
return Object.freeze({ name, send });
|
|
390
|
+
},
|
|
391
|
+
}),
|
|
341
392
|
shell: async (...args) => {
|
|
342
393
|
if (!bridge.shell)
|
|
343
394
|
fail("SHELL_FAILED", "No shell bridge is available");
|
|
@@ -363,7 +414,7 @@ export function withWorkflowFunctions(bridge, store, runContext, registry, setti
|
|
|
363
414
|
phase: (name) => { ownSideEffect(() => bridge.phase?.(name)); },
|
|
364
415
|
log: (message) => { ownSideEffect(() => bridge.log?.(message)); },
|
|
365
416
|
};
|
|
366
|
-
const result = await inheritedHostAgentPath.run([...structuralPath], async () => registry.invokeFunction(name, input, context, path, { get: () => replayed?.value, put: (_path, value) => { stored = value; } }));
|
|
417
|
+
const result = await inheritedHostAgentPath.run([...structuralPath], async () => registry.invokeFunction(name, input, context, path, { get: () => replayed?.value, put: (_path, value) => { stored = value; } })).finally(() => { closed = true; });
|
|
367
418
|
await Promise.all(sideEffects);
|
|
368
419
|
if (!replayed)
|
|
369
420
|
await store.complete(path, stored ?? result);
|
package/dist/src/host-view.d.ts
CHANGED
|
@@ -95,7 +95,6 @@ export interface AgentDetailPresentation {
|
|
|
95
95
|
readonly lastEventAt?: number;
|
|
96
96
|
readonly structuralPath?: readonly string[];
|
|
97
97
|
readonly model?: AgentRecord["model"];
|
|
98
|
-
readonly role?: string;
|
|
99
98
|
readonly tools?: readonly string[];
|
|
100
99
|
readonly attempts?: number;
|
|
101
100
|
readonly startedAt?: number;
|
|
@@ -132,5 +131,6 @@ export declare function formatWorkflowPhaseDashboard(run: PersistedRun, snapshot
|
|
|
132
131
|
export declare function formatNavigatorColumns(sidebar: readonly string[], detail: readonly string[], width: number, layout?: Readonly<{
|
|
133
132
|
treeOnly?: boolean | undefined;
|
|
134
133
|
detailsOnly?: boolean | undefined;
|
|
134
|
+
detailOffset?: number | undefined;
|
|
135
135
|
}>): string[];
|
|
136
136
|
export declare function formatCheckpointReview(checkpoint: AwaitingCheckpoint): string;
|
package/dist/src/host-view.js
CHANGED
|
@@ -686,7 +686,6 @@ export function formatAgentDetail(agent, styles = PLAIN_WORKFLOW_PROGRESS_STYLES
|
|
|
686
686
|
`State: ${state(agent.state)}`,
|
|
687
687
|
...(agent.structuralPath?.length ? [`Structural path: ${agent.structuralPath.join(" > ")}`] : []),
|
|
688
688
|
...(model === undefined ? [] : [`Model: ${model}`]),
|
|
689
|
-
`Role: ${agent.role ?? "(none)"}`,
|
|
690
689
|
`Tools: ${tools}`,
|
|
691
690
|
...(agent.attempts !== undefined && Number.isFinite(agent.attempts) && agent.attempts > 1 ? [`Attempts: ${String(agent.attempts)}`] : []),
|
|
692
691
|
...(duration === undefined ? [] : [`Duration: ${formatWorkflowRuntime(duration)}`]),
|
|
@@ -812,11 +811,10 @@ export function formatNavigatorRun(loaded, checkpoints, worktrees, now = Date.no
|
|
|
812
811
|
const byId = new Map(run.agents.map((agent) => [agent.id, agent]));
|
|
813
812
|
lines.push(...renderGroupedAgents(run.agents, ({ agent, index, depth }, grouped) => {
|
|
814
813
|
const model = `${agent.model.provider}/${agent.model.model}${agent.model.thinking ? `:${agent.model.thinking}` : ""}`;
|
|
815
|
-
const role = agent.role ? ` role=${agent.role}` : "";
|
|
816
814
|
const tools = ` tools=${agent.tools.join(",") || "(none)"}`;
|
|
817
815
|
const accounting = agent.accounting ? ` input=${String(agent.accounting.input)} output=${String(agent.accounting.output)} cache-read=${String(agent.accounting.cacheRead)} cache-write=${String(agent.accounting.cacheWrite)} cost=${formatCost(agent.accounting.cost) || "$0.00"}` : "";
|
|
818
816
|
const indent = " ".repeat((grouped ? 2 : 1) + depth);
|
|
819
|
-
const result = [`${indent}#${String(index + 1)} ${grouped ? agent.label ?? agent.name : agentBreadcrumb(agent, byId)} state=${agent.state} model=${model}${agent.requestedModel ? ` requested=${agent.requestedModel}` : ""}${
|
|
817
|
+
const result = [`${indent}#${String(index + 1)} ${grouped ? agent.label ?? agent.name : agentBreadcrumb(agent, byId)} state=${agent.state} model=${model}${agent.requestedModel ? ` requested=${agent.requestedModel}` : ""}${tools} attempts=${String(agent.attempts)} retries=${String(Math.max(0, agent.attempts - 1))}${accounting}`];
|
|
820
818
|
for (const attempt of agent.attemptDetails ?? [])
|
|
821
819
|
result.push(`${indent} attempt ${String(attempt.attempt)}${attempt.error ? ` error=${attempt.error.code}: ${attempt.error.message}` : ""}`);
|
|
822
820
|
for (const call of agent.toolCalls ?? [])
|
|
@@ -856,17 +854,17 @@ export function formatWorkflowPhaseDashboard(run, snapshot, width, selection = {
|
|
|
856
854
|
return;
|
|
857
855
|
if (child.agent)
|
|
858
856
|
agents.push(child.agent);
|
|
859
|
-
|
|
860
|
-
for (const childId of child.children)
|
|
861
|
-
visit(childId);
|
|
862
|
-
};
|
|
863
|
-
if (node.agent)
|
|
864
|
-
agents.push(node.agent);
|
|
865
|
-
else
|
|
866
|
-
for (const childId of node.children)
|
|
857
|
+
for (const childId of child.children)
|
|
867
858
|
visit(childId);
|
|
859
|
+
};
|
|
860
|
+
visit(node.id);
|
|
868
861
|
return agents;
|
|
869
862
|
};
|
|
863
|
+
const usageLines = (agents) => {
|
|
864
|
+
const spans = agents.flatMap((agent) => agent.startedAt === undefined ? [] : [[agent.startedAt, agent.startedAt + (elapsedDurationMs(agent, now) ?? 0)]]);
|
|
865
|
+
const elapsed = spans.length ? formatWorkflowRuntime(Math.max(...spans.map(([, end]) => end)) - Math.min(...spans.map(([start]) => start))) : "";
|
|
866
|
+
return [...formatAgentAccounting(sumAccounting(agents.map((agent) => agent.accounting ?? zeroAccounting()))), ...(elapsed ? [`Elapsed: ${elapsed}`] : [])];
|
|
867
|
+
};
|
|
870
868
|
const nodeStatus = (node) => phaseStyle(node.state)(node.state);
|
|
871
869
|
const nodeIcon = (node) => node.children.length ? expanded.has(node.id) ? "▾" : "▸" : node.kind === "agent" ? "•" : node.kind === "shell" ? "◇" : " ";
|
|
872
870
|
const treeLine = (node) => {
|
|
@@ -887,12 +885,12 @@ export function formatWorkflowPhaseDashboard(run, snapshot, width, selection = {
|
|
|
887
885
|
const selected = node.phase;
|
|
888
886
|
const counts = selected?.counts ?? phaseAgentCounts(agents);
|
|
889
887
|
const hint = node.children.length ? "enter expand/collapse" : "enter phase details";
|
|
890
|
-
return [styles.bold(`Selected phase: ${node.label}`), `Status: ${nodeStatus(node)}`, `agents completed=${String(counts.completed)} running=${String(counts.running)} failed=${String(counts.failed)} cancelled=${String(counts.cancelled)} pending=${String(counts.pending)}`, `Agents: ${String(agents.length)}`, ...(selection.actions ? [] : [styles.muted(hint)])];
|
|
888
|
+
return [styles.bold(`Selected phase: ${node.label}`), `Status: ${nodeStatus(node)}`, `agents completed=${String(counts.completed)} running=${String(counts.running)} failed=${String(counts.failed)} cancelled=${String(counts.cancelled)} pending=${String(counts.pending)}`, `Agents: ${String(agents.length)}`, ...usageLines(agents), ...(selection.actions ? [] : [styles.muted(hint)])];
|
|
891
889
|
}
|
|
892
890
|
if (node.kind === "operation") {
|
|
893
891
|
const states = phaseAgentCounts(agents);
|
|
894
892
|
const hint = node.children.length ? "enter expand/collapse" : "enter phase details";
|
|
895
|
-
return [styles.bold(`Selected operation: ${node.operationPath.join(" > ")}`), `Phase: ${node.phase?.name ?? node.phaseId}`, `Status: ${nodeStatus(node)}`, `agents completed=${String(states.completed)} running=${String(states.running)} failed=${String(states.failed)} cancelled=${String(states.cancelled)} pending=${String(states.pending)}`, `Agents: ${String(agents.length)}`, ...(selection.actions ? [] : [styles.muted(hint)])];
|
|
893
|
+
return [styles.bold(`Selected operation: ${node.operationPath.join(" > ")}`), `Phase: ${node.phase?.name ?? node.phaseId}`, `Status: ${nodeStatus(node)}`, `agents completed=${String(states.completed)} running=${String(states.running)} failed=${String(states.failed)} cancelled=${String(states.cancelled)} pending=${String(states.pending)}`, `Agents: ${String(agents.length)}`, ...usageLines(agents), ...(selection.actions ? [] : [styles.muted(hint)])];
|
|
896
894
|
}
|
|
897
895
|
if (node.kind === "shell") {
|
|
898
896
|
return [styles.bold("Selected shell"), formatShellActivity(node.shellActivity?.active, node.shellActivity?.startedAt, "⠦", styles, now) ?? "shell [running]"];
|
|
@@ -928,7 +926,8 @@ export function formatWorkflowPhaseDashboard(run, snapshot, width, selection = {
|
|
|
928
926
|
return ["", styles.bold(actions.title), ...actions.options.map((option, index) => `${index === actions.index ? "→ " : " "}${index === actions.index ? styles.accent(option) : option}`)];
|
|
929
927
|
};
|
|
930
928
|
const treeRows = [styles.bold("Tree"), ...(visibleNodes.length ? visibleNodes.map(treeLine) : [styles.muted("(empty)")])];
|
|
931
|
-
|
|
929
|
+
const detailOffset = Math.max(0, (selection.scrollTop ?? 0) - lines.flatMap(wrap).length);
|
|
930
|
+
lines.push(...formatNavigatorColumns(treeRows, [...details(selectedNode), ...actionRows()], safeWidth, { ...selection, detailOffset }));
|
|
932
931
|
if (model.unassignedAgents?.length && !tree.nodes.some((node) => node.phaseId === "unassigned"))
|
|
933
932
|
lines.push(...wrap(styles.muted(`Unassigned agents: ${String(model.unassignedAgents.length)}`)));
|
|
934
933
|
return lines.flatMap((line) => wrap(line));
|
|
@@ -942,7 +941,10 @@ export function formatNavigatorColumns(sidebar, detail, width, layout = {}) {
|
|
|
942
941
|
if (safeWidth >= 80) {
|
|
943
942
|
const sidebarWidth = Math.min(42, Math.max(24, Math.floor((safeWidth - 3) * 0.38)));
|
|
944
943
|
const left = sidebar.flatMap((line) => wrapNavigatorLine(line, sidebarWidth));
|
|
945
|
-
const
|
|
944
|
+
const wrapped = detail.flatMap((line) => wrapNavigatorLine(line, safeWidth - sidebarWidth - 3));
|
|
945
|
+
// Keep the details in view when the tree is scrolled, without making the block taller than the tree.
|
|
946
|
+
const shift = Math.min(Math.max(0, layout.detailOffset ?? 0), Math.max(0, left.length - wrapped.length));
|
|
947
|
+
const right = [...Array(shift).fill(""), ...wrapped];
|
|
946
948
|
return Array.from({ length: Math.max(left.length, right.length) }, (_, index) => {
|
|
947
949
|
const cell = left[index] ?? "";
|
|
948
950
|
return `${cell}${" ".repeat(Math.max(0, sidebarWidth - visibleWidth(cell)))} | ${right[index] ?? ""}`;
|
package/dist/src/host.js
CHANGED
|
@@ -12,9 +12,8 @@ import { registerWorkflowNavigator, uiHostCapabilities } from "./host-navigator.
|
|
|
12
12
|
import { acquireSessionLease, isPersistedRun, listPersistedSessionIds, listRunIds, RunStore, structuralPath as operationPath } from "./persistence.js";
|
|
13
13
|
import { retainTerminalRuns } from "./retention.js";
|
|
14
14
|
import { validateBudget, WorkflowBudgetRuntime } from "./budget.js";
|
|
15
|
-
import { SerialLane, asWorkflowError, createLaunchSnapshot, errorCode, errorText, fail, isNodeError, jsonValue,
|
|
16
|
-
import { loadCodemodeToolsSetting,
|
|
17
|
-
import { loadAgentDefinitions, loadProjectAgentDefinitions } from "./roles.js";
|
|
15
|
+
import { SerialLane, asWorkflowError, createLaunchSnapshot, errorCode, errorText, fail, isNodeError, jsonValue, modelAliasErrorName, object, parseModelReference, positiveInteger, reachableTools, resolveModelReference, VIRTUAL_MODEL_PROVIDER, sanitizeDisplayText, validateModelAliases } from "./utils.js";
|
|
16
|
+
import { loadCodemodeToolsSetting, preflight, resolveAgentResourcePolicy, staticAgentPreparationOptions, resolveWorkflowSettings, validateCheckpoint, validateModelAliasAvailability, validateWorkflowLaunchWithRegistry, workflowProjectSettingsPath, workflowSettingsPath, workflowToolExposure } from "./validation.js";
|
|
18
17
|
import { beginWorkflowExtensionLoading, loadingRegistry, resetWorkflowRegistryIfIdle, retainWorkflowRegistry } from "./registry.js";
|
|
19
18
|
import { agentHandleTurnPath, agentIdentityPath, agentWorktree, encoded, executeShellCommand, persistActiveAgentAttempt, persistAgentAttempts, readShellResult, runWorkflow, shellIdentityPath, toolIdentityPath } from "./execution.js";
|
|
20
19
|
import { prepareScriptToolLoadout, scriptTool, scriptToolContext, scriptToolValue, validateScriptToolReferences } from "./script-tools.js";
|
|
@@ -23,9 +22,9 @@ import { showChangelogNotice } from "./changelog.js";
|
|
|
23
22
|
import { createTrajectoryRunLoader, createTrajectoryRunMetadataLoader, createTrajectorySubagentLoader, createTrajectorySubagentMetadataLoader, createTrajectoryTranscriptLoader } from "./trajectory.js";
|
|
24
23
|
import { getTrajectoryHost } from "./trajectory-host-handle.js";
|
|
25
24
|
import { getSubagentManager } from "./subagent-manager-handle.js";
|
|
26
|
-
import { HARD_TERMINAL_RUN_STATES, LAUNCH_SNAPSHOT_IDENTITY_VERSION, THINKING_LEVELS, WORKFLOW_BLOCKED_EVENT, WorkflowError, isContextFileScope, isExternallyEndedRunState, isHardTerminalRunState
|
|
27
|
-
import { SETTLED_AGENT_STATES, catalogResultValue, formatWorkflowCatalog, styledTextBlock, textBlock, workflowCatalogBlock, workflowControlCall, workflowControlResult, workflowProgressBlock
|
|
28
|
-
import { DELIVERY_LIMIT_BYTES, ForegroundDeliveryController, markWorkflowFailureDiagnostics, WORKFLOW_LOG_ENTRY, completionDescriptor, completionDeliveryFromStore, createWorkflowFailureDiagnostics, failureDiagnosticsFrom, formatWorkflowFailure, formatWorkflowFailureDelivery, formatWorkflowFailureDeliveryFallback, formatWorkflowFailureDiagnostics, isWorkflowFailureDiagnostics, serializeWorkflowFailureDiagnostics, utf8Prefix
|
|
25
|
+
import { HARD_TERMINAL_RUN_STATES, LAUNCH_SNAPSHOT_IDENTITY_VERSION, THINKING_LEVELS, WORKFLOW_BLOCKED_EVENT, WorkflowError, isContextFileScope, isExternallyEndedRunState, isHardTerminalRunState } from "./types.js";
|
|
26
|
+
import { SETTLED_AGENT_STATES, catalogResultValue, formatWorkflowCatalog, styledTextBlock, textBlock, workflowCatalogBlock, workflowControlCall, workflowControlResult, workflowProgressBlock } from "./host-view.js";
|
|
27
|
+
import { DELIVERY_LIMIT_BYTES, ForegroundDeliveryController, markWorkflowFailureDiagnostics, WORKFLOW_LOG_ENTRY, completionDescriptor, completionDeliveryFromStore, createWorkflowFailureDiagnostics, failureDiagnosticsFrom, formatWorkflowFailure, formatWorkflowFailureDelivery, formatWorkflowFailureDeliveryFallback, formatWorkflowFailureDiagnostics, isWorkflowFailureDiagnostics, serializeWorkflowFailureDiagnostics, utf8Prefix } from "./host-delivery.js";
|
|
29
28
|
export { agentBreadcrumb, agentBreadcrumbParts, formatBudgetStatus, formatNavigatorDashboard, formatNavigatorRun, formatStalledDuration, formatWorkflowPhaseDashboard, formatWorkflowProgress, navigatorAttentionSort, truncateWorkflowProgress, } from "./host-view.js";
|
|
30
29
|
export { buildWorkflowPhaseModel, buildWorkflowPhaseTree, navigateWorkflowPhaseTree, preserveWorkflowPhaseSelection, preserveWorkflowPhaseTreeSelection, workflowPhaseTreeInitialExpanded, workflowPhaseTreeVisibleNodes } from "./host-phases.js";
|
|
31
30
|
export { formatWorkflowFailure, formatWorkflowFailureDelivery, formatWorkflowFailureDiagnostics } from "./host-delivery.js";
|
|
@@ -84,27 +83,6 @@ function completionControlContent(result, controlRunId) {
|
|
|
84
83
|
delete record.run;
|
|
85
84
|
return JSON.stringify({ ...record, value });
|
|
86
85
|
}
|
|
87
|
-
/** Persists a role definition (and its model) into the launch snapshot the first time an agent uses it, so later replays and retries see the definition the run actually ran with. */
|
|
88
|
-
function roleCapture(store, initial, definitions, projectDefinitions) {
|
|
89
|
-
let persisted = initial;
|
|
90
|
-
return {
|
|
91
|
-
get snapshot() { return persisted; },
|
|
92
|
-
async capture(role, model) {
|
|
93
|
-
const definition = definitions[role];
|
|
94
|
-
if (!definition)
|
|
95
|
-
return;
|
|
96
|
-
const modelName = `${model.provider}/${model.model}`;
|
|
97
|
-
const hasProjectRole = projectDefinitions[role] !== undefined;
|
|
98
|
-
if (persisted.roles?.[role] !== undefined && (!hasProjectRole || persisted.projectRoles?.includes(role)) && persisted.models.includes(modelName))
|
|
99
|
-
return;
|
|
100
|
-
const roles = { ...(persisted.roles ?? {}), [role]: definition };
|
|
101
|
-
const projectRoles = hasProjectRole ? [...new Set([...(persisted.projectRoles ?? []), role])] : persisted.projectRoles ?? [];
|
|
102
|
-
const models = [...new Set([...persisted.models, modelName])];
|
|
103
|
-
persisted = createLaunchSnapshot({ ...persisted, models, roles, projectRoles });
|
|
104
|
-
await store.saveSnapshot(persisted);
|
|
105
|
-
},
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
86
|
export function formatWorkflowPreview(args) {
|
|
109
87
|
const name = typeof args.name === "string" && args.name.trim() ? args.name.trim() : "workflow";
|
|
110
88
|
if (typeof args.script !== "string" || !args.script.trim())
|
|
@@ -283,6 +261,21 @@ async function resolveLaunchAliases(registry, staticAliases, context, availableM
|
|
|
283
261
|
throw error;
|
|
284
262
|
}
|
|
285
263
|
}
|
|
264
|
+
/**
|
|
265
|
+
* Prepares each completely static agent configuration in inspection mode, so preparation plugins resolve their own aliases
|
|
266
|
+
* and options without persisting a configuration. Any preparation failure rejects launch before effects; cancellation stops
|
|
267
|
+
* the remaining inspections.
|
|
268
|
+
*/
|
|
269
|
+
async function inspectStaticAgentConfigurations(executor, script, workflowName, signal, functions) {
|
|
270
|
+
// NOTE: inspection uses the launch cwd; agents inside withWorktree(...) prepare against their checkout at runtime.
|
|
271
|
+
for (const agentOptions of staticAgentPreparationOptions(script, functions)) {
|
|
272
|
+
if (signal.aborted)
|
|
273
|
+
throw new WorkflowError("CANCELLED", "Workflow launch cancelled");
|
|
274
|
+
await executor.prepare({ label: "agent", workflowName, agentOptions }, undefined, signal, "inspection");
|
|
275
|
+
}
|
|
276
|
+
if (signal.aborted)
|
|
277
|
+
throw new WorkflowError("CANCELLED", "Workflow launch cancelled");
|
|
278
|
+
}
|
|
286
279
|
export default function workflowExtension(pi, home, clipboard = copyToClipboard, transport = localAgentTransport, agentDir, additionalSkillPaths = []) {
|
|
287
280
|
beginWorkflowExtensionLoading();
|
|
288
281
|
const registry = loadingRegistry();
|
|
@@ -298,7 +291,7 @@ export default function workflowExtension(pi, home, clipboard = copyToClipboard,
|
|
|
298
291
|
});
|
|
299
292
|
let backgroundWidgetEnabled = true;
|
|
300
293
|
try {
|
|
301
|
-
const globalSettings =
|
|
294
|
+
const globalSettings = resolveWorkflowSettings(process.cwd(), false, workflowSettingsPath(extensionAgentDir)).effective;
|
|
302
295
|
backgroundWidgetEnabled = globalSettings.backgroundWidget ?? true;
|
|
303
296
|
// Registered at load, before Pi resolves --model and restores the session model. Project aliases need project trust, which is unknown here.
|
|
304
297
|
registerAliasVirtualModels(pi, Object.keys(globalSettings.modelAliases ?? {}), workflowSettingsPath(extensionAgentDir));
|
|
@@ -338,6 +331,9 @@ export default function workflowExtension(pi, home, clipboard = copyToClipboard,
|
|
|
338
331
|
return skillPath ? { skillPaths: [skillPath] } : undefined;
|
|
339
332
|
});
|
|
340
333
|
const runs = new Map();
|
|
334
|
+
// Extension load failures a headless launcher reported for this host. They hold for the whole process, so every executor,
|
|
335
|
+
// including those that paused resume, cold resume and retry create, keeps failing ownerless options closed.
|
|
336
|
+
let extensionLoad;
|
|
341
337
|
const deliveryController = new ForegroundDeliveryController({ runs, ...(typeof pi.sendMessage === "function" ? { deliver: (content) => { deliver(pi, content); } } : {}) });
|
|
342
338
|
let releaseWorkflowRegistry;
|
|
343
339
|
const providerRecoveryLane = new SerialLane();
|
|
@@ -817,7 +813,7 @@ export default function workflowExtension(pi, home, clipboard = copyToClipboard,
|
|
|
817
813
|
const persisted = await persistRunState(run.store, run.metadata, (current) => ({ ...current, ...run.budget.snapshot(), agents: current.agents.map((agent) => agent.id === id ? { ...agent, lastEventAt } : agent) }));
|
|
818
814
|
run.update?.(workflowToolUpdate(withLiveActivities(persisted)));
|
|
819
815
|
};
|
|
820
|
-
const result = await run.executor.execute(prompt, { label: options.label, workflowName: run.metadata.name, tuiIndex, tuiLabel: options.requestedLabel ?? options.label, agentNodeId: id, ...(options.extensionSettings === undefined ? {} : { inheritedExtensionSettings: options.extensionSettings }), onProgress, onAttempt, budget, ...(run.providerErrorRecovery ? { providerErrorRecovery: run.providerErrorRecovery } : {}), ...(parentId ? { parent: parentId, cwd: options.cwd, ...(options.worktreeOwner ? { worktreeOwner: options.worktreeOwner } : {}) } : options.worktreeOwner ? { worktreeOwner: options.worktreeOwner } : {}), ...(options.model ? { model: options.model } : {}), ...(options.
|
|
816
|
+
const result = await run.executor.execute(prompt, { label: options.label, workflowName: run.metadata.name, tuiIndex, tuiLabel: options.requestedLabel ?? options.label, agentNodeId: id, ...(options.extensionSettings === undefined ? {} : { inheritedExtensionSettings: options.extensionSettings }), onProgress, onAttempt, budget, ...(run.providerErrorRecovery ? { providerErrorRecovery: run.providerErrorRecovery } : {}), ...(parentId ? { parent: parentId, cwd: options.cwd, ...(options.worktreeOwner ? { worktreeOwner: options.worktreeOwner } : {}) } : options.worktreeOwner ? { worktreeOwner: options.worktreeOwner } : {}), ...(options.model ? { model: options.model } : {}), ...(options.contextFiles ? { contextFiles: options.contextFiles } : {}), tools: options.tools, ...(options.skills ? { skills: options.skills } : {}), ...(options.extensions ? { extensions: options.extensions } : {}), effectiveTools: options.tools, ...(options.schema ? { schema: options.schema } : {}), ...(options.retries === undefined ? {} : { retries: options.retries }), ...(options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }), ...(options.sessionPath ? { sessionPath: options.sessionPath } : {}), ...(options.agentOptions ? { agentOptions: options.agentOptions } : {}), ...(options.agentIdentity ? { agentIdentity: options.agentIdentity } : {}), ...(options.configuration ? { configuration: options.configuration } : {}), ...(options.capabilities ? { capabilities: options.capabilities } : {}), ...(options.projectTrusted === undefined ? {} : { projectTrusted: options.projectTrusted }), ...(options.inheritedExcludeTools ? { inheritedExcludeTools: options.inheritedExcludeTools } : {}), onConfiguration: (configuration) => scheduler.setConfiguration(id, configuration) }, signal, scheduler.toolsFor(id), setSteer, () => { scheduler.cancelChildren(id); scheduler.retry(id); });
|
|
821
817
|
cancelDeferredProgress();
|
|
822
818
|
const before = (await run.store.load()).run;
|
|
823
819
|
await persistAgentAttempts(run.store, id, result.attempts);
|
|
@@ -855,7 +851,7 @@ export default function workflowExtension(pi, home, clipboard = copyToClipboard,
|
|
|
855
851
|
const existing = new Map(current.agents.map((agent) => [agent.id, agent]));
|
|
856
852
|
const agents = ownership.map((node) => {
|
|
857
853
|
const previous = existing.get(node.id);
|
|
858
|
-
const requested = { label: node.options.label, workflowName: run.metadata.name, ...(node.options.skills ? { skills: node.options.skills } : {}), ...(node.options.extensions ? { extensions: node.options.extensions } : {}), ...(node.options.model ? { model: node.options.model } : {}), ...(node.options.
|
|
854
|
+
const requested = { label: node.options.label, workflowName: run.metadata.name, ...(node.options.skills ? { skills: node.options.skills } : {}), ...(node.options.extensions ? { extensions: node.options.extensions } : {}), ...(node.options.model ? { model: node.options.model } : {}), ...(node.options.contextFiles ? { contextFiles: node.options.contextFiles } : {}), ...(node.options.configuration ? { configuration: node.options.configuration } : {}) };
|
|
859
855
|
let effective;
|
|
860
856
|
try {
|
|
861
857
|
effective = { ...run.executor.resolve({ ...requested, effectiveTools: node.options.tools }), tools: node.options.tools };
|
|
@@ -864,12 +860,11 @@ export default function workflowExtension(pi, home, clipboard = copyToClipboard,
|
|
|
864
860
|
effective = previous ? { model: previous.model, ...(previous.requestedModel ? { requestedModel: previous.requestedModel } : {}), tools: previous.tools } : { model: node.options.model ? modelSpec(node.options.model, run.model) : run.model, ...(node.options.model ? { requestedModel: node.options.model } : {}), tools: node.options.tools };
|
|
865
861
|
}
|
|
866
862
|
const resultPath = !node.parentId && node.options.agentIdentity ? agentIdentityPath(node.options.agentIdentity) : undefined;
|
|
867
|
-
const nodeRole = roleNameOf(node.options.role);
|
|
868
863
|
const now = Date.now();
|
|
869
864
|
const lastEventAt = node.state === "running" ? previous?.state === "running" && previous.lastEventAt !== undefined ? previous.lastEventAt : now : previous?.lastEventAt;
|
|
870
865
|
const startedAt = previous?.startedAt ?? (node.state === "running" ? now : undefined);
|
|
871
866
|
const durationMs = previous?.durationMs ?? (SETTLED_AGENT_STATES.has(node.state) && startedAt !== undefined ? Math.max(0, now - startedAt) : undefined);
|
|
872
|
-
return { ...(previous?.systemPrompt === undefined ? {} : { systemPrompt: previous.systemPrompt }), ...(node.prompt !== undefined ? { prompt: node.prompt } : previous?.prompt !== undefined ? { prompt: previous.prompt } : {}), id: node.id, name: node.label, ...(node.options.requestedLabel ? { label: node.options.requestedLabel } : {}), path: node.id, state: node.state, ...(node.parentId ? { parentId: node.parentId } : {}), structuralPath: [...(node.options.agentIdentity?.structuralPath ?? [])], ...(resultPath ? { resultPath } : {}), ...(node.options.parentBreadcrumb ? { parentBreadcrumb: node.options.parentBreadcrumb } : {}), ...(node.options.worktreeOwner ? { worktreeOwner: node.options.worktreeOwner } : {}), ...(node.options.agentIdentity?.handle === undefined ? {} : { handle: node.options.agentIdentity.handle, ...(node.options.agentIdentity.turn === undefined ? {} : { turn: node.options.agentIdentity.turn }), ...(node.options.continuity ? { continuity: node.options.continuity } : {}) }), ...(
|
|
867
|
+
return { ...(previous?.systemPrompt === undefined ? {} : { systemPrompt: previous.systemPrompt }), ...(node.prompt !== undefined ? { prompt: node.prompt } : previous?.prompt !== undefined ? { prompt: previous.prompt } : {}), id: node.id, name: node.label, ...(node.options.requestedLabel ? { label: node.options.requestedLabel } : {}), path: node.id, state: node.state, ...(node.parentId ? { parentId: node.parentId } : {}), structuralPath: [...(node.options.agentIdentity?.structuralPath ?? [])], ...(resultPath ? { resultPath } : {}), ...(node.options.parentBreadcrumb ? { parentBreadcrumb: node.options.parentBreadcrumb } : {}), ...(node.options.worktreeOwner ? { worktreeOwner: node.options.worktreeOwner } : {}), ...(node.options.agentIdentity?.handle === undefined ? {} : { handle: node.options.agentIdentity.handle, ...(node.options.agentIdentity.turn === undefined ? {} : { turn: node.options.agentIdentity.turn }), ...(node.options.continuity ? { continuity: node.options.continuity } : {}) }), ...(effective.requestedModel ? { requestedModel: effective.requestedModel } : {}), model: effective.model, tools: effective.tools, attempts: previous?.attempts ?? 0, ...(startedAt === undefined ? {} : { startedAt }), ...(durationMs === undefined ? {} : { durationMs }), ...(previous?.attemptDetails ? { attemptDetails: previous.attemptDetails } : {}), ...(previous?.accounting ? { accounting: previous.accounting } : {}), ...(previous?.toolCalls ? { toolCalls: previous.toolCalls } : {}), ...(previous?.activity ? { activity: previous.activity } : {}), ...(lastEventAt === undefined ? {} : { lastEventAt }) };
|
|
873
868
|
});
|
|
874
869
|
return { ...current, agents };
|
|
875
870
|
});
|
|
@@ -1093,21 +1088,7 @@ export default function workflowExtension(pi, home, clipboard = copyToClipboard,
|
|
|
1093
1088
|
});
|
|
1094
1089
|
catalogRegistered = true;
|
|
1095
1090
|
};
|
|
1096
|
-
const createAgentExecutor = (root) => new WorkflowAgentExecutor({ ...root, agentDir: extensionAgentDir, ...(additionalSkillPaths.length ? { additionalSkillPaths } : {}), agentSetupHooks: registry.agentSetupHooks(), validateExtensionSettings: registry.validateExtensionSettings, onResourceWarning: (message) => { deliverWarning(pi, message); }, onAgentSettings: (agentId, settings) => { scheduler.setExtensionSettings(agentId, settings); } }, transport);
|
|
1097
|
-
// Resumed runs keep the role definitions frozen in their snapshot and fall back to the current definitions for roles a
|
|
1098
|
-
// registered function reaches for the first time after the restart; those are captured into the snapshot like at launch.
|
|
1099
|
-
const resumeRoles = (store, snapshot, cwd, trustedProject) => {
|
|
1100
|
-
let current = {};
|
|
1101
|
-
let project = {};
|
|
1102
|
-
try {
|
|
1103
|
-
current = loadAgentDefinitions(cwd, extensionAgentDir, trustedProject, registry.roleDirectoryRegistrations());
|
|
1104
|
-
project = trustedProject ? loadProjectAgentDefinitions(cwd) : {};
|
|
1105
|
-
}
|
|
1106
|
-
catch { /* Unreadable current roles leave the resume with the snapshot roles, as before. */ }
|
|
1107
|
-
const definitions = { ...current, ...(snapshot.roles ?? {}) };
|
|
1108
|
-
const captured = roleCapture(store, snapshot, definitions, project);
|
|
1109
|
-
return { definitions, capture: (role, model) => captured.capture(role, model) };
|
|
1110
|
-
};
|
|
1091
|
+
const createAgentExecutor = (root) => new WorkflowAgentExecutor({ ...root, ...(extensionLoad ? { extensionLoad } : {}), agentDir: extensionAgentDir, ...(additionalSkillPaths.length ? { additionalSkillPaths } : {}), agentPreparationHooks: registry.agentPreparationHooks(), agentSetupHooks: registry.agentSetupHooks(), validateExtensionSettings: registry.validateExtensionSettings, onResourceWarning: root.onResourceWarning ?? ((message) => { deliverWarning(pi, message); }), onAgentSettings: (agentId, settings) => { scheduler.setExtensionSettings(agentId, settings); }, onAgentCapabilities: (agentId, capabilities, projectTrusted) => { scheduler.setAttemptCapabilities(agentId, capabilities, projectTrusted); } }, transport);
|
|
1111
1092
|
const activeSnapshotTools = (tools, active) => active === "session"
|
|
1112
1093
|
? new Set(tools.filter((tool) => reachableTools(pi).includes(tool) && tool !== "workflow_catalog"))
|
|
1113
1094
|
: new Set(tools.filter((tool) => active.has(tool) || tool === "workflow_catalog"));
|
|
@@ -1121,27 +1102,27 @@ export default function workflowExtension(pi, home, clipboard = copyToClipboard,
|
|
|
1121
1102
|
const extensionSettings = input.snapshot.settings.extensionSettings;
|
|
1122
1103
|
const extensionSettingsPath = input.snapshot.settingsSources?.extensionSettings ?? input.snapshot.settingsPath ?? settingsPath;
|
|
1123
1104
|
registry.validateExtensionSettings(extensionSettings, { source: "effective", cwd: input.cwd, projectTrusted: input.trustedProject, settingsPath: extensionSettingsPath });
|
|
1124
|
-
for (const [role, definition] of Object.entries(input.snapshot.roles ?? {}))
|
|
1125
|
-
registry.validateExtensionSettings(mergeWorkflowExtensionSettings(extensionSettings, definition.extensionSettings), { source: "role", cwd: input.cwd, projectTrusted: input.trustedProject, settingsPath: extensionSettingsPath, role });
|
|
1126
1105
|
const currentPolicy = resolveAgentResourcePolicy(input.cwd, input.trustedProject, settingsPath);
|
|
1127
1106
|
const staticAliases = resolution.effective.modelAliases ?? {};
|
|
1128
1107
|
const previousAliases = input.snapshot.modelAliases ?? input.snapshot.settings.modelAliases ?? {};
|
|
1129
1108
|
const inventory = modelInventory(input.rootModel, input.modelRegistry);
|
|
1130
1109
|
const knownModels = input.modelRegistry ? inventory.knownModels : new Set([...input.snapshot.models, ...inventory.knownModels]);
|
|
1131
1110
|
const availableModels = input.modelRegistry ? inventory.availableModels : new Set([...input.snapshot.models, ...inventory.availableModels]);
|
|
1132
|
-
const
|
|
1111
|
+
const resolved = input.resolvedAliases ? { aliases: input.resolvedAliases, dynamicNames: input.resolvedDynamicAliasNames ?? [] } : await resolveLaunchAliases(registry, staticAliases, { cwd: input.cwd, projectTrusted: input.trustedProject, rootModel: input.rootModel, knownModels, availableModels, signal: input.signal }, availableModels, knownModels, settingsPath);
|
|
1112
|
+
const currentAliases = resolved.aliases;
|
|
1113
|
+
const dynamicAliasNames = resolved.dynamicNames;
|
|
1133
1114
|
const blockedAliases = input.blockedAliases ?? new Set(Object.keys(previousAliases).filter((name) => !Object.prototype.hasOwnProperty.call(currentAliases, name)));
|
|
1134
1115
|
const blockedAliasTargets = input.blockedAliasTargets ?? Object.fromEntries(Object.entries(previousAliases).filter(([name]) => !Object.prototype.hasOwnProperty.call(currentAliases, name)));
|
|
1135
1116
|
const script = input.withPreflight ? input.snapshot.script : undefined;
|
|
1136
1117
|
if (script !== undefined) {
|
|
1137
1118
|
const resumeAliases = { ...previousAliases, ...currentAliases };
|
|
1138
|
-
preflight(script, { models: availableModels, tools: active,
|
|
1119
|
+
preflight(script, { models: availableModels, tools: active, modelAliases: resumeAliases, knownModels, settingsPath, skipModelAvailability: true }, input.snapshot.schemas, input.snapshot.metadata, true);
|
|
1139
1120
|
}
|
|
1140
1121
|
const refreshed = resumedSnapshotSettings(input.snapshot, resolution, currentAliases);
|
|
1141
1122
|
const snapshot = createLaunchSnapshot({ ...input.snapshot, settingsPath, ...refreshed, modelAliases: currentAliases });
|
|
1142
|
-
return { active, settingsPath, resolution, currentPolicy, previousAliases, knownModels, availableModels, currentAliases, blockedAliases, blockedAliasTargets, snapshot, script };
|
|
1123
|
+
return { active, settingsPath, resolution, currentPolicy, previousAliases, knownModels, availableModels, currentAliases, dynamicAliasNames, blockedAliases, blockedAliasTargets, snapshot, script };
|
|
1143
1124
|
};
|
|
1144
|
-
const workflowAgentHandler = (store, metadata, lifecycle, executor, cwd, runId
|
|
1125
|
+
const workflowAgentHandler = (store, metadata, lifecycle, executor, cwd, runId) => {
|
|
1145
1126
|
// Replay lookup and spawn are serialised per run so concurrent agent(...) calls reach the scheduler in call order.
|
|
1146
1127
|
const admission = new SerialLane();
|
|
1147
1128
|
return async (prompt, options, agentSignal, identity) => {
|
|
@@ -1154,21 +1135,21 @@ export default function workflowExtension(pi, home, clipboard = copyToClipboard,
|
|
|
1154
1135
|
return { replayed };
|
|
1155
1136
|
const worktree = agentWorktree(identity);
|
|
1156
1137
|
const agentCwd = worktree.worktreeOwner ? (await persistWorktree(store, metadata, worktree.worktreeOwner)).cwd : cwd;
|
|
1157
|
-
const role = typeof options.role === "string" ? options.role : undefined;
|
|
1158
1138
|
const model = typeof options.model === "string" ? options.model : undefined;
|
|
1159
1139
|
const requestedLabel = typeof options.label === "string" ? options.label : undefined;
|
|
1160
1140
|
const skills = Array.isArray(options.skills) ? options.skills : undefined;
|
|
1161
1141
|
const extensions = Array.isArray(options.extensions) ? options.extensions : undefined;
|
|
1162
1142
|
const contextFiles = Array.isArray(options.contextFiles) && options.contextFiles.every(isContextFileScope) ? options.contextFiles : undefined;
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
const
|
|
1143
|
+
// A paused resume replaces the run executor with refreshed aliases; new identities must prepare against it.
|
|
1144
|
+
const current = runs.get(runId)?.executor ?? executor;
|
|
1145
|
+
const configuration = await current.prepare({ label: requestedLabel ?? "agent", workflowName: metadata.name, agentOptions: options, agentIdentity: identity, ...(worktree.worktreeOwner ? { worktreeOwner: worktree.worktreeOwner } : {}) }, agentCwd, agentSignal);
|
|
1146
|
+
const resolved = configuration;
|
|
1147
|
+
const label = requestedLabel ?? configuration.label ?? resolved.model.model;
|
|
1167
1148
|
const tools = resolved.tools;
|
|
1168
1149
|
const schema = object(options.outputSchema) ? options.outputSchema : undefined;
|
|
1169
1150
|
const sessionPath = identity.handle !== undefined && identity.turn !== undefined ? await handleTurnInput(store, identity.handle, identity.turn) : undefined;
|
|
1170
1151
|
const continuity = identity.handle === undefined ? undefined : sessionPath ? "continued" : "fresh";
|
|
1171
|
-
return { spawned: scheduler.spawn(runId, prompt, { label, ...(requestedLabel ? { requestedLabel } : {}), ...(identity.parentBreadcrumb ? { parentBreadcrumb: identity.parentBreadcrumb } : {}), cwd: agentCwd, tools, ...(skills ? { skills } : {}), ...(extensions ? { extensions } : {}), ...worktree, ...(model ? { model } : {}), ...(
|
|
1152
|
+
return { spawned: scheduler.spawn(runId, prompt, { label, ...(requestedLabel ? { requestedLabel } : {}), ...(identity.parentBreadcrumb ? { parentBreadcrumb: identity.parentBreadcrumb } : {}), cwd: agentCwd, tools, ...(skills ? { skills } : {}), ...(extensions ? { extensions } : {}), ...worktree, ...(model ? { model } : {}), ...(contextFiles ? { contextFiles } : {}), ...(schema ? { schema } : {}), ...(typeof options.retries === "number" ? { retries: options.retries } : {}), ...(positiveInteger(options.timeoutMs) || options.timeoutMs === null ? { timeoutMs: options.timeoutMs } : {}), ...(sessionPath ? { sessionPath } : {}), ...(continuity ? { continuity } : {}), agentOptions: options, agentIdentity: identity, configuration }) };
|
|
1172
1153
|
});
|
|
1173
1154
|
if ("replayed" in admitted)
|
|
1174
1155
|
return admitted.replayed.value;
|
|
@@ -1191,7 +1172,7 @@ export default function workflowExtension(pi, home, clipboard = copyToClipboard,
|
|
|
1191
1172
|
};
|
|
1192
1173
|
};
|
|
1193
1174
|
const recovery = createWorkflowRecovery({
|
|
1194
|
-
pi, home, runs, scheduler, eventPublisher, persistRunState, projectTrusted, resumeHostContext, ensureSessionLease, coordinateRunMutation, createAgentExecutor, activeSnapshotTools, frozenResourcePolicy, resolveLaunchPrologue: resumeLaunchPrologue,
|
|
1175
|
+
pi, home, runs, scheduler, eventPublisher, persistRunState, projectTrusted, resumeHostContext, ensureSessionLease, coordinateRunMutation, createAgentExecutor, activeSnapshotTools, frozenResourcePolicy, resolveLaunchPrologue: resumeLaunchPrologue, workflowAgentHandler, shellForRun, toolForRun, resolveWorktree, checkpointBridge, phaseBridge, logBridge, lifecycleFor, createProviderErrorRecovery, cleanupTerminalRun, deliver: (content) => { deliver(pi, content); }, deliverTerminal: deliveryController.deliverTerminal, workflowToolUpdate, registry, modelSpec,
|
|
1195
1176
|
});
|
|
1196
1177
|
const resumeSelectedWorkflow = async (runId, foreground, context, budgetPatch) => {
|
|
1197
1178
|
const run = runs.get(runId);
|
|
@@ -1212,9 +1193,8 @@ export default function workflowExtension(pi, home, clipboard = copyToClipboard,
|
|
|
1212
1193
|
await deliveryController.moveForegroundToBackground(runId);
|
|
1213
1194
|
if (foreground && !wasAttached) {
|
|
1214
1195
|
run.foreground = true;
|
|
1215
|
-
const loaded = await run.store.load();
|
|
1216
1196
|
await persistRunState(run.store, run.metadata, (current) => ({ ...current, delivery: { ...(current.delivery ?? {}), mode: "foreground", state: "attached" } }));
|
|
1217
|
-
await run.store.
|
|
1197
|
+
await run.store.setLaunchMode("foreground");
|
|
1218
1198
|
}
|
|
1219
1199
|
else if (!foreground)
|
|
1220
1200
|
run.foreground = false;
|
|
@@ -1349,10 +1329,9 @@ export default function workflowExtension(pi, home, clipboard = copyToClipboard,
|
|
|
1349
1329
|
const budgetRuntime = new WorkflowBudgetRuntime(budget, loaded.run.budgetVersion ?? 1, loaded.run.usage, loaded.run.budgetEvents, { active: loaded.run.state === "running" });
|
|
1350
1330
|
const lifecycle = lifecycleFor(store, loaded.run.state, loaded.snapshot.metadata);
|
|
1351
1331
|
const providerPause = async () => { deliver(pi, `Workflow ${loaded.snapshot.metadata.name} paused: provider limit.`); await lifecycle.providerPause(); };
|
|
1352
|
-
const roleDefinitions = loaded.snapshot.roles ?? {};
|
|
1353
1332
|
const abortController = new AbortController();
|
|
1354
1333
|
const providerErrorRecovery = createProviderErrorRecovery(ctx, new Set(loaded.snapshot.models), () => { abortController.abort(); });
|
|
1355
|
-
runs.set(runId, { executor: createAgentExecutor({ cwd: ctx.cwd, model, tools: activeSnapshotTools(loaded.snapshot.tools, "session"), resourceSelectors: snapshotResourcePolicy(loaded.snapshot, store.cwd, projectTrusted(ctx), workflowSettingsPath(extensionAgentDir)).effective, extensionSettings: loaded.snapshot.settings.extensionSettings, availableModels: new Set(loaded.snapshot.models), knownModels: new Set(loaded.snapshot.models), ...(loaded.snapshot.modelAliases ?? loaded.snapshot.settings.modelAliases ? { modelAliases: loaded.snapshot.modelAliases ?? loaded.snapshot.settings.modelAliases } : {}), ...(loaded.snapshot.settingsSources?.modelAliases ? { settingsPath: loaded.snapshot.settingsSources.modelAliases } : loaded.snapshot.settingsPath ? { settingsPath: loaded.snapshot.settingsPath } : {}),
|
|
1334
|
+
runs.set(runId, { executor: createAgentExecutor({ cwd: ctx.cwd, projectTrusted: projectTrusted(ctx), model, tools: activeSnapshotTools(loaded.snapshot.tools, "session"), resourceSelectors: snapshotResourcePolicy(loaded.snapshot, store.cwd, projectTrusted(ctx), workflowSettingsPath(extensionAgentDir)).effective, extensionSettings: loaded.snapshot.settings.extensionSettings, availableModels: new Set(loaded.snapshot.models), knownModels: new Set(loaded.snapshot.models), ...(loaded.snapshot.modelAliases ?? loaded.snapshot.settings.modelAliases ? { modelAliases: loaded.snapshot.modelAliases ?? loaded.snapshot.settings.modelAliases } : {}), ...(loaded.snapshot.settingsSources?.modelAliases ? { settingsPath: loaded.snapshot.settingsSources.modelAliases } : loaded.snapshot.settingsPath ? { settingsPath: loaded.snapshot.settingsPath } : {}), runStore: store, providerPause, agentResourcePolicy: frozenResourcePolicy(snapshotResourcePolicy(loaded.snapshot, store.cwd, projectTrusted(ctx), workflowSettingsPath(extensionAgentDir))) }), store, metadata: loaded.snapshot.metadata, model, lifecycle, budget: budgetRuntime, abortController, projectTrusted: () => projectTrusted(ctx), checkpointResolvers: new Map(), ...(providerErrorRecovery ? { providerErrorRecovery } : {}) });
|
|
1356
1335
|
for (const checkpoint of await store.awaitingCheckpoints())
|
|
1357
1336
|
deliver(pi, `Workflow ${loaded.snapshot.metadata.name} checkpoint ${checkpoint.name}: ${checkpoint.prompt}\nContext: ${JSON.stringify(checkpoint.context)}\nRespond with workflow_respond.`);
|
|
1358
1337
|
for (const decision of await store.pendingWorkflowDecisions())
|
|
@@ -1391,15 +1370,6 @@ export default function workflowExtension(pi, home, clipboard = copyToClipboard,
|
|
|
1391
1370
|
throw error;
|
|
1392
1371
|
}
|
|
1393
1372
|
});
|
|
1394
|
-
pi.on("before_agent_start", (event, ctx) => {
|
|
1395
|
-
if (!pi.getActiveTools().includes("workflow"))
|
|
1396
|
-
return;
|
|
1397
|
-
const roles = Object.entries(loadAgentDefinitions(ctx.cwd, extensionAgentDir, projectTrusted(ctx), typeof registry.roleDirectoryRegistrations === "function" ? registry.roleDirectoryRegistrations() : undefined)).filter(([, definition]) => definition.description);
|
|
1398
|
-
if (!roles.length)
|
|
1399
|
-
return;
|
|
1400
|
-
const content = `Workflow role descriptions:\n${roles.map(([name, definition]) => `- \`${name}\`: ${String(definition.description)}`).join("\n")}`;
|
|
1401
|
-
return { systemPrompt: `${event.systemPrompt}\n\n${content}` };
|
|
1402
|
-
});
|
|
1403
1373
|
const workflowTool = {
|
|
1404
1374
|
name: "workflow",
|
|
1405
1375
|
label: WORKFLOW_TOOL_LABEL,
|
|
@@ -1448,25 +1418,33 @@ export default function workflowExtension(pi, home, clipboard = copyToClipboard,
|
|
|
1448
1418
|
if (trustedProject && launch.resolution.project.extensionSettings !== undefined)
|
|
1449
1419
|
registry.validateExtensionSettings(launch.resolution.project.extensionSettings, { source: "project", cwd: ctx.cwd, projectTrusted: true, settingsPath: launch.resolution.projectSettingsPath });
|
|
1450
1420
|
const validated = validateWorkflowLaunchWithRegistry(params, { cwd: ctx.cwd, agentDir: extensionAgentDir, projectTrusted: trustedProject, availableModels, rootTools: new Set(rootTools), modelAliases, knownModels, settingsPath, ...(settings.extensionSettings === undefined ? {} : { extensionSettings: settings.extensionSettings }) }, registry);
|
|
1451
|
-
const { script, checked
|
|
1421
|
+
const { script, checked } = validated;
|
|
1452
1422
|
const toolContext = scriptToolContext(ctx);
|
|
1453
1423
|
validateScriptToolReferences(script, toolContext);
|
|
1424
|
+
// Headless hosts that tolerated extension load failures report them with the extensions they loaded.
|
|
1425
|
+
const strings = (value) => Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
|
|
1426
|
+
const loadErrors = object(ctx) ? strings(ctx.extensionLoadErrors) : [];
|
|
1427
|
+
if (loadErrors.length)
|
|
1428
|
+
extensionLoad = Object.freeze({ errors: loadErrors, loaded: object(ctx) ? strings(ctx.loadedExtensionPaths) : [] });
|
|
1429
|
+
const executorRoot = { cwd: ctx.cwd, projectTrusted: launch.resourcePolicy.projectTrusted, model: rootModel, tools: new Set(rootTools), resourceSelectors: launch.resourcePolicy.effective, extensionSettings: settings.extensionSettings, availableModels, knownModels, modelAliases, dynamicModelAliasNames: resolvedAliases.dynamicNames, settingsPath, agentResourcePolicy: frozenResourcePolicy(launch.resourcePolicy) };
|
|
1430
|
+
// Runtime emits the same resource warnings, so inspection stays silent.
|
|
1431
|
+
await inspectStaticAgentConfigurations(createAgentExecutor({ ...executorRoot, onResourceWarning: () => undefined }), script, checked.metadata.name, runController.signal, registry.functions());
|
|
1454
1432
|
await ensureSessionLease(ctx.cwd, ctx.sessionManager.getSessionId());
|
|
1433
|
+
// Cancellation can arrive while the lease or a queued parent validation is pending; no run may be created after it.
|
|
1434
|
+
const assertLaunchActive = () => { if (runController.signal.aborted)
|
|
1435
|
+
throw new WorkflowError("CANCELLED", "Workflow launch cancelled"); };
|
|
1436
|
+
assertLaunchActive();
|
|
1455
1437
|
const runId = randomUUID();
|
|
1456
1438
|
const args = params.args ?? null;
|
|
1457
1439
|
encoded(args);
|
|
1458
1440
|
const runContext = workflowRunContext(ctx.cwd, ctx.sessionManager.getSessionId(), runId, checked.metadata, args, runController.signal);
|
|
1459
1441
|
const store = new RunStore(ctx.cwd, ctx.sessionManager.getSessionId(), runId, home);
|
|
1460
1442
|
const parentRunId = params.parentRunId;
|
|
1461
|
-
const
|
|
1462
|
-
const
|
|
1463
|
-
const roleModels = roleNames.flatMap((role) => { const model = agentDefinitions[role]?.model; return model ? [modelCapability(model, modelAliases, knownModels, settingsPath)] : []; });
|
|
1464
|
-
const snapshotModels = [...new Set([rootModelName, ...checked.referenced.models, ...roleModels])];
|
|
1465
|
-
const snapshot = createLaunchSnapshot({ script, args, metadata: checked.metadata, launchMode: params.foreground ? "foreground" : "background", settings, settingsPath, settingsSources: { ...launch.resolution.sources, concurrency: params.concurrency === undefined ? launch.resolution.sources.concurrency : "per-run options" }, ...(Object.keys(modelAliases).length ? { modelAliases } : {}), ...(budget ? { budget } : {}), ...(checked.referenced.phases.length ? { phases: checked.referenced.phases } : {}), models: snapshotModels, tools: rootTools, agentTypes: checked.referenced.agentTypes, roles, projectRoles, schemas: checked.schemas });
|
|
1466
|
-
const capturedRoles = roleCapture(store, snapshot, agentDefinitions, projectAgentDefinitions);
|
|
1443
|
+
const snapshotModels = [...new Set([rootModelName, ...checked.referenced.models])];
|
|
1444
|
+
const snapshot = createLaunchSnapshot({ script, args, metadata: checked.metadata, launchMode: params.foreground ? "foreground" : "background", settings, settingsPath, settingsSources: { ...launch.resolution.sources, concurrency: params.concurrency === undefined ? launch.resolution.sources.concurrency : "per-run options" }, ...(Object.keys(modelAliases).length ? { modelAliases } : {}), ...(budget ? { budget } : {}), ...(checked.referenced.phases.length ? { phases: checked.referenced.phases } : {}), models: snapshotModels, tools: rootTools, agentConfigurations: {}, schemas: checked.schemas });
|
|
1467
1445
|
const budgetRuntime = new WorkflowBudgetRuntime(budget);
|
|
1468
1446
|
const initialBudget = budgetRuntime.snapshot();
|
|
1469
|
-
const createRun = () => store.create({ id: runId, workflowName: checked.metadata.name, cwd: ctx.cwd, sessionId: ctx.sessionManager.getSessionId(), state: "running", ...(parentRunId !== undefined ? { parentRunId } : {}), agents: [], agentSessions: [], delivery: params.foreground ? { mode: "foreground", state: "attached", toolCallId } : { mode: "background", state: "pending" }, ...(budget ? { budget } : {}), budgetVersion: 1, ...initialBudget }, snapshot);
|
|
1447
|
+
const createRun = () => { assertLaunchActive(); return store.create({ id: runId, workflowName: checked.metadata.name, cwd: ctx.cwd, sessionId: ctx.sessionManager.getSessionId(), state: "running", ...(parentRunId !== undefined ? { parentRunId } : {}), agents: [], agentSessions: [], delivery: params.foreground ? { mode: "foreground", state: "attached", toolCallId } : { mode: "background", state: "pending" }, ...(budget ? { budget } : {}), budgetVersion: 1, ...initialBudget }, snapshot); };
|
|
1470
1448
|
// Parent validation and persistence share the mutation lane so a manual deletion cannot remove the parent between the two.
|
|
1471
1449
|
if (parentRunId !== undefined)
|
|
1472
1450
|
await coordinateRunMutation(async () => { await store.validateParentRun(parentRunId); await createRun(); });
|
|
@@ -1494,7 +1472,7 @@ export default function workflowExtension(pi, home, clipboard = copyToClipboard,
|
|
|
1494
1472
|
activeRun.foreground = false;
|
|
1495
1473
|
delete activeRun.update;
|
|
1496
1474
|
}
|
|
1497
|
-
await store.
|
|
1475
|
+
await store.setLaunchMode("background");
|
|
1498
1476
|
for (const checkpoint of await store.awaitingCheckpoints())
|
|
1499
1477
|
deliverBackgroundCheckpoint(checked.metadata.name, runId, checkpoint);
|
|
1500
1478
|
signal?.removeEventListener("abort", onForegroundAbort);
|
|
@@ -1511,13 +1489,13 @@ export default function workflowExtension(pi, home, clipboard = copyToClipboard,
|
|
|
1511
1489
|
const providerPause = async () => { if (!foregroundAttached)
|
|
1512
1490
|
deliver(pi, `Workflow ${checked.metadata.name} paused: provider limit.`); await lifecycle.providerPause(); };
|
|
1513
1491
|
const providerErrorRecovery = createProviderErrorRecovery(ctx, availableModels, () => { runController.abort(); });
|
|
1514
|
-
const executor = createAgentExecutor({
|
|
1492
|
+
const executor = createAgentExecutor({ ...executorRoot, runStore: store, providerPause, runContext });
|
|
1515
1493
|
const runRecord = { executor, store, metadata: checked.metadata, model: rootModel, lifecycle, budget: budgetRuntime, abortController: runController, foreground: foregroundAttached, projectTrusted: () => projectTrusted(ctx), checkpointResolvers: new Map(), ...(providerErrorRecovery ? { providerErrorRecovery } : {}), ...(params.foreground && onUpdate ? { update: onUpdate } : {}) };
|
|
1516
1494
|
runs.set(runId, runRecord);
|
|
1517
1495
|
if (params.foreground && onUpdate)
|
|
1518
1496
|
onUpdate(workflowToolUpdate((await store.load()).run));
|
|
1519
1497
|
scheduler.addRun(runId, settings.concurrency, () => runs.get(runId)?.budget.checkAgentLaunch(), settings.extensionSettings);
|
|
1520
|
-
const execution = runWorkflow(script, args, withWorkflowFunctions({ tool: (identifier, toolArgs, signal, identity) => toolForRun(store, lifecycle, toolContext, identifier, toolArgs, signal, identity), shell: (command, options, signal, identity) => shellForRun(store, checked.metadata, lifecycle, command, options, signal, identity), agent: workflowAgentHandler(store, checked.metadata, lifecycle, executor, ctx.cwd, runId
|
|
1498
|
+
const execution = runWorkflow(script, args, withWorkflowFunctions({ tool: (identifier, toolArgs, signal, identity) => toolForRun(store, lifecycle, toolContext, identifier, toolArgs, signal, identity), shell: (command, options, signal, identity) => shellForRun(store, checked.metadata, lifecycle, command, options, signal, identity), agent: workflowAgentHandler(store, checked.metadata, lifecycle, executor, ctx.cwd, runId), worktree: async (owner) => resolveWorktree(store, checked.metadata, owner), checkpoint: checkpointBridge(runId, store, checked.metadata, () => runs.get(runId)?.foreground ?? foregroundAttached, ctx.hasUI ? ctx.ui : undefined, headless), phase: phaseBridge(store, checked.metadata, lifecycle), log: logBridge(store, lifecycle, checked.metadata.name) }, store, runContext, registry, settings.extensionSettings), runController.signal);
|
|
1521
1499
|
runRecord.execution = execution;
|
|
1522
1500
|
await eventPublisher.runStarted(store, checked.metadata);
|
|
1523
1501
|
const finish = execution.result.then(async (value) => {
|
|
@@ -1813,9 +1791,6 @@ async function handleTurnInput(store, handle, turn) {
|
|
|
1813
1791
|
}
|
|
1814
1792
|
return target;
|
|
1815
1793
|
}
|
|
1816
|
-
function displayAgentName(label, role, model) {
|
|
1817
|
-
return label ?? role ?? model.model;
|
|
1818
|
-
}
|
|
1819
1794
|
function modelSpec(value, fallback) {
|
|
1820
1795
|
try {
|
|
1821
1796
|
const parsed = parseModelReference(value);
|
package/dist/src/index.d.ts
CHANGED
|
@@ -3,10 +3,9 @@ export * from "./package-identity.js";
|
|
|
3
3
|
export { createToolTimingExtension, TOOL_TIMING_ENTRY_TYPE } from "./tool-timing.js";
|
|
4
4
|
export type { ToolTimingEntry } from "./tool-timing.js";
|
|
5
5
|
export * from "./utils.js";
|
|
6
|
-
export { canonicalPath, sameFilesystemPath } from "./paths.js";
|
|
6
|
+
export { canonicalPath, extensionIdentity, sameFilesystemPath } from "./paths.js";
|
|
7
7
|
export * from "./budget.js";
|
|
8
8
|
export * from "./validation.js";
|
|
9
|
-
export * from "./roles.js";
|
|
10
9
|
export * from "./registry.js";
|
|
11
10
|
export * from "./execution.js";
|
|
12
11
|
export * from "./host.js";
|