howtofixcli 0.7.8 → 0.7.9
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/dist/chunk-27TRKD65.js +2 -0
- package/dist/chunk-3FYCFLNK.js +1 -0
- package/dist/chunk-56SEAGAQ.js +1 -0
- package/dist/chunk-63ZCQDXH.js +6 -0
- package/dist/chunk-6FUWMLWO.js +1 -0
- package/dist/chunk-7CHDNTH6.js +1 -0
- package/dist/chunk-ACOMNDUL.js +185 -0
- package/dist/chunk-AEVPET7X.js +2 -0
- package/dist/chunk-AGGIYXN7.js +2 -0
- package/dist/chunk-BHVBO4SC.js +1 -0
- package/dist/chunk-FPXVDP32.js +8 -0
- package/dist/chunk-GIUYIKH3.js +109 -0
- package/dist/chunk-GQCAJ56I.js +5 -0
- package/dist/chunk-GXB7RZHH.js +1 -0
- package/dist/chunk-IGEY3AWF.js +1 -0
- package/dist/chunk-ILDDDLRC.js +1 -0
- package/dist/chunk-JFYVRWJI.js +2 -0
- package/dist/chunk-MVLCGU3G.js +1 -0
- package/dist/chunk-N345KWWK.js +1 -0
- package/dist/chunk-NBQFPCRT.js +1 -0
- package/dist/chunk-OIPSLIZE.js +5 -0
- package/dist/chunk-ORJ2QZBG.js +3 -0
- package/dist/chunk-PCALBZOO.js +2 -0
- package/dist/chunk-PDNBGUYP.js +2 -0
- package/dist/chunk-S656RE4A.js +1 -0
- package/dist/chunk-TSEKPGK4.js +1 -0
- package/dist/chunk-U6CI6KXN.js +34 -0
- package/dist/chunk-UTYFLLSM.js +1 -0
- package/dist/chunk-V3R4FYTJ.js +1 -0
- package/dist/chunk-V656XVXV.js +8 -0
- package/dist/chunk-WKUJ7CFO.js +1 -0
- package/dist/chunk-WOT6VMZA.js +1 -0
- package/dist/chunk-WUPU6LTD.js +4 -0
- package/dist/chunk-WX3V5P7U.js +5 -0
- package/dist/chunk-ZKZ6WCI3.js +2 -0
- package/dist/main.js +94 -536
- package/package.json +15 -62
- package/dist/account-credential.js +0 -136
- package/dist/account-credential.test.js +0 -38
- package/dist/activity.js +0 -124
- package/dist/agent-defs.js +0 -237
- package/dist/agent-limiter.js +0 -68
- package/dist/agents.js +0 -554
- package/dist/anim.js +0 -361
- package/dist/args.js +0 -228
- package/dist/at-file.js +0 -28
- package/dist/brand-extension.js +0 -87
- package/dist/brand.js +0 -76
- package/dist/browser.js +0 -58
- package/dist/cache-report.js +0 -139
- package/dist/checkpoint.js +0 -437
- package/dist/claims.js +0 -167
- package/dist/cli-auth.js +0 -142
- package/dist/cli-checkin.js +0 -120
- package/dist/cli-credentials.js +0 -254
- package/dist/clipboard.js +0 -149
- package/dist/collapse.js +0 -129
- package/dist/commands.js +0 -301
- package/dist/compaction-guard.js +0 -81
- package/dist/context-extension.js +0 -17
- package/dist/dcp/commands.js +0 -172
- package/dist/dcp/compress-state.js +0 -139
- package/dist/dcp/compress.js +0 -329
- package/dist/dcp/dcp-extension.js +0 -288
- package/dist/dcp/dedup.js +0 -93
- package/dist/dcp/ids.js +0 -189
- package/dist/dcp/index.js +0 -25
- package/dist/dcp/prompts.js +0 -68
- package/dist/dcp/protected-content.js +0 -221
- package/dist/dcp/protected.js +0 -161
- package/dist/dcp/prune.js +0 -418
- package/dist/dcp/state.js +0 -176
- package/dist/device-id.js +0 -90
- package/dist/device-id.test.js +0 -23
- package/dist/errors.js +0 -63
- package/dist/extensions.js +0 -48
- package/dist/fatal.js +0 -35
- package/dist/feed.js +0 -498
- package/dist/fs-atomic.js +0 -54
- package/dist/gateway-errors.js +0 -90
- package/dist/git.js +0 -230
- package/dist/goal-extension.js +0 -88
- package/dist/goal.js +0 -222
- package/dist/hooks-extension.js +0 -176
- package/dist/hooks.js +0 -443
- package/dist/howtofix-account.js +0 -715
- package/dist/hud-line.js +0 -153
- package/dist/hwid.js +0 -130
- package/dist/interrupt.js +0 -60
- package/dist/json-events.js +0 -50
- package/dist/level-style.js +0 -112
- package/dist/loop-guard.js +0 -95
- package/dist/lsp-tools.js +0 -32
- package/dist/lsp.js +0 -347
- package/dist/mcp-config.js +0 -85
- package/dist/mcp-lifecycle.js +0 -147
- package/dist/model-catalog.js +0 -217
- package/dist/model-resolve.js +0 -65
- package/dist/notes.js +0 -78
- package/dist/oauth-login.js +0 -568
- package/dist/paths.js +0 -61
- package/dist/permissions.js +0 -298
- package/dist/plan-mode.js +0 -363
- package/dist/preset-command.js +0 -227
- package/dist/preset-extension.js +0 -35
- package/dist/preset-mcp.js +0 -292
- package/dist/presets.js +0 -727
- package/dist/prompt-files.js +0 -103
- package/dist/prompt-text.js +0 -41
- package/dist/proxy.js +0 -137
- package/dist/quality/ast.js +0 -196
- package/dist/quality/cognitive.js +0 -83
- package/dist/quality/cyclomatic.js +0 -30
- package/dist/quality/halstead.js +0 -83
- package/dist/quality/index.js +0 -18
- package/dist/quality/length.js +0 -3
- package/dist/quality/maintainability.js +0 -15
- package/dist/quality/nesting.js +0 -57
- package/dist/quality/report.js +0 -803
- package/dist/quality/smells.js +0 -67
- package/dist/quality-tool.js +0 -63
- package/dist/read-only-commands.js +0 -597
- package/dist/repo-map.js +0 -292
- package/dist/rtk.js +0 -193
- package/dist/sandbox.js +0 -105
- package/dist/schema.js +0 -214
- package/dist/session-lifecycle.js +0 -19
- package/dist/session-retention.js +0 -111
- package/dist/session.js +0 -446
- package/dist/sessions.js +0 -100
- package/dist/settings.js +0 -148
- package/dist/skills.js +0 -35
- package/dist/slop-tool.js +0 -96
- package/dist/spend-cap.js +0 -43
- package/dist/subagent.js +0 -171
- package/dist/tar.js +0 -146
- package/dist/theme.js +0 -79
- package/dist/thinking.js +0 -45
- package/dist/todo.js +0 -114
- package/dist/tool-loadout.js +0 -97
- package/dist/tool-render.js +0 -769
- package/dist/tools.js +0 -176
- package/dist/ui.js +0 -4757
- package/dist/update.js +0 -533
- package/dist/usage-segment.js +0 -69
- package/dist/web-tools.js +0 -46
- package/dist/web.js +0 -265
- package/dist/welcome.js +0 -179
- package/dist/workflow-engine.js +0 -246
- package/dist/workflow-progress.js +0 -446
- package/dist/workflow-view.js +0 -195
- package/dist/workflow.js +0 -349
package/dist/workflow.js
DELETED
|
@@ -1,349 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The `workflow` tool: one script becomes a fleet of agents.
|
|
3
|
-
*
|
|
4
|
-
* The model writes a JavaScript workflow and this runs it, which is Claude
|
|
5
|
-
* Code's `ultracode` mode reduced to what this harness's SDK can support. The
|
|
6
|
-
* script's bindings live in `workflow-engine.ts`; this file owns the two things
|
|
7
|
-
* that are about *this* harness:
|
|
8
|
-
*
|
|
9
|
-
* 1. **Which agent a call becomes.** The engine is backend-agnostic and asks for
|
|
10
|
-
* an agent; here that resolves to the subagent registry in `agents.ts`
|
|
11
|
-
* (`scout`/`task`/`reviewer`), opened directly rather than through the `task`
|
|
12
|
-
* tool — the tool's `drain()` runs *every* queued run, so concurrent
|
|
13
|
-
* `agent()` calls would each try to execute each other's runs. One run per
|
|
14
|
-
* call, one session per call, no shared queue.
|
|
15
|
-
*
|
|
16
|
-
* 2. **`meta` and the load-time rules.** The spec's file grammar: the first
|
|
17
|
-
* statement must be `export const meta = <pure literal>`, and the body must
|
|
18
|
-
* not import or export. `meta.phases[].title` values are matched against
|
|
19
|
-
* `phase()` calls for progress grouping.
|
|
20
|
-
*/
|
|
21
|
-
import { readFileSync } from "node:fs";
|
|
22
|
-
import { basename } from "node:path";
|
|
23
|
-
import { StringEnum } from "@netrodev/hxf-ai";
|
|
24
|
-
import { defineTool } from "@netrodev/hxf-coding-agent";
|
|
25
|
-
import { Type } from "typebox";
|
|
26
|
-
import { agents, modelRef, openAgentSession } from "./agents.js";
|
|
27
|
-
import { createAgentWorktree } from "./git.js";
|
|
28
|
-
import { createBindings, WorkflowError } from "./workflow-engine.js";
|
|
29
|
-
import { formatElapsed, formatProgressLine } from "./workflow-progress.js";
|
|
30
|
-
/** The status key the live progress line is published under. */
|
|
31
|
-
export const WORKFLOW_STATUS_KEY = "workflow";
|
|
32
|
-
let workflowControls = null;
|
|
33
|
-
export function setWorkflowControls(next) {
|
|
34
|
-
workflowControls = next;
|
|
35
|
-
}
|
|
36
|
-
/** Cap on how long one run may take, so a runaway script cannot hang the tool. */
|
|
37
|
-
const RUN_TIMEOUT_MS = 10 * 60 * 1000;
|
|
38
|
-
/**
|
|
39
|
-
* Read `meta` and enforce the load-time grammar.
|
|
40
|
-
*
|
|
41
|
-
* The spec allows only literal forms in `meta` (no identifiers, calls, member
|
|
42
|
-
* access or spreads) and forbids `import`/`export` in the body. Rather than
|
|
43
|
-
* writing a full AST check, the export is extracted textually and the literal is
|
|
44
|
-
* evaluated by a `Function` with the binding names shadowed — so an identifier
|
|
45
|
-
* in `meta` fails loudly instead of silently evaluating.
|
|
46
|
-
*/
|
|
47
|
-
export function parseMeta(code) {
|
|
48
|
-
// Comments and blank lines may precede `meta`: both reference parsers skip
|
|
49
|
-
// them, and the contract's own checklist *requires* a header comment ("use
|
|
50
|
-
// case, when to use, args, patterns demonstrated, cost profile, output
|
|
51
|
-
// shape") above the literal. Requiring the export to be the byte-zero
|
|
52
|
-
// statement rejected every real workflow.
|
|
53
|
-
const stripped = code.replace(/^(?:\s+|\/\/[^\n]*\n|\/\*[\s\S]*?\*\/)*/, "");
|
|
54
|
-
const head = /^export\s+const\s+meta\s*=\s*/.exec(stripped);
|
|
55
|
-
if (!head) {
|
|
56
|
-
throw new WorkflowError("a workflow script must start with `export const meta = { name, description }`");
|
|
57
|
-
}
|
|
58
|
-
// Keep the body relative to the original source so line numbers in later
|
|
59
|
-
// errors still point at the file the author wrote.
|
|
60
|
-
const bodyStart = code.length - stripped.length;
|
|
61
|
-
// Find the end of the literal by brace matching rather than by looking for a
|
|
62
|
-
// semicolon: the reference scripts omit it (`export const meta = { ... }`),
|
|
63
|
-
// and requiring one rejected every real workflow.
|
|
64
|
-
const literalStart = head[0].length;
|
|
65
|
-
let depth = 0;
|
|
66
|
-
let inString = null;
|
|
67
|
-
let end = -1;
|
|
68
|
-
for (let index = literalStart; index < stripped.length; index++) {
|
|
69
|
-
const char = stripped[index];
|
|
70
|
-
if (inString) {
|
|
71
|
-
if (char === "\\")
|
|
72
|
-
index++;
|
|
73
|
-
else if (char === inString)
|
|
74
|
-
inString = null;
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
if (char === '"' || char === "'" || char === "`")
|
|
78
|
-
inString = char;
|
|
79
|
-
else if (char === "{" || char === "[")
|
|
80
|
-
depth++;
|
|
81
|
-
else if (char === "}" || char === "]") {
|
|
82
|
-
depth--;
|
|
83
|
-
if (depth === 0) {
|
|
84
|
-
end = index + 1;
|
|
85
|
-
break;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
if (end === -1)
|
|
90
|
-
throw new WorkflowError("meta is not a closed literal object");
|
|
91
|
-
const literal = stripped.slice(literalStart, end);
|
|
92
|
-
// A trailing semicolon is optional.
|
|
93
|
-
const body = code.slice(bodyStart + end).replace(/^\s*;/, "");
|
|
94
|
-
if (/^\s*(import|export)\b/m.test(body)) {
|
|
95
|
-
throw new WorkflowError("a workflow body must not contain import or export declarations");
|
|
96
|
-
}
|
|
97
|
-
if (/\bimport\s*\(/.test(body)) {
|
|
98
|
-
throw new WorkflowError("a workflow body must not use dynamic import()");
|
|
99
|
-
}
|
|
100
|
-
let meta;
|
|
101
|
-
try {
|
|
102
|
-
meta = new Function(`"use strict"; return (${literal});`)();
|
|
103
|
-
}
|
|
104
|
-
catch (error) {
|
|
105
|
-
throw new WorkflowError(`meta must be a literal object: ${error instanceof Error ? error.message : String(error)}`);
|
|
106
|
-
}
|
|
107
|
-
if (typeof meta !== "object" || meta === null)
|
|
108
|
-
throw new WorkflowError("meta must be an object");
|
|
109
|
-
const record = meta;
|
|
110
|
-
if (typeof record.name !== "string" || !record.name.trim())
|
|
111
|
-
throw new WorkflowError("meta.name is required");
|
|
112
|
-
if (typeof record.description !== "string" || !record.description.trim()) {
|
|
113
|
-
throw new WorkflowError("meta.description is required");
|
|
114
|
-
}
|
|
115
|
-
const phases = Array.isArray(record.phases)
|
|
116
|
-
? record.phases
|
|
117
|
-
.filter((entry) => typeof entry === "object" && entry !== null)
|
|
118
|
-
.map((entry) => ({
|
|
119
|
-
title: String(entry.title ?? ""),
|
|
120
|
-
...(typeof entry.detail === "string" ? { detail: entry.detail } : {}),
|
|
121
|
-
}))
|
|
122
|
-
.filter((entry) => entry.title)
|
|
123
|
-
: undefined;
|
|
124
|
-
return {
|
|
125
|
-
meta: {
|
|
126
|
-
name: record.name,
|
|
127
|
-
description: record.description,
|
|
128
|
-
...(typeof record.whenToUse === "string" ? { whenToUse: record.whenToUse } : {}),
|
|
129
|
-
...(phases ? { phases } : {}),
|
|
130
|
-
},
|
|
131
|
-
body,
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* Compile a body into an async function.
|
|
136
|
-
*
|
|
137
|
-
* `Function` is the only option here: the sandbox `codemode` uses is not
|
|
138
|
-
* reachable with custom bindings (`CodemodeToolOptions` has no hook for them),
|
|
139
|
-
* and a workflow script is model-authored code running in the user's own
|
|
140
|
-
* session — the same trust level as the shell commands the agent already runs.
|
|
141
|
-
* The body is a module body, so top-level `await` and `return` are legal, which
|
|
142
|
-
* rules out a plain `Function`.
|
|
143
|
-
*/
|
|
144
|
-
export function compileBody(body) {
|
|
145
|
-
const names = ["agent", "parallel", "pipeline", "phase", "log", "args", "budget"];
|
|
146
|
-
const source = `"use strict"; return (async () => {\n${body}\n})();`;
|
|
147
|
-
let factory;
|
|
148
|
-
try {
|
|
149
|
-
factory = new Function(...names, source);
|
|
150
|
-
}
|
|
151
|
-
catch (error) {
|
|
152
|
-
throw new WorkflowError(`workflow did not compile: ${error instanceof Error ? error.message : String(error)}`);
|
|
153
|
-
}
|
|
154
|
-
return factory;
|
|
155
|
-
}
|
|
156
|
-
/** Read the final text out of an agent session, whatever shape it returns. */
|
|
157
|
-
function finalText(session) {
|
|
158
|
-
return session.getLastAssistantText()?.trim() ?? "";
|
|
159
|
-
}
|
|
160
|
-
/**
|
|
161
|
-
* Run one agent call on a fresh subagent session.
|
|
162
|
-
*
|
|
163
|
-
* Output tokens, tool calls and the model come from the session's own stats,
|
|
164
|
-
* which is what makes the script's `budget` real rather than advisory and what
|
|
165
|
-
* fills the per-agent metric in the progress view.
|
|
166
|
-
*
|
|
167
|
-
* `isolation: "worktree"` gives the call its own checkout via `openAgentSession`,
|
|
168
|
-
* which is the only correct way to run parallel agents that write to the same
|
|
169
|
-
* files: siblings in one working tree race each other.
|
|
170
|
-
*/
|
|
171
|
-
export function createInvoker(parentModel) {
|
|
172
|
-
return async ({ prompt, agentType, effort, model, isolation }) => {
|
|
173
|
-
const spec = agents.get(agentType) ?? agents.get("task");
|
|
174
|
-
if (!spec)
|
|
175
|
-
throw new WorkflowError(`no agent named ${agentType}`);
|
|
176
|
-
// `isolation: "worktree"` means a private checkout, not just a private cwd:
|
|
177
|
-
// parallel writers in one tree race each other's builds and `git add -A`.
|
|
178
|
-
const worktree = isolation === "worktree" ? await createAgentWorktree(process.cwd(), agentType) : null;
|
|
179
|
-
const session = await openAgentSession({
|
|
180
|
-
agent: spec.name,
|
|
181
|
-
effort: (effort ?? spec.effort),
|
|
182
|
-
// The script's `model` has to reach the session, not only the metric line:
|
|
183
|
-
// it used to be reported as the model that ran while the child actually
|
|
184
|
-
// ran the runtime default.
|
|
185
|
-
...(model ? { model } : {}),
|
|
186
|
-
...(parentModel ? { parentModel } : {}),
|
|
187
|
-
...(worktree ? { worktree: worktree.path } : {}),
|
|
188
|
-
});
|
|
189
|
-
try {
|
|
190
|
-
await session.prompt(`${spec.prompt}\n\nTask:\n${prompt}`);
|
|
191
|
-
const stats = session.getSessionStats();
|
|
192
|
-
// The session first: it is the one that ran. The script's request is the
|
|
193
|
-
// fallback for a runtime that cannot name its own model.
|
|
194
|
-
const resolved = modelId(session) ?? model;
|
|
195
|
-
return {
|
|
196
|
-
text: finalText(session),
|
|
197
|
-
outputTokens: stats.tokens.output,
|
|
198
|
-
toolCalls: stats.toolCalls,
|
|
199
|
-
...(resolved ? { model: resolved } : {}),
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
finally {
|
|
203
|
-
session.dispose();
|
|
204
|
-
await worktree?.remove();
|
|
205
|
-
}
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
/** The model a session actually ran on, when the SDK exposes it. */
|
|
209
|
-
function modelId(session) {
|
|
210
|
-
return session.model?.id ?? session.model?.name;
|
|
211
|
-
}
|
|
212
|
-
export const workflowParams = Type.Object({
|
|
213
|
-
code: Type.Optional(Type.String({ description: "The workflow script source" })),
|
|
214
|
-
path: Type.Optional(Type.String({ description: "Path to a workflow script, instead of code" })),
|
|
215
|
-
agent: Type.Optional(StringEnum(["scout", "task", "reviewer"], { description: "Default agent profile for calls" })),
|
|
216
|
-
budget: Type.Optional(Type.Number({ description: "Output-token ceiling for the whole run" })),
|
|
217
|
-
});
|
|
218
|
-
export function createWorkflowTool(options) {
|
|
219
|
-
// The workbench registers its panel hooks once, the same way `setAsker` and
|
|
220
|
-
// `setMirror` hand other modules a live surface. The tool is built during
|
|
221
|
-
// session construction, before the TUI exists, so it cannot be passed in.
|
|
222
|
-
const hooks = () => options ?? workflowControls;
|
|
223
|
-
return defineTool({
|
|
224
|
-
name: "workflow",
|
|
225
|
-
label: "workflow",
|
|
226
|
-
description: "Run a JavaScript workflow that orchestrates many agents deterministically. Provide `code` (or `path`). The script starts with `export const meta = { name, description, phases }` and then uses the injected bindings: `agent(prompt, {label, phase, schema, effort, model, agentType, isolation})`, `parallel(thunks)`, `pipeline(items, ...stages)`, `phase(title)`, `log(msg)`, `args`, `budget`. `agent()` resolves null on failure and throws only when the budget or the call cap is exhausted, so null-check every result. `parallel()` takes thunks and never rejects; `pipeline()` has no barrier between items. Use it for fan-out, for builder-verifier loops that converge on a verdict, and for anything needing many agents whose order and count must be deterministic.",
|
|
227
|
-
promptSnippet: "workflow: orchestrate many agents from one JavaScript script",
|
|
228
|
-
parameters: workflowParams,
|
|
229
|
-
executionMode: "sequential",
|
|
230
|
-
async execute(_toolCallId, params, signal, onUpdate, ctx) {
|
|
231
|
-
const code = params.path ? readFileSync(params.path, "utf8") : (params.code ?? "");
|
|
232
|
-
if (!code.trim())
|
|
233
|
-
throw new WorkflowError("provide code or path");
|
|
234
|
-
const { meta, body } = parseMeta(code);
|
|
235
|
-
// The contract requires `meta.name` to match the script's filename, so a
|
|
236
|
-
// run launched by path is identifiable in the progress list and in any
|
|
237
|
-
// artifact directory named after it. A mismatch is a real error, not a
|
|
238
|
-
// warning: it is what makes the name trustworthy.
|
|
239
|
-
if (params.path) {
|
|
240
|
-
const file = basename(params.path).replace(/\.(js|mjs|cjs|ts)$/, "");
|
|
241
|
-
if (file !== meta.name) {
|
|
242
|
-
throw new WorkflowError(`meta.name "${meta.name}" must match the script filename "${file}"`);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
const factory = compileBody(body);
|
|
246
|
-
const startedAt = Date.now();
|
|
247
|
-
const steps = [];
|
|
248
|
-
let outputTokens = 0;
|
|
249
|
-
let callCount = 0;
|
|
250
|
-
const detailsOf = (ok, error) => ({
|
|
251
|
-
name: meta.name,
|
|
252
|
-
...(meta.description ? { description: meta.description } : {}),
|
|
253
|
-
ok,
|
|
254
|
-
agents: callCount,
|
|
255
|
-
outputTokens,
|
|
256
|
-
startedAt,
|
|
257
|
-
phases: meta.phases ?? [],
|
|
258
|
-
steps,
|
|
259
|
-
...(error ? { error } : {}),
|
|
260
|
-
});
|
|
261
|
-
// The live surface: the dock line shows the run in flight, the way the
|
|
262
|
-
// reference puts its progress line in the task panel under the input box.
|
|
263
|
-
const publish = (progress, ended) => {
|
|
264
|
-
ctx.ui.setStatus(WORKFLOW_STATUS_KEY, ended ? undefined : formatProgressLine(progress));
|
|
265
|
-
onUpdate?.({ content: [], details: detailsOf(false) });
|
|
266
|
-
};
|
|
267
|
-
const onProgress = (agents) => {
|
|
268
|
-
callCount = agents.length;
|
|
269
|
-
outputTokens = agents.reduce((sum, agent) => sum + agent.outputTokens, 0);
|
|
270
|
-
steps.splice(0, steps.length, ...agents.map((agent) => ({
|
|
271
|
-
label: agent.label,
|
|
272
|
-
phase: agent.phase,
|
|
273
|
-
agent: agent.agentType,
|
|
274
|
-
status: agent.status,
|
|
275
|
-
outputTokens: agent.outputTokens,
|
|
276
|
-
startedAt: agent.startedAt,
|
|
277
|
-
...(agent.toolCalls !== undefined ? { toolCalls: agent.toolCalls } : {}),
|
|
278
|
-
...(agent.model ? { model: agent.model } : {}),
|
|
279
|
-
...(agent.endedAt ? { endedAt: agent.endedAt } : {}),
|
|
280
|
-
...(agent.detail ? { detail: agent.detail } : {}),
|
|
281
|
-
...(agent.tasks ? { tasks: agent.tasks } : {}),
|
|
282
|
-
})));
|
|
283
|
-
publish({ name: meta.name, ...(meta.description ? { description: meta.description } : {}), phases: meta.phases ?? [], steps, startedAt }, false);
|
|
284
|
-
};
|
|
285
|
-
const invoke = createInvoker(modelRef(ctx?.model));
|
|
286
|
-
const bindings = createBindings({
|
|
287
|
-
invoke: params.agent ? wrapAgent(invoke, params.agent) : invoke,
|
|
288
|
-
budgetTotal: params.budget ?? null,
|
|
289
|
-
onProgress,
|
|
290
|
-
...(signal ? { signal } : {}),
|
|
291
|
-
...(hooks()?.isPaused ? { isPaused: hooks().isPaused } : {}),
|
|
292
|
-
});
|
|
293
|
-
const controller = new AbortController();
|
|
294
|
-
const timer = setTimeout(() => controller.abort(), RUN_TIMEOUT_MS);
|
|
295
|
-
if (signal)
|
|
296
|
-
signal.addEventListener("abort", () => controller.abort(), { once: true });
|
|
297
|
-
// Hand the panel a way to stop this run. `x` in the panel calls it; the
|
|
298
|
-
// abort then resolves in-flight calls as `null`, which is the engine's
|
|
299
|
-
// normal failure mode, so a stopped run still returns a partial result
|
|
300
|
-
// instead of throwing.
|
|
301
|
-
hooks()?.onStop?.(() => controller.abort());
|
|
302
|
-
try {
|
|
303
|
-
const result = await Promise.race([
|
|
304
|
-
factory(bindings.agent, bindings.parallel, bindings.pipeline, bindings.phase, bindings.log, bindings.args, bindings.budget),
|
|
305
|
-
new Promise((_resolve, reject) => controller.signal.addEventListener("abort", () => reject(new WorkflowError("workflow timed out or was aborted")))),
|
|
306
|
-
]);
|
|
307
|
-
const endedAt = Date.now();
|
|
308
|
-
ctx.ui.setStatus(WORKFLOW_STATUS_KEY, undefined);
|
|
309
|
-
return {
|
|
310
|
-
content: [
|
|
311
|
-
{
|
|
312
|
-
type: "text",
|
|
313
|
-
text: `${meta.name}: ${callCount} agent call(s), ${outputTokens} output tokens in ${formatElapsed(endedAt - startedAt)}\n\n${render(result)}`,
|
|
314
|
-
},
|
|
315
|
-
],
|
|
316
|
-
details: { ...detailsOf(true), endedAt },
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
catch (error) {
|
|
320
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
321
|
-
ctx.ui.setStatus(WORKFLOW_STATUS_KEY, undefined);
|
|
322
|
-
return {
|
|
323
|
-
content: [{ type: "text", text: `${meta.name} failed after ${callCount} agent call(s): ${message}` }],
|
|
324
|
-
details: { ...detailsOf(false, message), endedAt: Date.now() },
|
|
325
|
-
isError: true,
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
finally {
|
|
329
|
-
clearTimeout(timer);
|
|
330
|
-
hooks()?.onStop?.(null);
|
|
331
|
-
}
|
|
332
|
-
},
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
/** Force every call to one agent profile, whatever the script asked for. */
|
|
336
|
-
function wrapAgent(invoke, agentType) {
|
|
337
|
-
return (spec) => invoke({ ...spec, agentType });
|
|
338
|
-
}
|
|
339
|
-
/** The script's return value, as text for the model. */
|
|
340
|
-
function render(value) {
|
|
341
|
-
if (typeof value === "string")
|
|
342
|
-
return value;
|
|
343
|
-
try {
|
|
344
|
-
return JSON.stringify(value, null, 2);
|
|
345
|
-
}
|
|
346
|
-
catch {
|
|
347
|
-
return String(value);
|
|
348
|
-
}
|
|
349
|
-
}
|