@bubblebrain-ai/bubble 0.0.28 → 0.0.29
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 +21 -0
- package/dist/agent/categories.d.ts +2 -0
- package/dist/agent/categories.js +4 -0
- package/dist/agent/child-runner.d.ts +5 -1
- package/dist/agent/child-runner.js +35 -2
- package/dist/agent/profiles.js +3 -0
- package/dist/agent/structured-output.d.ts +37 -0
- package/dist/agent/structured-output.js +193 -0
- package/dist/agent/subagent-control.d.ts +3 -0
- package/dist/agent/subagent-scheduler.d.ts +10 -0
- package/dist/agent/subagent-scheduler.js +31 -0
- package/dist/agent/workflow/control.d.ts +37 -0
- package/dist/agent/workflow/control.js +20 -0
- package/dist/agent/workflow/errors.d.ts +16 -0
- package/dist/agent/workflow/errors.js +24 -0
- package/dist/agent/workflow/runtime.d.ts +75 -0
- package/dist/agent/workflow/runtime.js +237 -0
- package/dist/agent.d.ts +105 -0
- package/dist/agent.js +425 -17
- package/dist/context/compact-llm.d.ts +10 -1
- package/dist/context/compact-llm.js +13 -5
- package/dist/context/compact.d.ts +30 -0
- package/dist/context/compact.js +34 -17
- package/dist/network/provider-transport.d.ts +9 -0
- package/dist/network/provider-transport.js +19 -1
- package/dist/provider.d.ts +14 -0
- package/dist/provider.js +24 -0
- package/dist/session.d.ts +16 -0
- package/dist/session.js +33 -1
- package/dist/slash-commands/commands.js +47 -1
- package/dist/slash-commands/types.d.ts +16 -1
- package/dist/tools/agent-lifecycle.d.ts +6 -0
- package/dist/tools/agent-lifecycle.js +285 -0
- package/dist/tools/child-tools.d.ts +10 -0
- package/dist/tools/child-tools.js +12 -0
- package/dist/tools/read.d.ts +1 -1
- package/dist/tools/read.js +9 -0
- package/dist/tui/image-display.d.ts +6 -0
- package/dist/tui/image-display.js +26 -1
- package/dist/tui-ink/app.js +84 -6
- package/dist/tui-ink/compaction-progress.d.ts +19 -0
- package/dist/tui-ink/compaction-progress.js +74 -0
- package/dist/tui-ink/input-box.d.ts +7 -1
- package/dist/tui-ink/input-box.js +48 -15
- package/dist/tui-ink/markdown.d.ts +18 -0
- package/dist/tui-ink/markdown.js +172 -16
- package/dist/tui-ink/message-list.js +38 -94
- package/dist/tui-ink/run.js +5 -0
- package/dist/tui-ink/subagent-inspector.d.ts +17 -0
- package/dist/tui-ink/subagent-inspector.js +189 -0
- package/dist/tui-ink/subagent-view.d.ts +47 -0
- package/dist/tui-ink/subagent-view.js +163 -0
- package/dist/tui-ink/terminal-env.d.ts +15 -0
- package/dist/tui-ink/terminal-env.js +22 -0
- package/dist/tui-ink/use-terminal-size.js +33 -6
- package/dist/tui-ink/width.d.ts +18 -0
- package/dist/tui-ink/width.js +130 -0
- package/dist/types.d.ts +35 -0
- package/package.json +2 -1
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow runtime (option C) — executes an LLM-authored JS orchestration
|
|
3
|
+
* script in a QuickJS-wasm sandbox and returns its final value.
|
|
4
|
+
*
|
|
5
|
+
* Engine decision (proven empirically, node + bun): the SYNC variant with a
|
|
6
|
+
* `newPromise` deferred-promise bridge — NOT asyncify. Asyncify serializes
|
|
7
|
+
* parallel() and corrupts the VM on the 2nd agent failure; the sync+newPromise
|
|
8
|
+
* bridge gives true concurrency AND clean error propagation across many
|
|
9
|
+
* failures. The host drives the VM job queue via executePendingJobs().
|
|
10
|
+
*
|
|
11
|
+
* The script's only capability is agent(); it has no fs/shell/net/clock/RNG
|
|
12
|
+
* (determinism gating below). parallel/pipeline/phase/log/budget are a JS
|
|
13
|
+
* prelude over the single host function __agent plus a few host callbacks.
|
|
14
|
+
*/
|
|
15
|
+
import { getQuickJS } from "quickjs-emscripten";
|
|
16
|
+
/**
|
|
17
|
+
* Async semaphore bounding how many workflow agents run concurrently — kept
|
|
18
|
+
* below the global scheduler cap so interactive subagents always have slots
|
|
19
|
+
* (option C review M2/M5). Permits are acquired/released only around a leaf
|
|
20
|
+
* agent dispatch, never across parallel/pipeline composition (no deadlock).
|
|
21
|
+
*/
|
|
22
|
+
export class WorkflowConcurrencyGate {
|
|
23
|
+
capacity;
|
|
24
|
+
active = 0;
|
|
25
|
+
waiters = [];
|
|
26
|
+
constructor(capacity) {
|
|
27
|
+
this.capacity = capacity;
|
|
28
|
+
}
|
|
29
|
+
async acquire() {
|
|
30
|
+
if (this.active < this.capacity) {
|
|
31
|
+
this.active += 1;
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
await new Promise((resolve) => this.waiters.push(resolve));
|
|
35
|
+
this.active += 1;
|
|
36
|
+
}
|
|
37
|
+
release() {
|
|
38
|
+
this.active -= 1;
|
|
39
|
+
const next = this.waiters.shift();
|
|
40
|
+
if (next)
|
|
41
|
+
next();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const DEFAULT_MAX_AGENTS = 1000;
|
|
45
|
+
const DEFAULT_COMPUTE_DEADLINE_MS = 10_000;
|
|
46
|
+
// JS prelude evaluated before the user script: defines the script API in terms
|
|
47
|
+
// of the minimal host surface (__agent / __phase / __log / __budget*).
|
|
48
|
+
const PRELUDE = `
|
|
49
|
+
globalThis.agent = async (prompt, opts) => {
|
|
50
|
+
const raw = await __agent(JSON.stringify({ prompt: String(prompt ?? ""), opts: opts || {} }));
|
|
51
|
+
return JSON.parse(raw);
|
|
52
|
+
};
|
|
53
|
+
globalThis.parallel = (thunks) => Promise.all((thunks || []).map((t) => {
|
|
54
|
+
try { return Promise.resolve(t()).catch(() => null); } catch (_e) { return Promise.resolve(null); }
|
|
55
|
+
}));
|
|
56
|
+
globalThis.pipeline = (items, ...stages) => Promise.all((items || []).map(async (item, i) => {
|
|
57
|
+
let v = item;
|
|
58
|
+
for (const stage of stages) {
|
|
59
|
+
try { v = await stage(v, item, i); } catch (_e) { return null; }
|
|
60
|
+
}
|
|
61
|
+
return v;
|
|
62
|
+
}));
|
|
63
|
+
globalThis.phase = (t) => __phase(String(t ?? ""));
|
|
64
|
+
globalThis.log = (m) => __log(String(m ?? ""));
|
|
65
|
+
globalThis.budget = {
|
|
66
|
+
get total() { return __budgetTotal(); },
|
|
67
|
+
spent() { return __budgetSpent(); },
|
|
68
|
+
remaining() { return __budgetRemaining(); },
|
|
69
|
+
};
|
|
70
|
+
`;
|
|
71
|
+
/** Removes ambient nondeterminism so a run is reproducible (design §4.3). */
|
|
72
|
+
const DETERMINISM_GATING = `
|
|
73
|
+
delete globalThis.Date;
|
|
74
|
+
delete globalThis.WeakRef;
|
|
75
|
+
delete globalThis.FinalizationRegistry;
|
|
76
|
+
Math.random = () => { throw new Error("Math.random is disabled in workflows (nondeterministic)"); };
|
|
77
|
+
`;
|
|
78
|
+
/** Turns `export const meta = …` / `export function …` into plain declarations. */
|
|
79
|
+
function stripExports(script) {
|
|
80
|
+
return script.replace(/(^|\n)\s*export\s+(const|let|var|function|class|async)\b/g, "$1$2");
|
|
81
|
+
}
|
|
82
|
+
export async function runWorkflow(options) {
|
|
83
|
+
const QuickJS = await getQuickJS();
|
|
84
|
+
const vm = QuickJS.newContext();
|
|
85
|
+
const pending = new Set();
|
|
86
|
+
const state = { disposed: false };
|
|
87
|
+
let agentCount = 0;
|
|
88
|
+
const maxAgents = options.maxAgents ?? DEFAULT_MAX_AGENTS;
|
|
89
|
+
// Per-compute deadline: only counts VM bytecode time, reset on each pump so a
|
|
90
|
+
// long run that is mostly waiting on agents is not killed.
|
|
91
|
+
let computeStart = Date.now();
|
|
92
|
+
const computeDeadline = options.computeDeadlineMs ?? DEFAULT_COMPUTE_DEADLINE_MS;
|
|
93
|
+
vm.runtime.setInterruptHandler(() => Date.now() - computeStart > computeDeadline);
|
|
94
|
+
try {
|
|
95
|
+
vm.unwrapResult(vm.evalCode(DETERMINISM_GATING)).dispose();
|
|
96
|
+
installHostFunctions(vm, options, pending, () => agentCount, () => { agentCount += 1; }, maxAgents, state);
|
|
97
|
+
// args as a deterministic injected global.
|
|
98
|
+
const argsJson = JSON.stringify(options.args ?? null);
|
|
99
|
+
vm.unwrapResult(vm.evalCode(`globalThis.args = ${argsJson};`)).dispose();
|
|
100
|
+
vm.unwrapResult(vm.evalCode(PRELUDE)).dispose();
|
|
101
|
+
const body = stripExports(options.script);
|
|
102
|
+
const wrapped = [
|
|
103
|
+
"globalThis.__wfdone = false; globalThis.__wfresult = null; globalThis.__wferror = null;",
|
|
104
|
+
"(async () => {",
|
|
105
|
+
body,
|
|
106
|
+
"})().then(",
|
|
107
|
+
" (r) => { globalThis.__wfresult = r === undefined ? null : r; globalThis.__wfdone = true; },",
|
|
108
|
+
" (e) => { globalThis.__wferror = (e && e.message) ? String(e.message) : String(e); globalThis.__wfdone = true; }",
|
|
109
|
+
");",
|
|
110
|
+
].join("\n");
|
|
111
|
+
computeStart = Date.now();
|
|
112
|
+
const evalResult = vm.evalCode(wrapped);
|
|
113
|
+
if (evalResult.error) {
|
|
114
|
+
const message = vm.dump(evalResult.error);
|
|
115
|
+
evalResult.error.dispose();
|
|
116
|
+
return { ok: false, error: `workflow script error: ${formatError(message)}` };
|
|
117
|
+
}
|
|
118
|
+
evalResult.value.dispose();
|
|
119
|
+
// Drive the VM job queue interleaved with host agent settlements.
|
|
120
|
+
const isDone = () => {
|
|
121
|
+
const h = vm.getProp(vm.global, "__wfdone");
|
|
122
|
+
const done = vm.dump(h) === true;
|
|
123
|
+
h.dispose();
|
|
124
|
+
return done;
|
|
125
|
+
};
|
|
126
|
+
while (!isDone()) {
|
|
127
|
+
if (options.signal?.aborted) {
|
|
128
|
+
return { ok: false, error: "workflow aborted" };
|
|
129
|
+
}
|
|
130
|
+
computeStart = Date.now();
|
|
131
|
+
vm.runtime.executePendingJobs();
|
|
132
|
+
if (isDone())
|
|
133
|
+
break;
|
|
134
|
+
if (pending.size === 0) {
|
|
135
|
+
if (!vm.runtime.hasPendingJob || !vm.runtime.hasPendingJob())
|
|
136
|
+
break; // settled or stalled
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
await Promise.race([...pending, abortRace(options.signal)]);
|
|
140
|
+
}
|
|
141
|
+
const errH = vm.getProp(vm.global, "__wferror");
|
|
142
|
+
const err = vm.dump(errH);
|
|
143
|
+
errH.dispose();
|
|
144
|
+
if (err != null && err !== "") {
|
|
145
|
+
return { ok: false, error: String(err) };
|
|
146
|
+
}
|
|
147
|
+
const resH = vm.getProp(vm.global, "__wfresult");
|
|
148
|
+
const value = vm.dump(resH);
|
|
149
|
+
resH.dispose();
|
|
150
|
+
return { ok: true, value };
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
return { ok: false, error: error?.message || String(error) };
|
|
154
|
+
}
|
|
155
|
+
finally {
|
|
156
|
+
state.disposed = true;
|
|
157
|
+
vm.dispose();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function abortRace(signal) {
|
|
161
|
+
if (!signal)
|
|
162
|
+
return new Promise(() => { }); // never settles
|
|
163
|
+
if (signal.aborted)
|
|
164
|
+
return Promise.resolve();
|
|
165
|
+
return new Promise((resolve) => signal.addEventListener("abort", () => resolve(), { once: true }));
|
|
166
|
+
}
|
|
167
|
+
function installHostFunctions(vm, options, pending, getCount, bumpCount, maxAgents, state) {
|
|
168
|
+
vm.newFunction("__agent", (specHandle) => {
|
|
169
|
+
const spec = JSON.parse(vm.getString(specHandle));
|
|
170
|
+
const deferred = vm.newPromise();
|
|
171
|
+
// Settling the VM promise touches the context, which may have been disposed
|
|
172
|
+
// if the run was aborted while host work was still in flight — guard it.
|
|
173
|
+
const settle = (fn) => {
|
|
174
|
+
if (state.disposed)
|
|
175
|
+
return;
|
|
176
|
+
try {
|
|
177
|
+
fn();
|
|
178
|
+
}
|
|
179
|
+
catch { /* VM disposed mid-settle */ }
|
|
180
|
+
};
|
|
181
|
+
if (getCount() >= maxAgents) {
|
|
182
|
+
const e = vm.newString(`workflow exceeded the ${maxAgents}-agent cap`);
|
|
183
|
+
deferred.reject(e);
|
|
184
|
+
e.dispose();
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
bumpCount();
|
|
188
|
+
const p = options.dispatchAgent(spec).then((res) => settle(() => {
|
|
189
|
+
if (res.ok) {
|
|
190
|
+
const v = vm.newString(JSON.stringify(res.value ?? null));
|
|
191
|
+
deferred.resolve(v);
|
|
192
|
+
v.dispose();
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
const e = vm.newString(res.error);
|
|
196
|
+
deferred.reject(e);
|
|
197
|
+
e.dispose();
|
|
198
|
+
}
|
|
199
|
+
}), (err) => settle(() => {
|
|
200
|
+
const e = vm.newString(err?.message || String(err));
|
|
201
|
+
deferred.reject(e);
|
|
202
|
+
e.dispose();
|
|
203
|
+
})).finally(() => { pending.delete(p); });
|
|
204
|
+
pending.add(p);
|
|
205
|
+
}
|
|
206
|
+
deferred.settled.then(() => { if (!state.disposed)
|
|
207
|
+
try {
|
|
208
|
+
vm.runtime.executePendingJobs();
|
|
209
|
+
}
|
|
210
|
+
catch { /* disposed */ } });
|
|
211
|
+
return deferred.handle;
|
|
212
|
+
}).consume((f) => vm.setProp(vm.global, "__agent", f));
|
|
213
|
+
vm.newFunction("__phase", (h) => {
|
|
214
|
+
options.onPhase?.(vm.getString(h));
|
|
215
|
+
return vm.undefined;
|
|
216
|
+
}).consume((f) => vm.setProp(vm.global, "__phase", f));
|
|
217
|
+
vm.newFunction("__log", (h) => {
|
|
218
|
+
options.onLog?.(vm.getString(h));
|
|
219
|
+
return vm.undefined;
|
|
220
|
+
}).consume((f) => vm.setProp(vm.global, "__log", f));
|
|
221
|
+
vm.newFunction("__budgetTotal", () => {
|
|
222
|
+
const total = options.budget?.total ?? null;
|
|
223
|
+
return total === null ? vm.null : vm.newNumber(total);
|
|
224
|
+
}).consume((f) => vm.setProp(vm.global, "__budgetTotal", f));
|
|
225
|
+
vm.newFunction("__budgetSpent", () => vm.newNumber(options.budget?.spent() ?? 0))
|
|
226
|
+
.consume((f) => vm.setProp(vm.global, "__budgetSpent", f));
|
|
227
|
+
vm.newFunction("__budgetRemaining", () => {
|
|
228
|
+
const remaining = options.budget?.remaining() ?? Number.POSITIVE_INFINITY;
|
|
229
|
+
return vm.newNumber(Number.isFinite(remaining) ? remaining : Number.MAX_SAFE_INTEGER);
|
|
230
|
+
}).consume((f) => vm.setProp(vm.global, "__budgetRemaining", f));
|
|
231
|
+
}
|
|
232
|
+
function formatError(value) {
|
|
233
|
+
if (value && typeof value === "object" && "message" in value) {
|
|
234
|
+
return String(value.message);
|
|
235
|
+
}
|
|
236
|
+
return String(value);
|
|
237
|
+
}
|
package/dist/agent.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type { AgentEvent, AgentInputController, ContentPart, PermissionMode, Mes
|
|
|
7
7
|
import { type TurnHooks } from "./orchestrator/hooks.js";
|
|
8
8
|
import type { ExternalHookController } from "./hooks/controller.js";
|
|
9
9
|
import { type AgentCategoriesConfig, type ResolvedSubagentRoute } from "./agent/categories.js";
|
|
10
|
+
import { type WorkflowRunSnapshot } from "./agent/workflow/control.js";
|
|
10
11
|
import { BudgetLedger } from "./agent/budget-ledger.js";
|
|
11
12
|
import { type AgentProfile, type SubagentRunResult } from "./agent/profiles.js";
|
|
12
13
|
import { type SubagentThreadSnapshot } from "./agent/subagent-control.js";
|
|
@@ -23,6 +24,8 @@ export interface AgentSubagentRuntimeConfig {
|
|
|
23
24
|
launchIntervalMs?: number;
|
|
24
25
|
rateLimitMaxAttempts?: number;
|
|
25
26
|
rateLimitBackoffMs?: number[];
|
|
27
|
+
transportRetryMaxAttempts?: number;
|
|
28
|
+
transportRetryBackoffMs?: number[];
|
|
26
29
|
/**
|
|
27
30
|
* Directory for persisted child state (design §7). Defaults to
|
|
28
31
|
* `<session>.subagents` next to the session file when a session exists.
|
|
@@ -112,6 +115,10 @@ export declare class Agent {
|
|
|
112
115
|
private readonly subagentScheduler;
|
|
113
116
|
private readonly childRunner;
|
|
114
117
|
private readonly resultIntegrator;
|
|
118
|
+
/** Background dynamic-workflow runs (option C Phase 4), keyed by runId. */
|
|
119
|
+
private readonly workflowRuns;
|
|
120
|
+
/** runIds whose completed result should be ingested at the next turn. */
|
|
121
|
+
private readonly pendingWorkflowDeliveries;
|
|
115
122
|
private subagentsConfig;
|
|
116
123
|
private readonly rateLimitPolicy?;
|
|
117
124
|
private pendingSubagentUpdates;
|
|
@@ -164,6 +171,17 @@ export declare class Agent {
|
|
|
164
171
|
private recoverFromOverflow;
|
|
165
172
|
compactResidentHistory(): void;
|
|
166
173
|
private maybeCompactWithLLM;
|
|
174
|
+
/**
|
|
175
|
+
* Stream a 9-section handoff summary of `oldMessages` from the session model.
|
|
176
|
+
* Powers the manual `/compact` command: streaming (rather than `complete()`)
|
|
177
|
+
* is what lets the TUI show live progress as the summary is produced.
|
|
178
|
+
*
|
|
179
|
+
* `onDelta` receives the full accumulated text and the latest delta on each
|
|
180
|
+
* chunk. Returns the trimmed summary, or "" if the model produced nothing
|
|
181
|
+
* (the caller falls back to heuristic compaction in that case). Throws only
|
|
182
|
+
* if the provider stream itself errors.
|
|
183
|
+
*/
|
|
184
|
+
summarizeForCompaction(oldMessages: Message[], onDelta?: (full: string, delta: string) => void, abortSignal?: AbortSignal): Promise<string>;
|
|
167
185
|
runSubtask(input: string | ContentPart[], cwd: string, options?: {
|
|
168
186
|
subtaskType?: string;
|
|
169
187
|
description?: string;
|
|
@@ -186,6 +204,8 @@ export declare class Agent {
|
|
|
186
204
|
profile: AgentProfile;
|
|
187
205
|
parentToolCallId: string;
|
|
188
206
|
category?: string;
|
|
207
|
+
model?: string;
|
|
208
|
+
effort?: ThinkingLevel;
|
|
189
209
|
route?: ResolvedSubagentRoute;
|
|
190
210
|
approval?: "fail" | "disabled";
|
|
191
211
|
description?: string;
|
|
@@ -211,6 +231,8 @@ export declare class Agent {
|
|
|
211
231
|
runAgentTeam(cwd: string, options: {
|
|
212
232
|
profile: AgentProfile;
|
|
213
233
|
category?: string;
|
|
234
|
+
model?: string;
|
|
235
|
+
effort?: ThinkingLevel;
|
|
214
236
|
promptTemplate: string;
|
|
215
237
|
items: string[];
|
|
216
238
|
parentToolCallId: string;
|
|
@@ -218,6 +240,83 @@ export declare class Agent {
|
|
|
218
240
|
abortSignal?: AbortSignal;
|
|
219
241
|
approval?: "fail" | "disabled";
|
|
220
242
|
}): Promise<SubagentThreadSnapshot[]>;
|
|
243
|
+
/**
|
|
244
|
+
* Heterogeneous fan-out (design v2 §1.3): N independent specs, each with its
|
|
245
|
+
* own task, profile, and per-call model/effort, dispatched concurrently as a
|
|
246
|
+
* SINGLE tool call. Unlike runAgentTeam (one template over N items), members
|
|
247
|
+
* differ. Like the team, every member goes through the same scheduler
|
|
248
|
+
* dispatch and the tool blocks until all are final, returning in spec order.
|
|
249
|
+
* Keeping fan-out inside one tool call (rather than N parallel spawn_agent
|
|
250
|
+
* tool_calls) avoids the provider parallel-tool_call bug (Kimi 400 / lost
|
|
251
|
+
* responses).
|
|
252
|
+
*/
|
|
253
|
+
runAgentBatch(cwd: string, options: {
|
|
254
|
+
specs: Array<{
|
|
255
|
+
task: string;
|
|
256
|
+
profile: AgentProfile;
|
|
257
|
+
category?: string;
|
|
258
|
+
model?: string;
|
|
259
|
+
effort?: ThinkingLevel;
|
|
260
|
+
outputSchema?: unknown;
|
|
261
|
+
}>;
|
|
262
|
+
parentToolCallId: string;
|
|
263
|
+
emitUpdate?: (update: ToolUpdate) => void;
|
|
264
|
+
abortSignal?: AbortSignal;
|
|
265
|
+
approval?: "fail" | "disabled";
|
|
266
|
+
}): Promise<SubagentThreadSnapshot[]>;
|
|
267
|
+
/**
|
|
268
|
+
* Dynamic workflow (option C): runs an LLM-authored JS orchestration script in
|
|
269
|
+
* a QuickJS sandbox. Each agent() call in the script becomes a real scheduled
|
|
270
|
+
* subagent (same route resolution, ChildRunner, scheduler, schema validation
|
|
271
|
+
* as spawn_agent), so the script expresses deterministic control flow while
|
|
272
|
+
* the runtime keeps owning concurrency/budget/retry.
|
|
273
|
+
*
|
|
274
|
+
* Foreground entry point (used by `-p`/headless and tests): awaits to
|
|
275
|
+
* completion and returns the result. Background runs go through startWorkflow.
|
|
276
|
+
*/
|
|
277
|
+
runWorkflow(cwd: string, options: {
|
|
278
|
+
script: string;
|
|
279
|
+
args?: unknown;
|
|
280
|
+
parentToolCallId: string;
|
|
281
|
+
emitUpdate?: (update: ToolUpdate) => void;
|
|
282
|
+
abortSignal?: AbortSignal;
|
|
283
|
+
}): Promise<{
|
|
284
|
+
result: {
|
|
285
|
+
ok: true;
|
|
286
|
+
value: unknown;
|
|
287
|
+
} | {
|
|
288
|
+
ok: false;
|
|
289
|
+
error: string;
|
|
290
|
+
};
|
|
291
|
+
agentCount: number;
|
|
292
|
+
logs: string[];
|
|
293
|
+
snapshots: SubagentThreadSnapshot[];
|
|
294
|
+
}>;
|
|
295
|
+
/**
|
|
296
|
+
* Starts a workflow in the BACKGROUND (option C Phase 4): returns a runId
|
|
297
|
+
* immediately; the script runs detached, its agents stream progress through
|
|
298
|
+
* the queued channel (drained at turn boundaries like spawn_agent), and its
|
|
299
|
+
* result is ingested at the next turn. Collect explicitly with waitWorkflow.
|
|
300
|
+
*/
|
|
301
|
+
startWorkflow(cwd: string, options: {
|
|
302
|
+
script: string;
|
|
303
|
+
args?: unknown;
|
|
304
|
+
title?: string;
|
|
305
|
+
parentToolCallId: string;
|
|
306
|
+
abortSignal?: AbortSignal;
|
|
307
|
+
}): {
|
|
308
|
+
runId: string;
|
|
309
|
+
title: string;
|
|
310
|
+
};
|
|
311
|
+
/** Blocks until a background workflow reaches a final state (or times out). */
|
|
312
|
+
waitWorkflow(runId: string, timeoutMs?: number): Promise<WorkflowRunSnapshot | undefined>;
|
|
313
|
+
/** Cancels a running background workflow. */
|
|
314
|
+
closeWorkflow(runId: string): WorkflowRunSnapshot | undefined;
|
|
315
|
+
listWorkflows(): WorkflowRunSnapshot[];
|
|
316
|
+
private snapshotWorkflow;
|
|
317
|
+
/** Injects completed background-workflow results before the next turn (§5 analog). */
|
|
318
|
+
private flushWorkflowDeliveries;
|
|
319
|
+
private executeWorkflow;
|
|
221
320
|
/** Marks a child's full summary as delivered to parent context (design §3.3). */
|
|
222
321
|
markSubagentDelivered(agentId: string): void;
|
|
223
322
|
private snapshotSubagent;
|
|
@@ -234,6 +333,12 @@ export declare class Agent {
|
|
|
234
333
|
private dispatchSubagentRun;
|
|
235
334
|
private emitSubagentLifecycle;
|
|
236
335
|
private runSubagentLifecycleHookFor;
|
|
336
|
+
/**
|
|
337
|
+
* Resolves a child's model route. Priority, highest first (design v2 §1.1):
|
|
338
|
+
* call-site override (model/effort) > profile.model > category > inherit parent.
|
|
339
|
+
* The call-site override is what lets the model say "opus for this reviewer,
|
|
340
|
+
* haiku for these twenty scouts" per spawn/batch member at request time.
|
|
341
|
+
*/
|
|
237
342
|
private resolveRouteForSubagent;
|
|
238
343
|
private createSubagentThreadRecord;
|
|
239
344
|
private runSubagentThread;
|