pi-agent-flow 1.1.0 → 1.2.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 +158 -44
- package/agents/audit.md +19 -24
- package/agents/build.md +28 -49
- package/agents/craft.md +16 -24
- package/agents/debug.md +14 -23
- package/agents/ideas.md +16 -17
- package/agents/scout.md +15 -20
- package/package.json +4 -15
- package/{agents.ts → src/agents.ts} +10 -4
- package/src/ambient.d.ts +85 -0
- package/{batch.ts → src/batch.ts} +687 -121
- package/src/cli-args.ts +283 -0
- package/src/config.ts +310 -0
- package/{flow.ts → src/flow.ts} +93 -21
- package/{hooks.ts → src/hooks.ts} +6 -6
- package/{index.ts → src/index.ts} +522 -103
- package/{render-utils.ts → src/render-utils.ts} +2 -2
- package/{render.ts → src/render.ts} +54 -10
- package/src/runner-events.ts +692 -0
- package/src/structured-output.ts +97 -0
- package/{types.ts → src/types.ts} +100 -0
- package/config.ts +0 -102
- package/runner-cli.js +0 -254
- package/runner-events.js +0 -559
- /package/{web-tool.ts → src/web-tool.ts} +0 -0
package/{flow.ts → src/flow.ts}
RENAMED
|
@@ -10,8 +10,8 @@ import * as fs from "node:fs";
|
|
|
10
10
|
import * as os from "node:os";
|
|
11
11
|
import * as path from "node:path";
|
|
12
12
|
import type { AgentToolResult } from "@mariozechner/pi-agent-core";
|
|
13
|
-
import { type FlowConfig
|
|
14
|
-
import { parseFlowCliArgs } from "./
|
|
13
|
+
import { type FlowConfig } from "./agents.js";
|
|
14
|
+
import { parseFlowCliArgs } from "./cli-args.js";
|
|
15
15
|
import { processFlowJsonLine, drainStreamingText, drainStreamingEstimate, drainCtxEstimate, updateSmoothedTps, drainSmoothedTps } from "./runner-events.js";
|
|
16
16
|
import {
|
|
17
17
|
type SingleResult,
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
getFlowOutput,
|
|
21
21
|
normalizeFlowResult,
|
|
22
22
|
} from "./types.js";
|
|
23
|
+
import { extractStructuredOutput } from "./structured-output.js";
|
|
23
24
|
|
|
24
25
|
const isWindows = process.platform === "win32";
|
|
25
26
|
const SIGKILL_TIMEOUT_MS = 5000;
|
|
@@ -52,8 +53,8 @@ function mergeStreamingUsage(
|
|
|
52
53
|
...actual,
|
|
53
54
|
...(estimatedOutputTokens > 0 ? { output: Math.max(actual.output, estimatedOutputTokens) } : {}),
|
|
54
55
|
...(ctxEstimate > 0 ? { contextTokens: Math.max(actual.contextTokens, ctxEstimate) } : {}),
|
|
55
|
-
//
|
|
56
|
-
...(smoothedTps > 0 ? { smoothedTps
|
|
56
|
+
// Show the live EMA value so the dashboard can rise and fall smoothly.
|
|
57
|
+
...(smoothedTps > 0 ? { smoothedTps } : {}),
|
|
57
58
|
};
|
|
58
59
|
}
|
|
59
60
|
|
|
@@ -117,7 +118,7 @@ export function getOptimizedTools(
|
|
|
117
118
|
);
|
|
118
119
|
if (!hasLegacyTools) return flowTools;
|
|
119
120
|
const filtered = flowTools.filter(
|
|
120
|
-
(t) => t !== "read" && t !== "write" && t !== "edit" && t !== "batch",
|
|
121
|
+
(t) => t !== "read" && t !== "write" && t !== "edit" && t !== "batch" && t !== "batch_read",
|
|
121
122
|
);
|
|
122
123
|
return filtered.includes("batch")
|
|
123
124
|
? filtered
|
|
@@ -128,10 +129,11 @@ function buildFlowArgs(
|
|
|
128
129
|
flow: FlowConfig,
|
|
129
130
|
intent: string,
|
|
130
131
|
forkSessionPath: string | null,
|
|
131
|
-
|
|
132
|
+
model?: string,
|
|
132
133
|
parentDepth: number = 0,
|
|
133
134
|
maxDepth: number = 0,
|
|
134
135
|
toolOptimize: boolean = false,
|
|
136
|
+
structuredOutput: boolean = true,
|
|
135
137
|
): string[] {
|
|
136
138
|
const args: string[] = [
|
|
137
139
|
"--mode",
|
|
@@ -145,10 +147,21 @@ function buildFlowArgs(
|
|
|
145
147
|
args.push("--session", forkSessionPath);
|
|
146
148
|
}
|
|
147
149
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
if (
|
|
150
|
+
if (inheritedCliArgs.flowModelConfig) {
|
|
151
|
+
args.push("--flow-model-config", inheritedCliArgs.flowModelConfig);
|
|
152
|
+
}
|
|
153
|
+
if (inheritedCliArgs.tieredModels?.lite) {
|
|
154
|
+
args.push("--flow-lite-model", inheritedCliArgs.tieredModels.lite);
|
|
155
|
+
}
|
|
156
|
+
if (inheritedCliArgs.tieredModels?.flash) {
|
|
157
|
+
args.push("--flow-flash-model", inheritedCliArgs.tieredModels.flash);
|
|
158
|
+
}
|
|
159
|
+
if (inheritedCliArgs.tieredModels?.full) {
|
|
160
|
+
args.push("--flow-full-model", inheritedCliArgs.tieredModels.full);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const resolvedModel = model ?? flow.model ?? inheritedCliArgs.fallbackModel;
|
|
164
|
+
if (resolvedModel) args.push("--model", resolvedModel);
|
|
152
165
|
|
|
153
166
|
const thinking = flow.thinking ?? inheritedCliArgs.fallbackThinking;
|
|
154
167
|
if (thinking) args.push("--thinking", thinking);
|
|
@@ -199,8 +212,42 @@ function buildFlowArgs(
|
|
|
199
212
|
`</activation>`;
|
|
200
213
|
|
|
201
214
|
// Phase 3: Directive — the flow's system prompt (renamed from <system-directive>)
|
|
202
|
-
|
|
203
|
-
|
|
215
|
+
let directiveBody = flow.systemPrompt.trim();
|
|
216
|
+
|
|
217
|
+
// Append structured output instructions when enabled
|
|
218
|
+
if (structuredOutput && directiveBody) {
|
|
219
|
+
directiveBody +=
|
|
220
|
+
`\n\n## Structured Output\n\n` +
|
|
221
|
+
`End your response with a JSON code block containing:\n` +
|
|
222
|
+
`\n` +
|
|
223
|
+
`\`\`\`json\n` +
|
|
224
|
+
`{\n` +
|
|
225
|
+
` "version": "1.0",\n` +
|
|
226
|
+
` "status": "complete",\n` +
|
|
227
|
+
` "summary": "2-3 sentence summary of what was accomplished",\n` +
|
|
228
|
+
` "files": [\n` +
|
|
229
|
+
` { "path": "relative/path", "role": "read", "description": "why it matters", "snippet": "short excerpt", "ranges": [{ "start": 10, "end": 25, "label": "bug" }] }\n` +
|
|
230
|
+
` ],\n` +
|
|
231
|
+
` "actions": [\n` +
|
|
232
|
+
` { "type": "read", "description": "what was done", "target": "file.ts", "result": "success", "evidence": "output or proof" }\n` +
|
|
233
|
+
` ],\n` +
|
|
234
|
+
` "commands": [\n` +
|
|
235
|
+
` { "command": "npm test", "tool": "bash", "target": ".", "result": "success", "output": "12 passing, 2 failing", "purpose": "Run test suite to verify fix" }\n` +
|
|
236
|
+
` ],\n` +
|
|
237
|
+
` "notDone": [\n` +
|
|
238
|
+
` { "item": "unfinished work", "reason": "why it was not completed", "blocker": "blocking issue if any", "nextStep": "specific follow-up" }\n` +
|
|
239
|
+
` ],\n` +
|
|
240
|
+
` "nextSteps": ["recommended follow-up action"],\n` +
|
|
241
|
+
` "reasoning": ["key hypothesis or inference"],\n` +
|
|
242
|
+
` "notes": ["observation or warning"]\n` +
|
|
243
|
+
`}\n` +
|
|
244
|
+
`\`\`\`\n` +
|
|
245
|
+
`\n` +
|
|
246
|
+
`Only include fields that have data. Omit empty arrays; missing array fields are acceptable. Keep snippets under 300 characters. List at most 10 files, 10 actions, 10 commands, and 10 notDone items. If you cannot produce valid structured output, omit the JSON block entirely.`;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const directive = directiveBody
|
|
250
|
+
? `\n\n<directive>\n${directiveBody}\n</directive>`
|
|
204
251
|
: "";
|
|
205
252
|
|
|
206
253
|
// Phase 4: Mission — the intent wrapped with execution contract
|
|
@@ -245,8 +292,10 @@ export interface RunFlowOptions {
|
|
|
245
292
|
preventCycles: boolean;
|
|
246
293
|
/** Whether to transform tool lists to use batch. */
|
|
247
294
|
toolOptimize?: boolean;
|
|
248
|
-
/**
|
|
249
|
-
|
|
295
|
+
/** Whether to inject structured JSON output instructions. Default: true. */
|
|
296
|
+
structuredOutput?: boolean;
|
|
297
|
+
/** Explicit model to use for this flow execution. */
|
|
298
|
+
model?: string;
|
|
250
299
|
/** Abort signal for cancellation. */
|
|
251
300
|
signal?: AbortSignal;
|
|
252
301
|
/** Streaming update callback. */
|
|
@@ -276,6 +325,8 @@ export async function runFlow(opts: RunFlowOptions): Promise<SingleResult> {
|
|
|
276
325
|
maxDepth,
|
|
277
326
|
preventCycles,
|
|
278
327
|
toolOptimize = false,
|
|
328
|
+
structuredOutput = true,
|
|
329
|
+
model,
|
|
279
330
|
signal,
|
|
280
331
|
onUpdate,
|
|
281
332
|
makeDetails,
|
|
@@ -297,7 +348,7 @@ export async function runFlow(opts: RunFlowOptions): Promise<SingleResult> {
|
|
|
297
348
|
};
|
|
298
349
|
}
|
|
299
350
|
|
|
300
|
-
const resolvedModel = flow.model ?? inheritedCliArgs.fallbackModel;
|
|
351
|
+
const resolvedModel = model ?? flow.model ?? inheritedCliArgs.fallbackModel;
|
|
301
352
|
const result: SingleResult = {
|
|
302
353
|
type: normalizedFlowName,
|
|
303
354
|
agentSource: flow.source,
|
|
@@ -310,21 +361,30 @@ export async function runFlow(opts: RunFlowOptions): Promise<SingleResult> {
|
|
|
310
361
|
model: resolvedModel,
|
|
311
362
|
};
|
|
312
363
|
|
|
364
|
+
let liveStreamingText = "";
|
|
365
|
+
let liveEstimatedOutputTokens = 0;
|
|
366
|
+
let lastActualOutputTokens = result.usage.output;
|
|
313
367
|
const emitUpdate = () => {
|
|
314
|
-
const
|
|
368
|
+
const streamingDelta = drainStreamingText(result);
|
|
369
|
+
if (streamingDelta) liveStreamingText += streamingDelta;
|
|
315
370
|
const estimatedTokens = drainStreamingEstimate(result);
|
|
371
|
+
if (result.usage.output !== lastActualOutputTokens) {
|
|
372
|
+
lastActualOutputTokens = result.usage.output;
|
|
373
|
+
liveEstimatedOutputTokens = result.usage.output;
|
|
374
|
+
}
|
|
375
|
+
liveEstimatedOutputTokens += estimatedTokens;
|
|
316
376
|
const ctxEst = drainCtxEstimate(result);
|
|
317
377
|
updateSmoothedTps(result, estimatedTokens);
|
|
318
378
|
const smoothedTps = drainSmoothedTps(result);
|
|
319
|
-
const mergedUsage = mergeStreamingUsage(result.usage,
|
|
379
|
+
const mergedUsage = mergeStreamingUsage(result.usage, liveEstimatedOutputTokens, ctxEst, smoothedTps);
|
|
320
380
|
onUpdate?.({
|
|
321
381
|
content: [
|
|
322
382
|
{
|
|
323
383
|
type: "text",
|
|
324
|
-
text:
|
|
384
|
+
text: liveStreamingText || getFlowOutput(result.messages) || "(running...)",
|
|
325
385
|
},
|
|
326
386
|
],
|
|
327
|
-
details: makeDetails([{ ...result, usage: mergedUsage }]),
|
|
387
|
+
details: makeDetails([{ ...result, usage: mergedUsage, streamingText: liveStreamingText || undefined }]),
|
|
328
388
|
});
|
|
329
389
|
};
|
|
330
390
|
|
|
@@ -342,10 +402,11 @@ export async function runFlow(opts: RunFlowOptions): Promise<SingleResult> {
|
|
|
342
402
|
flow,
|
|
343
403
|
intent,
|
|
344
404
|
forkSessionTmpPath,
|
|
345
|
-
|
|
405
|
+
model,
|
|
346
406
|
parentDepth,
|
|
347
407
|
maxDepth,
|
|
348
408
|
toolOptimize,
|
|
409
|
+
structuredOutput,
|
|
349
410
|
);
|
|
350
411
|
let wasAborted = false;
|
|
351
412
|
|
|
@@ -511,7 +572,18 @@ export async function runFlow(opts: RunFlowOptions): Promise<SingleResult> {
|
|
|
511
572
|
result.usage.smoothedTps = finalSmoothedTps;
|
|
512
573
|
}
|
|
513
574
|
|
|
514
|
-
|
|
575
|
+
const normalized = normalizeFlowResult(result, wasAborted);
|
|
576
|
+
|
|
577
|
+
// Extract structured JSON output from the final assistant text
|
|
578
|
+
if (structuredOutput) {
|
|
579
|
+
const flowText = getFlowOutput(normalized.messages);
|
|
580
|
+
const extracted = extractStructuredOutput(flowText);
|
|
581
|
+
if (extracted) {
|
|
582
|
+
normalized.structuredOutput = extracted;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
return normalized;
|
|
515
587
|
} finally {
|
|
516
588
|
cleanupFlowTempDir(forkSessionTmpDir);
|
|
517
589
|
}
|
|
@@ -104,19 +104,19 @@ registerHook({
|
|
|
104
104
|
},
|
|
105
105
|
});
|
|
106
106
|
|
|
107
|
-
/** Suggest
|
|
107
|
+
/** Suggest scout flow after a successful audit flow. */
|
|
108
108
|
registerHook({
|
|
109
|
-
name: "pi-agent-flow/audit-to-
|
|
109
|
+
name: "pi-agent-flow/audit-to-scout",
|
|
110
110
|
trigger: { flowTypes: ["audit"], onlyOnSuccess: true },
|
|
111
111
|
action: (ctx) => {
|
|
112
|
-
const
|
|
113
|
-
(p) => p.type.toLowerCase() === "
|
|
112
|
+
const scoutWasRequested = ctx.params.some(
|
|
113
|
+
(p) => p.type.toLowerCase() === "scout",
|
|
114
114
|
);
|
|
115
|
-
if (
|
|
115
|
+
if (scoutWasRequested) return null;
|
|
116
116
|
|
|
117
117
|
return {
|
|
118
118
|
content:
|
|
119
|
-
"Audit complete. Consider running
|
|
119
|
+
"Audit complete. Consider running a [scout] flow to trace the audit findings across the codebase.",
|
|
120
120
|
priority: 15,
|
|
121
121
|
};
|
|
122
122
|
},
|