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.
Files changed (154) hide show
  1. package/README.md +4 -30
  2. package/agents/audit.md +1 -2
  3. package/agents/build.md +1 -0
  4. package/agents/craft.md +12 -8
  5. package/agents/debug.md +2 -2
  6. package/agents/ideas.md +1 -0
  7. package/agents/scout.md +1 -0
  8. package/dist/agents.d.ts +41 -0
  9. package/dist/agents.d.ts.map +1 -0
  10. package/dist/agents.js +283 -0
  11. package/dist/agents.js.map +1 -0
  12. package/dist/batch/batch-bash.d.ts +87 -0
  13. package/dist/batch/batch-bash.d.ts.map +1 -0
  14. package/dist/batch/batch-bash.js +369 -0
  15. package/dist/batch/batch-bash.js.map +1 -0
  16. package/dist/batch/constants.d.ts +100 -0
  17. package/dist/batch/constants.d.ts.map +1 -0
  18. package/dist/batch/constants.js +15 -0
  19. package/dist/batch/constants.js.map +1 -0
  20. package/dist/batch/execute.d.ts +21 -0
  21. package/dist/batch/execute.d.ts.map +1 -0
  22. package/dist/batch/execute.js +440 -0
  23. package/dist/batch/execute.js.map +1 -0
  24. package/dist/batch/fuzzy-edit.d.ts +29 -0
  25. package/dist/batch/fuzzy-edit.d.ts.map +1 -0
  26. package/dist/batch/fuzzy-edit.js +257 -0
  27. package/dist/batch/fuzzy-edit.js.map +1 -0
  28. package/dist/batch/index.d.ts +85 -0
  29. package/dist/batch/index.d.ts.map +1 -0
  30. package/dist/batch/index.js +422 -0
  31. package/dist/batch/index.js.map +1 -0
  32. package/dist/batch/render.d.ts +14 -0
  33. package/dist/batch/render.d.ts.map +1 -0
  34. package/dist/batch/render.js +74 -0
  35. package/dist/batch/render.js.map +1 -0
  36. package/dist/batch/symbols.d.ts +9 -0
  37. package/dist/batch/symbols.d.ts.map +1 -0
  38. package/dist/batch/symbols.js +310 -0
  39. package/dist/batch/symbols.js.map +1 -0
  40. package/dist/batch.d.ts +12 -0
  41. package/dist/batch.d.ts.map +1 -0
  42. package/dist/batch.js +11 -0
  43. package/dist/batch.js.map +1 -0
  44. package/dist/cli-args.d.ts +27 -0
  45. package/dist/cli-args.d.ts.map +1 -0
  46. package/dist/cli-args.js +265 -0
  47. package/dist/cli-args.js.map +1 -0
  48. package/dist/config.d.ts +58 -0
  49. package/dist/config.d.ts.map +1 -0
  50. package/dist/config.js +296 -0
  51. package/dist/config.js.map +1 -0
  52. package/dist/depth.d.ts +25 -0
  53. package/dist/depth.d.ts.map +1 -0
  54. package/dist/depth.js +160 -0
  55. package/dist/depth.js.map +1 -0
  56. package/dist/executor.d.ts +87 -0
  57. package/dist/executor.d.ts.map +1 -0
  58. package/dist/executor.js +295 -0
  59. package/dist/executor.js.map +1 -0
  60. package/dist/flow-prompt.d.ts +23 -0
  61. package/dist/flow-prompt.d.ts.map +1 -0
  62. package/dist/flow-prompt.js +99 -0
  63. package/dist/flow-prompt.js.map +1 -0
  64. package/dist/flow.d.ts +76 -0
  65. package/dist/flow.d.ts.map +1 -0
  66. package/dist/flow.js +704 -0
  67. package/dist/flow.js.map +1 -0
  68. package/dist/index.d.ts +10 -0
  69. package/dist/index.d.ts.map +1 -0
  70. package/dist/index.js +327 -0
  71. package/dist/index.js.map +1 -0
  72. package/dist/reasoning-strip.d.ts +26 -0
  73. package/dist/reasoning-strip.d.ts.map +1 -0
  74. package/dist/reasoning-strip.js +58 -0
  75. package/dist/reasoning-strip.js.map +1 -0
  76. package/dist/render-utils.d.ts +42 -0
  77. package/dist/render-utils.d.ts.map +1 -0
  78. package/dist/render-utils.js +182 -0
  79. package/dist/render-utils.js.map +1 -0
  80. package/dist/render.d.ts +24 -0
  81. package/dist/render.d.ts.map +1 -0
  82. package/dist/render.js +409 -0
  83. package/dist/render.js.map +1 -0
  84. package/dist/runner-events.d.ts +59 -0
  85. package/dist/runner-events.d.ts.map +1 -0
  86. package/dist/runner-events.js +539 -0
  87. package/dist/runner-events.js.map +1 -0
  88. package/dist/session-mode.d.ts +10 -0
  89. package/dist/session-mode.d.ts.map +1 -0
  90. package/dist/session-mode.js +25 -0
  91. package/dist/session-mode.js.map +1 -0
  92. package/dist/settings-resolver.d.ts +28 -0
  93. package/dist/settings-resolver.d.ts.map +1 -0
  94. package/dist/settings-resolver.js +148 -0
  95. package/dist/settings-resolver.js.map +1 -0
  96. package/dist/sliding-prompt.d.ts +40 -0
  97. package/dist/sliding-prompt.d.ts.map +1 -0
  98. package/dist/sliding-prompt.js +121 -0
  99. package/dist/sliding-prompt.js.map +1 -0
  100. package/dist/snapshot.d.ts +29 -0
  101. package/dist/snapshot.d.ts.map +1 -0
  102. package/dist/snapshot.js +199 -0
  103. package/dist/snapshot.js.map +1 -0
  104. package/dist/structured-output.d.ts +36 -0
  105. package/dist/structured-output.d.ts.map +1 -0
  106. package/dist/structured-output.js +244 -0
  107. package/dist/structured-output.js.map +1 -0
  108. package/dist/timed-bash.d.ts +45 -0
  109. package/dist/timed-bash.d.ts.map +1 -0
  110. package/dist/timed-bash.js +219 -0
  111. package/dist/timed-bash.js.map +1 -0
  112. package/dist/tool-utils.d.ts +20 -0
  113. package/dist/tool-utils.d.ts.map +1 -0
  114. package/dist/tool-utils.js +38 -0
  115. package/dist/tool-utils.js.map +1 -0
  116. package/dist/transitions.d.ts +39 -0
  117. package/dist/transitions.d.ts.map +1 -0
  118. package/dist/transitions.js +59 -0
  119. package/dist/transitions.js.map +1 -0
  120. package/dist/types.d.ts +207 -0
  121. package/dist/types.d.ts.map +1 -0
  122. package/dist/types.js +143 -0
  123. package/dist/types.js.map +1 -0
  124. package/dist/web-tool.d.ts +35 -0
  125. package/dist/web-tool.d.ts.map +1 -0
  126. package/dist/web-tool.js +545 -0
  127. package/dist/web-tool.js.map +1 -0
  128. package/package.json +7 -5
  129. package/src/agents.ts +0 -299
  130. package/src/ambient.d.ts +0 -107
  131. package/src/batch/batch-bash.ts +0 -443
  132. package/src/batch/constants.ts +0 -128
  133. package/src/batch/execute.ts +0 -551
  134. package/src/batch/fuzzy-edit.ts +0 -323
  135. package/src/batch/index.ts +0 -494
  136. package/src/batch/render.ts +0 -81
  137. package/src/batch/symbols.ts +0 -341
  138. package/src/batch.ts +0 -28
  139. package/src/cli-args.ts +0 -315
  140. package/src/config.ts +0 -391
  141. package/src/executor.ts +0 -445
  142. package/src/flow.ts +0 -834
  143. package/src/hooks.ts +0 -294
  144. package/src/index.ts +0 -1132
  145. package/src/render-utils.ts +0 -205
  146. package/src/render.ts +0 -524
  147. package/src/runner-events.ts +0 -692
  148. package/src/session-mode.ts +0 -33
  149. package/src/sliding-prompt.ts +0 -144
  150. package/src/structured-output.ts +0 -195
  151. package/src/timed-bash.ts +0 -270
  152. package/src/transitions.ts +0 -86
  153. package/src/types.ts +0 -386
  154. 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
- }