pi-agent-flow 1.8.1 ā 1.8.3
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 +4 -30
- package/agents/audit.md +1 -2
- package/agents/build.md +1 -0
- package/agents/craft.md +12 -8
- package/agents/debug.md +2 -2
- package/agents/ideas.md +1 -0
- package/agents/scout.md +1 -0
- package/dist/agents.d.ts +41 -0
- package/dist/agents.d.ts.map +1 -0
- package/dist/agents.js +283 -0
- package/dist/agents.js.map +1 -0
- package/dist/batch/batch-bash.d.ts +87 -0
- package/dist/batch/batch-bash.d.ts.map +1 -0
- package/dist/batch/batch-bash.js +369 -0
- package/dist/batch/batch-bash.js.map +1 -0
- package/dist/batch/constants.d.ts +100 -0
- package/dist/batch/constants.d.ts.map +1 -0
- package/dist/batch/constants.js +15 -0
- package/dist/batch/constants.js.map +1 -0
- package/dist/batch/execute.d.ts +21 -0
- package/dist/batch/execute.d.ts.map +1 -0
- package/dist/batch/execute.js +440 -0
- package/dist/batch/execute.js.map +1 -0
- package/dist/batch/fuzzy-edit.d.ts +29 -0
- package/dist/batch/fuzzy-edit.d.ts.map +1 -0
- package/dist/batch/fuzzy-edit.js +257 -0
- package/dist/batch/fuzzy-edit.js.map +1 -0
- package/dist/batch/index.d.ts +85 -0
- package/dist/batch/index.d.ts.map +1 -0
- package/dist/batch/index.js +422 -0
- package/dist/batch/index.js.map +1 -0
- package/dist/batch/render.d.ts +14 -0
- package/dist/batch/render.d.ts.map +1 -0
- package/dist/batch/render.js +74 -0
- package/dist/batch/render.js.map +1 -0
- package/dist/batch/symbols.d.ts +9 -0
- package/dist/batch/symbols.d.ts.map +1 -0
- package/dist/batch/symbols.js +310 -0
- package/dist/batch/symbols.js.map +1 -0
- package/dist/batch.d.ts +12 -0
- package/dist/batch.d.ts.map +1 -0
- package/dist/batch.js +11 -0
- package/dist/batch.js.map +1 -0
- package/dist/cli-args.d.ts +27 -0
- package/dist/cli-args.d.ts.map +1 -0
- package/dist/cli-args.js +265 -0
- package/dist/cli-args.js.map +1 -0
- package/dist/config.d.ts +58 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +296 -0
- package/dist/config.js.map +1 -0
- package/dist/depth.d.ts +25 -0
- package/dist/depth.d.ts.map +1 -0
- package/dist/depth.js +160 -0
- package/dist/depth.js.map +1 -0
- package/dist/executor.d.ts +87 -0
- package/dist/executor.d.ts.map +1 -0
- package/dist/executor.js +295 -0
- package/dist/executor.js.map +1 -0
- package/dist/flow-prompt.d.ts +23 -0
- package/dist/flow-prompt.d.ts.map +1 -0
- package/dist/flow-prompt.js +99 -0
- package/dist/flow-prompt.js.map +1 -0
- package/dist/flow.d.ts +76 -0
- package/dist/flow.d.ts.map +1 -0
- package/dist/flow.js +704 -0
- package/dist/flow.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +327 -0
- package/dist/index.js.map +1 -0
- package/dist/reasoning-strip.d.ts +26 -0
- package/dist/reasoning-strip.d.ts.map +1 -0
- package/dist/reasoning-strip.js +58 -0
- package/dist/reasoning-strip.js.map +1 -0
- package/dist/render-utils.d.ts +42 -0
- package/dist/render-utils.d.ts.map +1 -0
- package/dist/render-utils.js +182 -0
- package/dist/render-utils.js.map +1 -0
- package/dist/render.d.ts +24 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +409 -0
- package/dist/render.js.map +1 -0
- package/dist/runner-events.d.ts +59 -0
- package/dist/runner-events.d.ts.map +1 -0
- package/dist/runner-events.js +539 -0
- package/dist/runner-events.js.map +1 -0
- package/dist/session-mode.d.ts +10 -0
- package/dist/session-mode.d.ts.map +1 -0
- package/dist/session-mode.js +25 -0
- package/dist/session-mode.js.map +1 -0
- package/dist/settings-resolver.d.ts +28 -0
- package/dist/settings-resolver.d.ts.map +1 -0
- package/dist/settings-resolver.js +148 -0
- package/dist/settings-resolver.js.map +1 -0
- package/dist/sliding-prompt.d.ts +40 -0
- package/dist/sliding-prompt.d.ts.map +1 -0
- package/dist/sliding-prompt.js +121 -0
- package/dist/sliding-prompt.js.map +1 -0
- package/dist/snapshot.d.ts +29 -0
- package/dist/snapshot.d.ts.map +1 -0
- package/dist/snapshot.js +199 -0
- package/dist/snapshot.js.map +1 -0
- package/dist/structured-output.d.ts +36 -0
- package/dist/structured-output.d.ts.map +1 -0
- package/dist/structured-output.js +244 -0
- package/dist/structured-output.js.map +1 -0
- package/dist/timed-bash.d.ts +45 -0
- package/dist/timed-bash.d.ts.map +1 -0
- package/dist/timed-bash.js +219 -0
- package/dist/timed-bash.js.map +1 -0
- package/dist/tool-utils.d.ts +20 -0
- package/dist/tool-utils.d.ts.map +1 -0
- package/dist/tool-utils.js +38 -0
- package/dist/tool-utils.js.map +1 -0
- package/dist/transitions.d.ts +39 -0
- package/dist/transitions.d.ts.map +1 -0
- package/dist/transitions.js +59 -0
- package/dist/transitions.js.map +1 -0
- package/dist/types.d.ts +207 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +143 -0
- package/dist/types.js.map +1 -0
- package/dist/web-tool.d.ts +35 -0
- package/dist/web-tool.d.ts.map +1 -0
- package/dist/web-tool.js +545 -0
- package/dist/web-tool.js.map +1 -0
- package/package.json +7 -5
- package/src/agents.ts +0 -299
- package/src/ambient.d.ts +0 -107
- package/src/batch/batch-bash.ts +0 -443
- package/src/batch/constants.ts +0 -128
- package/src/batch/execute.ts +0 -551
- package/src/batch/fuzzy-edit.ts +0 -323
- package/src/batch/index.ts +0 -494
- package/src/batch/render.ts +0 -81
- package/src/batch/symbols.ts +0 -341
- package/src/batch.ts +0 -28
- package/src/cli-args.ts +0 -315
- package/src/config.ts +0 -391
- package/src/executor.ts +0 -445
- package/src/flow.ts +0 -834
- package/src/hooks.ts +0 -294
- package/src/index.ts +0 -1132
- package/src/render-utils.ts +0 -205
- package/src/render.ts +0 -524
- package/src/runner-events.ts +0 -692
- package/src/session-mode.ts +0 -33
- package/src/sliding-prompt.ts +0 -144
- package/src/structured-output.ts +0 -195
- package/src/timed-bash.ts +0 -270
- package/src/transitions.ts +0 -86
- package/src/types.ts +0 -386
- package/src/web-tool.ts +0 -663
package/src/executor.ts
DELETED
|
@@ -1,445 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* FlowExecutor ā extracted from index.ts for testability.
|
|
3
|
-
*
|
|
4
|
-
* Encapsulates the orchestration logic for running flows: cycle detection,
|
|
5
|
-
* project-flow confirmation, parallel execution with failover, caching,
|
|
6
|
-
* hook invocation, auto-transition, and telemetry.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import type { FlowConfig } from "./agents.js";
|
|
10
|
-
import type {
|
|
11
|
-
SingleResult,
|
|
12
|
-
FlowDetails,
|
|
13
|
-
CompressedFlowResult,
|
|
14
|
-
FlowMetrics,
|
|
15
|
-
} from "./types.js";
|
|
16
|
-
import { isFlowSuccess, isFlowError, getFlowOutput, emptyFlowUsage } from "./types.js";
|
|
17
|
-
import { extractStructuredOutput } from "./structured-output.js";
|
|
18
|
-
import { runHooksDetailed, type RunHooksResult } from "./hooks.js";
|
|
19
|
-
import { mapFlowConcurrent, runFlow } from "./flow.js";
|
|
20
|
-
import { getFlowSummaryText } from "./runner-events.js";
|
|
21
|
-
import { normalizeFlowModeName, resolveFlowModelCandidates, selectFlowModelStrategy, type LoadedFlowModelConfigs, type FlowModelStrategy } from "./config.js";
|
|
22
|
-
import { getAgentSessionTimeoutMs, resolveAgentSessionMode, type AgentSessionMode } from "./session-mode.js";
|
|
23
|
-
|
|
24
|
-
// ---------------------------------------------------------------------------
|
|
25
|
-
// Types
|
|
26
|
-
// ---------------------------------------------------------------------------
|
|
27
|
-
|
|
28
|
-
export interface FlowExecutorDeps {
|
|
29
|
-
/** All discovered flow configs. */
|
|
30
|
-
flows: FlowConfig[];
|
|
31
|
-
/** Current delegation depth. */
|
|
32
|
-
currentDepth: number;
|
|
33
|
-
/** Maximum delegation depth. */
|
|
34
|
-
maxDepth: number;
|
|
35
|
-
/** Ancestor flow stack (names). */
|
|
36
|
-
ancestorFlowStack: string[];
|
|
37
|
-
/** Whether cycle prevention is enabled. */
|
|
38
|
-
preventCycles: boolean;
|
|
39
|
-
/** Whether to use optimized tool list. */
|
|
40
|
-
toolOptimize: boolean;
|
|
41
|
-
/** Whether to inject structured output instructions. */
|
|
42
|
-
structuredOutput: boolean;
|
|
43
|
-
/** Working directory. */
|
|
44
|
-
cwd: string;
|
|
45
|
-
/** Loaded flow model configs. */
|
|
46
|
-
loadedFlowModelConfigs: LoadedFlowModelConfigs;
|
|
47
|
-
/** Max concurrency for parallel flow execution. */
|
|
48
|
-
maxConcurrency: number;
|
|
49
|
-
/** Whether auto-transition is enabled. */
|
|
50
|
-
autoTransition: boolean;
|
|
51
|
-
/** Default child-flow session mode. */
|
|
52
|
-
defaultSessionMode: AgentSessionMode;
|
|
53
|
-
/** Abort signal. */
|
|
54
|
-
signal?: AbortSignal;
|
|
55
|
-
/** Streaming update callback. */
|
|
56
|
-
onUpdate?: (result: import("@mariozechner/pi-agent-core").AgentToolResult<FlowDetails>) => void;
|
|
57
|
-
/** Factory to wrap results into FlowDetails. */
|
|
58
|
-
makeDetails: (results: SingleResult[]) => FlowDetails;
|
|
59
|
-
/** Get a CLI flag value. */
|
|
60
|
-
getFlag: (name: string) => unknown;
|
|
61
|
-
/** Inherited CLI args for tier overrides. */
|
|
62
|
-
tierOverrideResolver: (tier: "lite" | "flash" | "full") => string | undefined;
|
|
63
|
-
/** Inherited fallback model. */
|
|
64
|
-
fallbackModel?: string;
|
|
65
|
-
/** Fork session snapshot JSONL. */
|
|
66
|
-
forkSessionSnapshotJsonl: string | null;
|
|
67
|
-
/** Flow result cache for compression. */
|
|
68
|
-
flowResultCache: Map<string, CompressedFlowResult[]>;
|
|
69
|
-
/** Project flows directory. */
|
|
70
|
-
projectFlowsDir: string | null;
|
|
71
|
-
/** Session manager for fork snapshot. */
|
|
72
|
-
sessionManager: { getHeader: () => unknown; getBranch: () => unknown[] };
|
|
73
|
-
/** Whether UI is available for confirmation. */
|
|
74
|
-
hasUI: boolean;
|
|
75
|
-
/** UI confirmation callback. */
|
|
76
|
-
uiConfirm: (title: string, body: string) => Promise<boolean>;
|
|
77
|
-
/** Telemetry callback. */
|
|
78
|
-
onFlowMetrics?: (metrics: FlowMetrics) => void;
|
|
79
|
-
/** Whether to prompt the user before running project-local flows. Default: true. */
|
|
80
|
-
confirmProjectFlows?: boolean;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export interface ExecuteFlowParams {
|
|
84
|
-
type: string;
|
|
85
|
-
intent: string;
|
|
86
|
-
aim: string;
|
|
87
|
-
cwd?: string;
|
|
88
|
-
sessionMode?: AgentSessionMode;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export interface ExecuteFlowResult {
|
|
92
|
-
content: Array<{ type: string; text: string }>;
|
|
93
|
-
details: FlowDetails;
|
|
94
|
-
isError?: boolean;
|
|
95
|
-
/** Auto-queued transitions for the caller to execute. */
|
|
96
|
-
autoTransitions?: Array<{ type: string; intent: string }>;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// ---------------------------------------------------------------------------
|
|
100
|
-
// Helpers
|
|
101
|
-
// ---------------------------------------------------------------------------
|
|
102
|
-
|
|
103
|
-
function getFlowCycleViolations(
|
|
104
|
-
requestedNames: Set<string>,
|
|
105
|
-
ancestorFlowStack: string[],
|
|
106
|
-
): string[] {
|
|
107
|
-
if (requestedNames.size === 0 || ancestorFlowStack.length === 0) return [];
|
|
108
|
-
const stackSet = new Set(ancestorFlowStack);
|
|
109
|
-
return Array.from(requestedNames).filter((name) => stackSet.has(name));
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function getRequestedProjectFlows(
|
|
113
|
-
flows: FlowConfig[],
|
|
114
|
-
requestedNames: Set<string>,
|
|
115
|
-
): FlowConfig[] {
|
|
116
|
-
return Array.from(requestedNames)
|
|
117
|
-
.map((name) => flows.find((f) => f.name === name.toLowerCase()))
|
|
118
|
-
.filter((f): f is FlowConfig => f?.source === "project");
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
async function confirmProjectFlowsIfNeeded(
|
|
122
|
-
projectFlows: FlowConfig[],
|
|
123
|
-
projectFlowsDir: string | null,
|
|
124
|
-
hasUI: boolean,
|
|
125
|
-
uiConfirm: (title: string, body: string) => Promise<boolean>,
|
|
126
|
-
): Promise<{ ok: boolean; blocked?: string }> {
|
|
127
|
-
if (projectFlows.length === 0) return { ok: true };
|
|
128
|
-
|
|
129
|
-
const names = projectFlows.map((f) => f.name).join(", ");
|
|
130
|
-
const dir = projectFlowsDir ?? "(unknown)";
|
|
131
|
-
|
|
132
|
-
if (hasUI) {
|
|
133
|
-
const ok = await uiConfirm(
|
|
134
|
-
"Run project-local flows?",
|
|
135
|
-
`Flows: ${names}\nSource: ${dir}\n\nProject flows are repo-controlled. Only continue for trusted repositories.`,
|
|
136
|
-
);
|
|
137
|
-
return { ok };
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
return {
|
|
141
|
-
ok: false,
|
|
142
|
-
blocked: `Blocked: project-local flow confirmation required in non-UI mode.\nFlows: ${names}\nRe-run with confirmProjectFlows: false if trusted.`,
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// ---------------------------------------------------------------------------
|
|
147
|
-
// Cache limits
|
|
148
|
-
// ---------------------------------------------------------------------------
|
|
149
|
-
|
|
150
|
-
const FLOW_RESULT_CACHE_MAX_ENTRIES = 50;
|
|
151
|
-
|
|
152
|
-
/** Evict oldest entries from the cache when it exceeds the cap. */
|
|
153
|
-
function evictCacheOverflow(cache: Map<string, unknown>): void {
|
|
154
|
-
if (cache.size <= FLOW_RESULT_CACHE_MAX_ENTRIES) return;
|
|
155
|
-
const excess = cache.size - FLOW_RESULT_CACHE_MAX_ENTRIES;
|
|
156
|
-
const keys = cache.keys();
|
|
157
|
-
for (let i = 0; i < excess; i++) {
|
|
158
|
-
const next = keys.next();
|
|
159
|
-
if (next.done) break;
|
|
160
|
-
cache.delete(next.value);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function shouldFailover(result: SingleResult): boolean {
|
|
165
|
-
if (result.stopReason === "aborted") return false;
|
|
166
|
-
const text = `${result.errorMessage ?? ""}\n${result.stderr ?? ""}`.toLowerCase();
|
|
167
|
-
if (!text.trim()) return false;
|
|
168
|
-
if (text.includes("permission") || text.includes("invalid tool") || text.includes("bad settings")) {
|
|
169
|
-
return false;
|
|
170
|
-
}
|
|
171
|
-
return result.exitCode > 0;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// ---------------------------------------------------------------------------
|
|
175
|
-
// FlowExecutor
|
|
176
|
-
// ---------------------------------------------------------------------------
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Execute a set of flow tasks with full orchestration: cycle detection,
|
|
180
|
-
* project confirmation, parallel execution with model failover, hook
|
|
181
|
-
* invocation, auto-transition, and telemetry.
|
|
182
|
-
*/
|
|
183
|
-
export async function executeFlows(
|
|
184
|
-
deps: FlowExecutorDeps,
|
|
185
|
-
params: ExecuteFlowParams[],
|
|
186
|
-
toolCallId: string,
|
|
187
|
-
): Promise<ExecuteFlowResult> {
|
|
188
|
-
const {
|
|
189
|
-
flows, currentDepth, maxDepth, ancestorFlowStack, preventCycles,
|
|
190
|
-
toolOptimize, structuredOutput, cwd, loadedFlowModelConfigs,
|
|
191
|
-
maxConcurrency, autoTransition, defaultSessionMode, signal, onUpdate, makeDetails,
|
|
192
|
-
getFlag, tierOverrideResolver, fallbackModel, forkSessionSnapshotJsonl,
|
|
193
|
-
flowResultCache, projectFlowsDir, hasUI, uiConfirm, onFlowMetrics,
|
|
194
|
-
confirmProjectFlows,
|
|
195
|
-
} = deps;
|
|
196
|
-
|
|
197
|
-
const requested = new Set<string>(params.map((f) => f.type.toLowerCase()));
|
|
198
|
-
|
|
199
|
-
// Cycle check
|
|
200
|
-
if (preventCycles) {
|
|
201
|
-
const violations = getFlowCycleViolations(requested, ancestorFlowStack);
|
|
202
|
-
if (violations.length > 0) {
|
|
203
|
-
const stack = ancestorFlowStack.join(" -> ") || "(root)";
|
|
204
|
-
return {
|
|
205
|
-
content: [{
|
|
206
|
-
type: "text",
|
|
207
|
-
text: `Blocked: cycle detected. Flow(s) in stack: ${violations.join(", ")}\nStack: ${stack}`,
|
|
208
|
-
}],
|
|
209
|
-
details: makeDetails([]),
|
|
210
|
-
isError: true,
|
|
211
|
-
};
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Project flow confirmation
|
|
216
|
-
const projectFlows = getRequestedProjectFlows(flows, requested);
|
|
217
|
-
if (projectFlows.length > 0 && confirmProjectFlows !== false) {
|
|
218
|
-
const { ok, blocked } = await confirmProjectFlowsIfNeeded(projectFlows, projectFlowsDir, hasUI, uiConfirm);
|
|
219
|
-
if (!ok) {
|
|
220
|
-
return {
|
|
221
|
-
content: [{ type: "text", text: blocked ?? "Canceled: project-local flows not approved." }],
|
|
222
|
-
details: makeDetails([]),
|
|
223
|
-
isError: !blocked,
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// Resolve model strategy
|
|
229
|
-
const cliFlowMode = normalizeFlowModeName(getFlag("flow-mode"));
|
|
230
|
-
const cliFlowModelConfig = normalizeFlowModeName(getFlag("flow-model-config"));
|
|
231
|
-
if (cliFlowMode !== undefined && cliFlowModelConfig !== undefined && cliFlowMode !== cliFlowModelConfig) {
|
|
232
|
-
console.warn(
|
|
233
|
-
`[pi-agent-flow] Both --flow-mode "${cliFlowMode}" and --flow-model-config "${cliFlowModelConfig}" were provided. Using --flow-mode.`,
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
const selectedFlowModelConfig = selectFlowModelStrategy(
|
|
237
|
-
loadedFlowModelConfigs.configs,
|
|
238
|
-
cliFlowMode ?? cliFlowModelConfig ?? loadedFlowModelConfigs.selectedName,
|
|
239
|
-
);
|
|
240
|
-
|
|
241
|
-
// Pre-allocate results array
|
|
242
|
-
const allResults: SingleResult[] = new Array(params.length);
|
|
243
|
-
for (let i = 0; i < params.length; i++) {
|
|
244
|
-
allResults[i] = {
|
|
245
|
-
type: params[i].type,
|
|
246
|
-
agentSource: "unknown",
|
|
247
|
-
intent: params[i].intent,
|
|
248
|
-
aim: params[i].aim,
|
|
249
|
-
exitCode: -1,
|
|
250
|
-
messages: [],
|
|
251
|
-
stderr: "",
|
|
252
|
-
usage: emptyFlowUsage(),
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Streaming progress
|
|
257
|
-
let lastStreamingText = "";
|
|
258
|
-
let lastEmittedSignature: string | undefined;
|
|
259
|
-
const emitProgress = (streamingText?: string) => {
|
|
260
|
-
if (!onUpdate) return;
|
|
261
|
-
if (streamingText !== undefined) lastStreamingText = streamingText;
|
|
262
|
-
const text = lastStreamingText || "";
|
|
263
|
-
const signature =
|
|
264
|
-
text +
|
|
265
|
-
"|" +
|
|
266
|
-
allResults
|
|
267
|
-
.map((r) => {
|
|
268
|
-
const remainingSeconds = r.exitCode === -1 && typeof r.deadlineAtMs === "number"
|
|
269
|
-
? Math.max(0, Math.ceil((r.deadlineAtMs - Date.now()) / 1000))
|
|
270
|
-
: "";
|
|
271
|
-
return `${r.messages.length}:${r.usage.toolCalls}:${r.usage.input}:${r.usage.output}:${r.usage.contextTokens}:${r.usage.smoothedTps ?? 0}:${r.startedAtMs ?? ""}:${r.deadlineAtMs ?? ""}:${remainingSeconds}:${r.errorMessage ?? ""}`;
|
|
272
|
-
})
|
|
273
|
-
.join(";");
|
|
274
|
-
if (signature === lastEmittedSignature) return;
|
|
275
|
-
lastEmittedSignature = signature;
|
|
276
|
-
onUpdate({
|
|
277
|
-
content: [{ type: "text", text }],
|
|
278
|
-
details: makeDetails([...allResults]),
|
|
279
|
-
});
|
|
280
|
-
};
|
|
281
|
-
|
|
282
|
-
if (onUpdate) emitProgress();
|
|
283
|
-
|
|
284
|
-
// Execute all flows in parallel
|
|
285
|
-
const executionStart = Date.now();
|
|
286
|
-
const results = await mapFlowConcurrent(params, maxConcurrency, async (item, index) => {
|
|
287
|
-
const normalizedType = item.type.toLowerCase();
|
|
288
|
-
const sessionMode = resolveAgentSessionMode(item.sessionMode, defaultSessionMode);
|
|
289
|
-
const targetFlow = flows.find((f) => f.name === normalizedType);
|
|
290
|
-
const effectiveMaxDepth =
|
|
291
|
-
targetFlow?.maxDepth !== undefined ? targetFlow.maxDepth : maxDepth;
|
|
292
|
-
|
|
293
|
-
const shouldInheritContext = targetFlow?.inheritContext !== false;
|
|
294
|
-
const tier = targetFlow?.tier ?? "flash";
|
|
295
|
-
const { candidates } = resolveFlowModelCandidates({
|
|
296
|
-
tier,
|
|
297
|
-
flowModel: targetFlow?.model,
|
|
298
|
-
cliTierOverride: tierOverrideResolver(tier),
|
|
299
|
-
strategy: selectedFlowModelConfig.strategy,
|
|
300
|
-
fallbackModel,
|
|
301
|
-
});
|
|
302
|
-
const attemptModels = candidates.length > 0 ? candidates : [undefined];
|
|
303
|
-
const attemptedModels: string[] = [];
|
|
304
|
-
let result = allResults[index];
|
|
305
|
-
const flowStart = Date.now();
|
|
306
|
-
|
|
307
|
-
for (let attempt = 0; attempt < attemptModels.length; attempt++) {
|
|
308
|
-
const candidateModel = attemptModels[attempt];
|
|
309
|
-
if (candidateModel) attemptedModels.push(candidateModel);
|
|
310
|
-
const attemptStartMs = Date.now();
|
|
311
|
-
const attemptTimeoutMs = getAgentSessionTimeoutMs(sessionMode);
|
|
312
|
-
allResults[index] = {
|
|
313
|
-
type: normalizedType,
|
|
314
|
-
agentSource: targetFlow?.source ?? "unknown",
|
|
315
|
-
intent: item.intent,
|
|
316
|
-
aim: item.aim,
|
|
317
|
-
exitCode: -1,
|
|
318
|
-
messages: [],
|
|
319
|
-
stderr: "",
|
|
320
|
-
usage: emptyFlowUsage(),
|
|
321
|
-
model: candidateModel,
|
|
322
|
-
startedAtMs: attemptStartMs,
|
|
323
|
-
deadlineAtMs: attemptStartMs + attemptTimeoutMs,
|
|
324
|
-
};
|
|
325
|
-
emitProgress();
|
|
326
|
-
result = await runFlow({
|
|
327
|
-
cwd,
|
|
328
|
-
flows,
|
|
329
|
-
flowName: normalizedType,
|
|
330
|
-
intent: item.intent,
|
|
331
|
-
aim: item.aim,
|
|
332
|
-
taskCwd: item.cwd,
|
|
333
|
-
forkSessionSnapshotJsonl: shouldInheritContext ? forkSessionSnapshotJsonl : null,
|
|
334
|
-
parentDepth: currentDepth,
|
|
335
|
-
parentFlowStack: ancestorFlowStack,
|
|
336
|
-
maxDepth: effectiveMaxDepth,
|
|
337
|
-
preventCycles,
|
|
338
|
-
toolOptimize,
|
|
339
|
-
structuredOutput,
|
|
340
|
-
sessionMode,
|
|
341
|
-
model: candidateModel,
|
|
342
|
-
signal,
|
|
343
|
-
onUpdate: (partial) => {
|
|
344
|
-
if (partial.details?.results[0]) {
|
|
345
|
-
allResults[index] = partial.details.results[0];
|
|
346
|
-
emitProgress(partial.content?.[0]?.text);
|
|
347
|
-
}
|
|
348
|
-
},
|
|
349
|
-
makeDetails,
|
|
350
|
-
});
|
|
351
|
-
allResults[index] = result;
|
|
352
|
-
emitProgress();
|
|
353
|
-
if (isFlowSuccess(result) || signal?.aborted) break;
|
|
354
|
-
if (attempt < attemptModels.length - 1 && shouldFailover(result)) {
|
|
355
|
-
continue;
|
|
356
|
-
}
|
|
357
|
-
break;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
if (result && !isFlowSuccess(result) && attemptedModels.length > 1) {
|
|
361
|
-
const summary = `Model failover attempts: ${attemptedModels.join(" -> ")}`;
|
|
362
|
-
const baseStderr = result.stderr.trim();
|
|
363
|
-
result.stderr = baseStderr ? `${baseStderr}\n\n${summary}` : summary;
|
|
364
|
-
allResults[index] = result;
|
|
365
|
-
emitProgress();
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
// Telemetry for individual flow
|
|
369
|
-
if (onFlowMetrics) {
|
|
370
|
-
const flowDuration = Date.now() - flowStart;
|
|
371
|
-
onFlowMetrics({
|
|
372
|
-
type: normalizedType,
|
|
373
|
-
durationMs: flowDuration,
|
|
374
|
-
exitCode: result.exitCode,
|
|
375
|
-
success: isFlowSuccess(result),
|
|
376
|
-
model: result.model,
|
|
377
|
-
failoverCount: Math.max(0, attemptedModels.length - 1),
|
|
378
|
-
usage: result.usage,
|
|
379
|
-
source: result.agentSource,
|
|
380
|
-
depth: currentDepth + 1,
|
|
381
|
-
});
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
return result;
|
|
385
|
-
});
|
|
386
|
-
|
|
387
|
-
// Cache flow results
|
|
388
|
-
for (const result of results) {
|
|
389
|
-
const so = result.structuredOutput;
|
|
390
|
-
if (!so) continue;
|
|
391
|
-
const compressed: CompressedFlowResult = {
|
|
392
|
-
type: result.type,
|
|
393
|
-
status: isFlowError(result) ? "failed" : "accomplished",
|
|
394
|
-
};
|
|
395
|
-
if (so.files.length > 0) compressed.files = so.files;
|
|
396
|
-
if (so.commands.length > 0) compressed.commands = so.commands;
|
|
397
|
-
if (result.errorMessage) compressed.error = result.errorMessage;
|
|
398
|
-
const existing = flowResultCache.get(toolCallId) ?? [];
|
|
399
|
-
existing.push(compressed);
|
|
400
|
-
flowResultCache.set(toolCallId, existing);
|
|
401
|
-
}
|
|
402
|
-
evictCacheOverflow(flowResultCache);
|
|
403
|
-
|
|
404
|
-
// Build tool result
|
|
405
|
-
const successCount = results.filter((r) => isFlowSuccess(r)).length;
|
|
406
|
-
const flowReports = results.map((r) => {
|
|
407
|
-
const output = getFlowSummaryText(r);
|
|
408
|
-
const status = isFlowError(r) ? "failed" : "accomplished";
|
|
409
|
-
return `flow [${r.type}] ${status}\n\n${output}`;
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
// Post-flow hooks
|
|
413
|
-
const hookResult: RunHooksResult = runHooksDetailed(params, results);
|
|
414
|
-
const advisorBlock = hookResult.advisors.length > 0
|
|
415
|
-
? "\n\n---\n\nš” " + hookResult.advisors.join("\nš” ")
|
|
416
|
-
: "";
|
|
417
|
-
|
|
418
|
-
// Auto-transition: collect qualifying transitions.
|
|
419
|
-
// No confidence threshold gating ā non-deterministic agents should not
|
|
420
|
-
// have unstable numeric params controlling execution flow.
|
|
421
|
-
const queuedTransitions: Array<{ type: string; intent: string }> = [];
|
|
422
|
-
if (autoTransition && hookResult.autoTransitions.length > 0) {
|
|
423
|
-
for (const transition of hookResult.autoTransitions) {
|
|
424
|
-
const normalizedType = transition.type.toLowerCase();
|
|
425
|
-
const flowExists = flows.some((f) => f.name === normalizedType);
|
|
426
|
-
const notAlreadyRequested = !requested.has(normalizedType);
|
|
427
|
-
const noCycles = !preventCycles || !ancestorFlowStack.includes(normalizedType);
|
|
428
|
-
if (flowExists && notAlreadyRequested && noCycles) {
|
|
429
|
-
queuedTransitions.push({
|
|
430
|
-
type: transition.type,
|
|
431
|
-
intent: transition.intent,
|
|
432
|
-
});
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
return {
|
|
438
|
-
content: [{
|
|
439
|
-
type: "text" as const,
|
|
440
|
-
text: `Flow: ${successCount}/${results.length} completed\n\n${flowReports.join("\n\n---\n\n")}${advisorBlock}`,
|
|
441
|
-
}],
|
|
442
|
-
details: makeDetails(results),
|
|
443
|
-
autoTransitions: queuedTransitions.length > 0 ? queuedTransitions : undefined,
|
|
444
|
-
};
|
|
445
|
-
}
|