@quintinshaw/pi-dynamic-workflows 1.9.0 → 1.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +95 -65
- package/dist/agent.d.ts +6 -0
- package/dist/agent.js +16 -0
- package/dist/display.d.ts +2 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/dist/run-persistence.d.ts +2 -0
- package/dist/task-panel.d.ts +14 -5
- package/dist/task-panel.js +35 -27
- package/dist/workflow-editor.d.ts +74 -0
- package/dist/workflow-editor.js +228 -0
- package/dist/workflow-manager.d.ts +38 -3
- package/dist/workflow-manager.js +66 -11
- package/dist/workflow-tool.d.ts +7 -0
- package/dist/workflow-tool.js +65 -67
- package/dist/workflow-ui.d.ts +1 -0
- package/dist/workflow-ui.js +27 -5
- package/dist/workflow.d.ts +9 -0
- package/dist/workflow.js +11 -4
- package/extensions/workflow.ts +12 -0
- package/package.json +16 -3
- package/src/agent.ts +16 -0
- package/src/display.ts +2 -0
- package/src/index.ts +13 -2
- package/src/run-persistence.ts +2 -0
- package/src/task-panel.ts +39 -28
- package/src/workflow-editor.ts +252 -0
- package/src/workflow-manager.ts +91 -14
- package/src/workflow-tool.ts +68 -66
- package/src/workflow-ui.ts +27 -5
- package/src/workflow.ts +27 -5
package/src/workflow-ui.ts
CHANGED
|
@@ -63,6 +63,14 @@ interface AgentRow {
|
|
|
63
63
|
status: string;
|
|
64
64
|
phase?: string;
|
|
65
65
|
tokens?: number;
|
|
66
|
+
model?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Short, human-friendly model label: drop the provider prefix for display. */
|
|
70
|
+
function shortModel(model: string | undefined): string | undefined {
|
|
71
|
+
if (!model) return undefined;
|
|
72
|
+
const slash = model.indexOf("/");
|
|
73
|
+
return slash > 0 ? model.slice(slash + 1) : model;
|
|
66
74
|
}
|
|
67
75
|
|
|
68
76
|
/** Reads run/phase/agent data from the manager, preferring live snapshots. */
|
|
@@ -127,7 +135,7 @@ export class NavigatorModel {
|
|
|
127
135
|
if (!snap) return [];
|
|
128
136
|
return snap.agents
|
|
129
137
|
.filter((a) => (a.phase ?? "(no phase)") === phase)
|
|
130
|
-
.map((a) => ({ id: a.id, label: a.label, status: a.status, phase: a.phase, tokens: a.tokens }));
|
|
138
|
+
.map((a) => ({ id: a.id, label: a.label, status: a.status, phase: a.phase, tokens: a.tokens, model: a.model }));
|
|
131
139
|
}
|
|
132
140
|
|
|
133
141
|
agentDetail(runId: string, agentId: number): WorkflowAgentSnapshot | undefined {
|
|
@@ -150,6 +158,7 @@ function persistedToSnapshot(p: PersistedRunState): WorkflowSnapshot {
|
|
|
150
158
|
resultPreview:
|
|
151
159
|
a.result == null ? undefined : String(typeof a.result === "string" ? a.result : JSON.stringify(a.result)),
|
|
152
160
|
error: a.error,
|
|
161
|
+
model: a.model,
|
|
153
162
|
})),
|
|
154
163
|
agentCount: p.agents.length,
|
|
155
164
|
runningCount: p.agents.filter((a) => a.status === "running").length,
|
|
@@ -293,8 +302,9 @@ export function renderNavigator(
|
|
|
293
302
|
lines.push(theme.bold(`${model.runName(state.runId)} › ${state.phase}`));
|
|
294
303
|
agents.forEach((a, i) => {
|
|
295
304
|
const icon = STATUS_ICON[a.status] ?? "?";
|
|
296
|
-
const
|
|
297
|
-
|
|
305
|
+
const mdl = shortModel(a.model);
|
|
306
|
+
const meta = [mdl, a.tokens ? fmtTokens(a.tokens) : undefined].filter(Boolean).join(" · ");
|
|
307
|
+
lines.push(sel(i, `${icon} ${a.label}${meta ? dim(` ${meta}`) : ""}`));
|
|
298
308
|
});
|
|
299
309
|
} else if (state.kind === "detail" && state.runId && state.agentId != null) {
|
|
300
310
|
const a = model.agentDetail(state.runId, state.agentId);
|
|
@@ -302,6 +312,7 @@ export function renderNavigator(
|
|
|
302
312
|
if (a) {
|
|
303
313
|
const body: string[] = [];
|
|
304
314
|
body.push(dim("Status: ") + (a.status ?? ""));
|
|
315
|
+
if (a.model) body.push(dim("Model: ") + (shortModel(a.model) ?? ""));
|
|
305
316
|
if (a.error) body.push(dim("Error: ") + a.error);
|
|
306
317
|
body.push("", dim("Prompt:"));
|
|
307
318
|
body.push(...wrap(a.prompt ?? "", width));
|
|
@@ -454,9 +465,20 @@ export function openWorkflowNavigator(
|
|
|
454
465
|
if (id) ui.notify(manager.stop(id) ? `Stopped ${id}` : `Cannot stop ${id}`, "info");
|
|
455
466
|
break;
|
|
456
467
|
}
|
|
457
|
-
case "restart":
|
|
458
|
-
|
|
468
|
+
case "restart": {
|
|
469
|
+
// Restart re-runs the whole workflow from scratch as a fresh
|
|
470
|
+
// background run (per-agent restart isn't meaningful — agents are
|
|
471
|
+
// driven by the script). The new run auto-delivers when it finishes.
|
|
472
|
+
const id = state.activeRunId(model);
|
|
473
|
+
const run = id ? manager.listRuns().find((r) => r.runId === id) : undefined;
|
|
474
|
+
if (!run?.script) {
|
|
475
|
+
ui.notify(id ? `Cannot restart ${id} (no script saved)` : "No run selected to restart", "warning");
|
|
476
|
+
break;
|
|
477
|
+
}
|
|
478
|
+
const { runId: newId } = manager.startInBackground(run.script, run.args);
|
|
479
|
+
ui.notify(`Restarted ${run.workflowName || "workflow"} as ${newId}`, "info");
|
|
459
480
|
break;
|
|
481
|
+
}
|
|
460
482
|
case "save": {
|
|
461
483
|
const id = state.activeRunId(model);
|
|
462
484
|
const run = id ? manager.listRuns().find((r) => r.runId === id) : undefined;
|
package/src/workflow.ts
CHANGED
|
@@ -48,6 +48,8 @@ export interface SharedRuntime {
|
|
|
48
48
|
export interface WorkflowRunOptions extends WorkflowAgentOptions {
|
|
49
49
|
args?: unknown;
|
|
50
50
|
agent?: Pick<WorkflowAgent, "run">;
|
|
51
|
+
/** The session's main model (provider/id), shown in /workflows for default agents. */
|
|
52
|
+
mainModel?: string;
|
|
51
53
|
concurrency?: number;
|
|
52
54
|
tokenBudget?: number | null;
|
|
53
55
|
signal?: AbortSignal;
|
|
@@ -72,7 +74,14 @@ export interface WorkflowRunOptions extends WorkflowAgentOptions {
|
|
|
72
74
|
onLog?: (message: string) => void;
|
|
73
75
|
onPhase?: (title: string) => void;
|
|
74
76
|
onAgentStart?: (event: { label: string; phase?: string; prompt: string; model?: string }) => void;
|
|
75
|
-
onAgentEnd?: (event: {
|
|
77
|
+
onAgentEnd?: (event: {
|
|
78
|
+
label: string;
|
|
79
|
+
phase?: string;
|
|
80
|
+
result: unknown;
|
|
81
|
+
tokens?: number;
|
|
82
|
+
worktree?: string;
|
|
83
|
+
model?: string;
|
|
84
|
+
}) => void;
|
|
76
85
|
onTokenUsage?: (usage: { input: number; output: number; total: number; cost: number }) => void;
|
|
77
86
|
}
|
|
78
87
|
|
|
@@ -96,6 +105,12 @@ export interface AgentOptions<TSchemaDef extends TSchema | undefined = TSchema |
|
|
|
96
105
|
label?: string;
|
|
97
106
|
phase?: string;
|
|
98
107
|
schema?: TSchemaDef;
|
|
108
|
+
/**
|
|
109
|
+
* Run this agent on a specific model (`provider/modelId` or a bare `modelId`).
|
|
110
|
+
* The workflow author chooses per-agent models per the routing policy in the
|
|
111
|
+
* tool guidelines (e.g. a lighter model for exploration, the main model for
|
|
112
|
+
* analysis). When omitted, the session's main model is used.
|
|
113
|
+
*/
|
|
99
114
|
model?: string;
|
|
100
115
|
isolation?: "worktree";
|
|
101
116
|
agentType?: string;
|
|
@@ -203,6 +218,10 @@ export async function runWorkflow<T = unknown>(
|
|
|
203
218
|
const requestedLabel = agentOptions.label?.trim();
|
|
204
219
|
// Precedence: explicit agentOptions.model > phase model (meta.phases[].model).
|
|
205
220
|
const modelSpec = agentOptions.model ?? resolveModelForPhase(assignedPhase, routingConfig);
|
|
221
|
+
// For display in /workflows: the model this agent runs on — its explicit/phase
|
|
222
|
+
// spec, else the session's main model. The real resolved id overrides this via
|
|
223
|
+
// onModelResolved once the subagent session is created.
|
|
224
|
+
let displayModel = modelSpec ?? options.mainModel;
|
|
206
225
|
|
|
207
226
|
// Deterministic resume key: assigned at lexical call time, before the limiter,
|
|
208
227
|
// so parallel()/pipeline() fan-out is reproducible for a fixed script.
|
|
@@ -215,8 +234,8 @@ export async function runWorkflow<T = unknown>(
|
|
|
215
234
|
if (cached && cached.hash === callHash) {
|
|
216
235
|
shared.agentCount++;
|
|
217
236
|
const label = requestedLabel || defaultAgentLabel(assignedPhase, shared.agentCount);
|
|
218
|
-
options.onAgentStart?.({ label, phase: assignedPhase, prompt, model:
|
|
219
|
-
options.onAgentEnd?.({ label, phase: assignedPhase, result: cached.result, tokens: 0 });
|
|
237
|
+
options.onAgentStart?.({ label, phase: assignedPhase, prompt, model: displayModel });
|
|
238
|
+
options.onAgentEnd?.({ label, phase: assignedPhase, result: cached.result, tokens: 0, model: displayModel });
|
|
220
239
|
return cached.result;
|
|
221
240
|
}
|
|
222
241
|
|
|
@@ -225,7 +244,7 @@ export async function runWorkflow<T = unknown>(
|
|
|
225
244
|
const label = requestedLabel || defaultAgentLabel(assignedPhase, shared.agentCount);
|
|
226
245
|
const timeout = agentOptions.timeoutMs ?? agentTimeoutMs;
|
|
227
246
|
|
|
228
|
-
options.onAgentStart?.({ label, phase: assignedPhase, prompt, model:
|
|
247
|
+
options.onAgentStart?.({ label, phase: assignedPhase, prompt, model: displayModel });
|
|
229
248
|
|
|
230
249
|
// Optional per-agent worktree isolation (deterministic name -> stable resume keys).
|
|
231
250
|
let worktree: Worktree | undefined;
|
|
@@ -262,6 +281,9 @@ export async function runWorkflow<T = unknown>(
|
|
|
262
281
|
instructions: buildAgentInstructions(assignedPhase, agentOptions),
|
|
263
282
|
model: modelSpec,
|
|
264
283
|
cwd: runCwd,
|
|
284
|
+
onModelResolved: (id: string) => {
|
|
285
|
+
displayModel = id;
|
|
286
|
+
},
|
|
265
287
|
onUsage: (u: AgentUsage) => {
|
|
266
288
|
usage = u;
|
|
267
289
|
},
|
|
@@ -274,7 +296,7 @@ export async function runWorkflow<T = unknown>(
|
|
|
274
296
|
|
|
275
297
|
const tokens = recordTokens(result);
|
|
276
298
|
options.onAgentJournal?.({ index: callIndex, hash: callHash, result });
|
|
277
|
-
options.onAgentEnd?.({ label, phase: assignedPhase, result, tokens, worktree: runCwd });
|
|
299
|
+
options.onAgentEnd?.({ label, phase: assignedPhase, result, tokens, worktree: runCwd, model: displayModel });
|
|
278
300
|
return result;
|
|
279
301
|
} catch (error) {
|
|
280
302
|
if (options.signal?.aborted) throw error;
|