@wrongstack/core 0.1.10 → 0.3.1
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/{agent-bridge-6KPqsFx6.d.ts → agent-bridge-C3DUGjSb.d.ts} +1 -1
- package/dist/{compactor-B4mQZXf2.d.ts → compactor-BUU6Zm_3.d.ts} +1 -1
- package/dist/{config-BU9f_5yH.d.ts → config-CKLYPkCi.d.ts} +1 -1
- package/dist/{context-BmM2xGUZ.d.ts → context-IovtuTf8.d.ts} +10 -0
- package/dist/coordination/index.d.ts +211 -13
- package/dist/coordination/index.js +964 -67
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +33 -18
- package/dist/defaults/index.js +1273 -42
- package/dist/defaults/index.js.map +1 -1
- package/dist/{events-BMNaEFZl.d.ts → events-CNB9PALO.d.ts} +99 -1
- package/dist/execution/index.d.ts +12 -12
- package/dist/extension/index.d.ts +9 -0
- package/dist/extension/index.js +234 -0
- package/dist/extension/index.js.map +1 -0
- package/dist/index-BDb0cAMP.d.ts +806 -0
- package/dist/index.d.ts +112 -29
- package/dist/index.js +2036 -490
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +6 -6
- package/dist/kernel/index.d.ts +12 -9
- package/dist/kernel/index.js +73 -7
- package/dist/kernel/index.js.map +1 -1
- package/dist/{mcp-servers-Dzgg4x1w.d.ts → mcp-servers-DR35ojJZ.d.ts} +3 -3
- package/dist/models/index.d.ts +2 -2
- package/dist/models/index.js +24 -1
- package/dist/models/index.js.map +1 -1
- package/dist/{multi-agent-fmkRHtof.d.ts → multi-agent-B9a6sflH.d.ts} +71 -3
- package/dist/observability/index.d.ts +2 -2
- package/dist/{path-resolver-DBjaoXFq.d.ts → path-resolver-Cl_q0u-R.d.ts} +2 -2
- package/dist/provider-runner-BXuADQqQ.d.ts +36 -0
- package/dist/sdd/index.d.ts +3 -3
- package/dist/{secret-scrubber-CicHLN4G.d.ts → secret-scrubber-CgG2tV2B.d.ts} +1 -1
- package/dist/{secret-scrubber-DF88luOe.d.ts → secret-scrubber-Cuy5afaQ.d.ts} +1 -1
- package/dist/security/index.d.ts +20 -4
- package/dist/security/index.js +37 -2
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-BbJqiEP4.d.ts → selector-wT2fv9Fg.d.ts} +1 -1
- package/dist/{session-reader-Drq8RvJu.d.ts → session-reader-CcPi4BQ8.d.ts} +1 -1
- package/dist/{skill-DhfSizKv.d.ts → skill-C_7znCIC.d.ts} +2 -2
- package/dist/storage/index.d.ts +164 -6
- package/dist/storage/index.js +297 -2
- package/dist/storage/index.js.map +1 -1
- package/dist/{renderer-rk_1Swwc.d.ts → system-prompt-Dk1qm8ey.d.ts} +30 -2
- package/dist/{tool-executor-CpuJPYm9.d.ts → tool-executor-DKu4A6nB.d.ts} +5 -5
- package/dist/types/index.d.ts +16 -16
- package/dist/types/index.js +24 -1
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +24 -1
- package/dist/utils/index.js.map +1 -1
- package/package.json +5 -1
- package/dist/plugin-DJk6LL8B.d.ts +0 -434
- package/dist/system-prompt-BC_8ypCG.d.ts +0 -23
package/dist/defaults/index.js
CHANGED
|
@@ -156,7 +156,7 @@ async function atomicWrite(targetPath, content, opts = {}) {
|
|
|
156
156
|
if (mode !== void 0) {
|
|
157
157
|
await fsp.chmod(tmp, mode);
|
|
158
158
|
}
|
|
159
|
-
await
|
|
159
|
+
await renameWithRetry(tmp, targetPath);
|
|
160
160
|
} catch (err) {
|
|
161
161
|
try {
|
|
162
162
|
await fsp.unlink(tmp);
|
|
@@ -168,6 +168,29 @@ async function atomicWrite(targetPath, content, opts = {}) {
|
|
|
168
168
|
async function ensureDir(dir) {
|
|
169
169
|
await fsp.mkdir(dir, { recursive: true });
|
|
170
170
|
}
|
|
171
|
+
var TRANSIENT_RENAME_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES", "ENOTEMPTY"]);
|
|
172
|
+
async function renameWithRetry(from, to) {
|
|
173
|
+
if (process.platform !== "win32") {
|
|
174
|
+
await fsp.rename(from, to);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const delays = [10, 25, 60, 120, 250];
|
|
178
|
+
let lastErr;
|
|
179
|
+
for (let i = 0; i <= delays.length; i++) {
|
|
180
|
+
try {
|
|
181
|
+
await fsp.rename(from, to);
|
|
182
|
+
return;
|
|
183
|
+
} catch (err) {
|
|
184
|
+
lastErr = err;
|
|
185
|
+
const code = err?.code;
|
|
186
|
+
if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {
|
|
187
|
+
throw err;
|
|
188
|
+
}
|
|
189
|
+
await new Promise((resolve2) => setTimeout(resolve2, delays[i]));
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
throw lastErr;
|
|
193
|
+
}
|
|
171
194
|
|
|
172
195
|
// src/storage/session-store.ts
|
|
173
196
|
var DefaultSessionStore = class {
|
|
@@ -406,6 +429,12 @@ var FileSessionWriter = class {
|
|
|
406
429
|
tokenIn = 0;
|
|
407
430
|
tokenOut = 0;
|
|
408
431
|
filePath;
|
|
432
|
+
/** Public accessor for the JSONL path — required by SessionWriter so
|
|
433
|
+
* observability surfaces (`/fleet log`, FleetPanel) can locate the
|
|
434
|
+
* transcript without recomputing the path from session metadata. */
|
|
435
|
+
get transcriptPath() {
|
|
436
|
+
return this.filePath || void 0;
|
|
437
|
+
}
|
|
409
438
|
initDone = false;
|
|
410
439
|
resumed;
|
|
411
440
|
appendFailCount = 0;
|
|
@@ -1796,6 +1825,272 @@ var SessionAnalyzer = class {
|
|
|
1796
1825
|
return last - first;
|
|
1797
1826
|
}
|
|
1798
1827
|
};
|
|
1828
|
+
async function loadTodosCheckpoint(filePath) {
|
|
1829
|
+
let raw;
|
|
1830
|
+
try {
|
|
1831
|
+
raw = await fsp.readFile(filePath, "utf8");
|
|
1832
|
+
} catch {
|
|
1833
|
+
return null;
|
|
1834
|
+
}
|
|
1835
|
+
try {
|
|
1836
|
+
const parsed = JSON.parse(raw);
|
|
1837
|
+
if (parsed?.version !== 1 || !Array.isArray(parsed.todos)) return null;
|
|
1838
|
+
return parsed.todos.filter(
|
|
1839
|
+
(t) => !!t && typeof t.id === "string" && typeof t.content === "string" && typeof t.status === "string"
|
|
1840
|
+
);
|
|
1841
|
+
} catch {
|
|
1842
|
+
return null;
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
async function saveTodosCheckpoint(filePath, sessionId, todos) {
|
|
1846
|
+
const payload = {
|
|
1847
|
+
version: 1,
|
|
1848
|
+
sessionId,
|
|
1849
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1850
|
+
todos: [...todos]
|
|
1851
|
+
};
|
|
1852
|
+
try {
|
|
1853
|
+
await atomicWrite(filePath, JSON.stringify(payload, null, 2), { mode: 384 });
|
|
1854
|
+
} catch (err) {
|
|
1855
|
+
console.warn(
|
|
1856
|
+
"[todos-checkpoint] save failed:",
|
|
1857
|
+
err instanceof Error ? err.message : String(err)
|
|
1858
|
+
);
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
function attachTodosCheckpoint(state, filePath, sessionId) {
|
|
1862
|
+
let timer = null;
|
|
1863
|
+
let pending = null;
|
|
1864
|
+
const flush = () => {
|
|
1865
|
+
timer = null;
|
|
1866
|
+
if (pending) {
|
|
1867
|
+
void saveTodosCheckpoint(filePath, sessionId, pending);
|
|
1868
|
+
pending = null;
|
|
1869
|
+
}
|
|
1870
|
+
};
|
|
1871
|
+
const unsubscribe = state.onChange((change) => {
|
|
1872
|
+
if (change.kind !== "todos_replaced") return;
|
|
1873
|
+
pending = change.todos;
|
|
1874
|
+
if (timer) clearTimeout(timer);
|
|
1875
|
+
timer = setTimeout(flush, 150);
|
|
1876
|
+
});
|
|
1877
|
+
return () => {
|
|
1878
|
+
unsubscribe();
|
|
1879
|
+
if (timer) {
|
|
1880
|
+
clearTimeout(timer);
|
|
1881
|
+
flush();
|
|
1882
|
+
}
|
|
1883
|
+
};
|
|
1884
|
+
}
|
|
1885
|
+
async function loadPlan(filePath) {
|
|
1886
|
+
let raw;
|
|
1887
|
+
try {
|
|
1888
|
+
raw = await fsp.readFile(filePath, "utf8");
|
|
1889
|
+
} catch {
|
|
1890
|
+
return null;
|
|
1891
|
+
}
|
|
1892
|
+
try {
|
|
1893
|
+
const parsed = JSON.parse(raw);
|
|
1894
|
+
if (parsed?.version !== 1 || !Array.isArray(parsed.items)) return null;
|
|
1895
|
+
return parsed;
|
|
1896
|
+
} catch {
|
|
1897
|
+
return null;
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
async function savePlan(filePath, plan) {
|
|
1901
|
+
try {
|
|
1902
|
+
await atomicWrite(filePath, JSON.stringify(plan, null, 2), { mode: 384 });
|
|
1903
|
+
} catch (err) {
|
|
1904
|
+
console.warn(
|
|
1905
|
+
"[plan-store] save failed:",
|
|
1906
|
+
err instanceof Error ? err.message : String(err)
|
|
1907
|
+
);
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
function emptyPlan(sessionId, title) {
|
|
1911
|
+
return {
|
|
1912
|
+
version: 1,
|
|
1913
|
+
sessionId,
|
|
1914
|
+
title,
|
|
1915
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1916
|
+
items: []
|
|
1917
|
+
};
|
|
1918
|
+
}
|
|
1919
|
+
function addPlanItem(plan, title, details) {
|
|
1920
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1921
|
+
const item = {
|
|
1922
|
+
id: `plan_${Date.now()}_${randomUUID().slice(0, 6)}`,
|
|
1923
|
+
title,
|
|
1924
|
+
details,
|
|
1925
|
+
status: "open",
|
|
1926
|
+
createdAt: now,
|
|
1927
|
+
updatedAt: now
|
|
1928
|
+
};
|
|
1929
|
+
return {
|
|
1930
|
+
plan: { ...plan, items: [...plan.items, item], updatedAt: now },
|
|
1931
|
+
item
|
|
1932
|
+
};
|
|
1933
|
+
}
|
|
1934
|
+
function removePlanItem(plan, idOrIndex) {
|
|
1935
|
+
const idx = matchIndex(plan, idOrIndex);
|
|
1936
|
+
if (idx === -1) return plan;
|
|
1937
|
+
return {
|
|
1938
|
+
...plan,
|
|
1939
|
+
items: plan.items.filter((_, i) => i !== idx),
|
|
1940
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1941
|
+
};
|
|
1942
|
+
}
|
|
1943
|
+
function setPlanItemStatus(plan, idOrIndex, status) {
|
|
1944
|
+
const idx = matchIndex(plan, idOrIndex);
|
|
1945
|
+
if (idx === -1) return plan;
|
|
1946
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1947
|
+
const items = plan.items.map(
|
|
1948
|
+
(it, i) => i === idx ? { ...it, status, updatedAt: now } : it
|
|
1949
|
+
);
|
|
1950
|
+
return { ...plan, items, updatedAt: now };
|
|
1951
|
+
}
|
|
1952
|
+
function clearPlan(plan) {
|
|
1953
|
+
return { ...plan, items: [], updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
1954
|
+
}
|
|
1955
|
+
function formatPlan(plan) {
|
|
1956
|
+
if (plan.items.length === 0) return "Plan is empty.";
|
|
1957
|
+
const lines = [];
|
|
1958
|
+
if (plan.title) lines.push(`# ${plan.title}`);
|
|
1959
|
+
plan.items.forEach((it, i) => {
|
|
1960
|
+
const mark = it.status === "done" ? "[x]" : it.status === "in_progress" ? "[~]" : "[ ]";
|
|
1961
|
+
lines.push(`${i + 1}. ${mark} ${it.title}`);
|
|
1962
|
+
if (it.details) {
|
|
1963
|
+
for (const line of it.details.split("\n")) lines.push(` ${line}`);
|
|
1964
|
+
}
|
|
1965
|
+
});
|
|
1966
|
+
return lines.join("\n");
|
|
1967
|
+
}
|
|
1968
|
+
function matchIndex(plan, idOrIndex) {
|
|
1969
|
+
const asNum = Number.parseInt(idOrIndex, 10);
|
|
1970
|
+
if (!Number.isNaN(asNum) && asNum >= 1 && asNum <= plan.items.length) return asNum - 1;
|
|
1971
|
+
const byId = plan.items.findIndex((it) => it.id === idOrIndex);
|
|
1972
|
+
if (byId !== -1) return byId;
|
|
1973
|
+
const lower = idOrIndex.toLowerCase();
|
|
1974
|
+
return plan.items.findIndex((it) => it.title.toLowerCase().includes(lower));
|
|
1975
|
+
}
|
|
1976
|
+
function attachPlanCheckpoint(_state, _filePath, _sessionId) {
|
|
1977
|
+
return () => void 0;
|
|
1978
|
+
}
|
|
1979
|
+
async function loadDirectorState(filePath) {
|
|
1980
|
+
let raw;
|
|
1981
|
+
try {
|
|
1982
|
+
raw = await fsp.readFile(filePath, "utf8");
|
|
1983
|
+
} catch {
|
|
1984
|
+
return null;
|
|
1985
|
+
}
|
|
1986
|
+
try {
|
|
1987
|
+
const parsed = JSON.parse(raw);
|
|
1988
|
+
if (parsed?.version !== 1) return null;
|
|
1989
|
+
return parsed;
|
|
1990
|
+
} catch {
|
|
1991
|
+
return null;
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
var DirectorStateCheckpoint = class {
|
|
1995
|
+
snapshot;
|
|
1996
|
+
filePath;
|
|
1997
|
+
timer = null;
|
|
1998
|
+
debounceMs;
|
|
1999
|
+
writing = false;
|
|
2000
|
+
rewriteRequested = false;
|
|
2001
|
+
constructor(filePath, init, debounceMs = 250) {
|
|
2002
|
+
this.filePath = filePath;
|
|
2003
|
+
this.debounceMs = debounceMs;
|
|
2004
|
+
this.snapshot = {
|
|
2005
|
+
version: 1,
|
|
2006
|
+
directorRunId: init.directorRunId,
|
|
2007
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2008
|
+
spawnCount: 0,
|
|
2009
|
+
maxSpawns: init.maxSpawns,
|
|
2010
|
+
spawnDepth: init.spawnDepth,
|
|
2011
|
+
maxSpawnDepth: init.maxSpawnDepth,
|
|
2012
|
+
subagents: [],
|
|
2013
|
+
tasks: []
|
|
2014
|
+
};
|
|
2015
|
+
}
|
|
2016
|
+
current() {
|
|
2017
|
+
return this.snapshot;
|
|
2018
|
+
}
|
|
2019
|
+
recordSpawn(sub, spawnCount) {
|
|
2020
|
+
this.snapshot = {
|
|
2021
|
+
...this.snapshot,
|
|
2022
|
+
spawnCount,
|
|
2023
|
+
subagents: [...this.snapshot.subagents.filter((s) => s.id !== sub.id), sub]
|
|
2024
|
+
};
|
|
2025
|
+
this.bumpUpdatedAt();
|
|
2026
|
+
this.schedule();
|
|
2027
|
+
}
|
|
2028
|
+
recordTaskAssigned(task) {
|
|
2029
|
+
const exists = this.snapshot.tasks.some((t) => t.taskId === task.taskId);
|
|
2030
|
+
this.snapshot = {
|
|
2031
|
+
...this.snapshot,
|
|
2032
|
+
tasks: exists ? this.snapshot.tasks.map((t) => t.taskId === task.taskId ? { ...t, ...task } : t) : [...this.snapshot.tasks, task]
|
|
2033
|
+
};
|
|
2034
|
+
this.bumpUpdatedAt();
|
|
2035
|
+
this.schedule();
|
|
2036
|
+
}
|
|
2037
|
+
recordTaskStatus(taskId, patch) {
|
|
2038
|
+
this.snapshot = {
|
|
2039
|
+
...this.snapshot,
|
|
2040
|
+
tasks: this.snapshot.tasks.map(
|
|
2041
|
+
(t) => t.taskId === taskId ? { ...t, ...patch } : t
|
|
2042
|
+
)
|
|
2043
|
+
};
|
|
2044
|
+
this.bumpUpdatedAt();
|
|
2045
|
+
this.schedule();
|
|
2046
|
+
}
|
|
2047
|
+
setUsage(usage) {
|
|
2048
|
+
this.snapshot = { ...this.snapshot, usage };
|
|
2049
|
+
this.bumpUpdatedAt();
|
|
2050
|
+
this.schedule();
|
|
2051
|
+
}
|
|
2052
|
+
/** Force a synchronous flush — used by Director.shutdown(). */
|
|
2053
|
+
async flush() {
|
|
2054
|
+
if (this.timer) {
|
|
2055
|
+
clearTimeout(this.timer);
|
|
2056
|
+
this.timer = null;
|
|
2057
|
+
}
|
|
2058
|
+
await this.persist();
|
|
2059
|
+
}
|
|
2060
|
+
bumpUpdatedAt() {
|
|
2061
|
+
this.snapshot = { ...this.snapshot, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
2062
|
+
}
|
|
2063
|
+
schedule() {
|
|
2064
|
+
if (this.timer) return;
|
|
2065
|
+
this.timer = setTimeout(() => {
|
|
2066
|
+
this.timer = null;
|
|
2067
|
+
void this.persist();
|
|
2068
|
+
}, this.debounceMs);
|
|
2069
|
+
}
|
|
2070
|
+
async persist() {
|
|
2071
|
+
if (this.writing) {
|
|
2072
|
+
this.rewriteRequested = true;
|
|
2073
|
+
return;
|
|
2074
|
+
}
|
|
2075
|
+
this.writing = true;
|
|
2076
|
+
try {
|
|
2077
|
+
await atomicWrite(this.filePath, JSON.stringify(this.snapshot, null, 2), {
|
|
2078
|
+
mode: 384
|
|
2079
|
+
});
|
|
2080
|
+
} catch (err) {
|
|
2081
|
+
console.warn(
|
|
2082
|
+
"[director-state] checkpoint write failed:",
|
|
2083
|
+
err instanceof Error ? err.message : String(err)
|
|
2084
|
+
);
|
|
2085
|
+
} finally {
|
|
2086
|
+
this.writing = false;
|
|
2087
|
+
if (this.rewriteRequested) {
|
|
2088
|
+
this.rewriteRequested = false;
|
|
2089
|
+
this.schedule();
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
};
|
|
1799
2094
|
|
|
1800
2095
|
// src/security/secret-scrubber.ts
|
|
1801
2096
|
var PATTERNS = [
|
|
@@ -2071,6 +2366,18 @@ var DefaultPermissionPolicy = class {
|
|
|
2071
2366
|
return void 0;
|
|
2072
2367
|
}
|
|
2073
2368
|
};
|
|
2369
|
+
var AutoApprovePermissionPolicy = class {
|
|
2370
|
+
async evaluate(tool) {
|
|
2371
|
+
if (tool.permission === "deny") {
|
|
2372
|
+
return { permission: "deny", source: "default", reason: "tool default deny" };
|
|
2373
|
+
}
|
|
2374
|
+
return { permission: "auto", source: "yolo" };
|
|
2375
|
+
}
|
|
2376
|
+
async trust() {
|
|
2377
|
+
}
|
|
2378
|
+
async reload() {
|
|
2379
|
+
}
|
|
2380
|
+
};
|
|
2074
2381
|
|
|
2075
2382
|
// src/types/errors.ts
|
|
2076
2383
|
var WrongStackError = class extends Error {
|
|
@@ -2434,6 +2741,302 @@ function parseDescription(raw) {
|
|
|
2434
2741
|
return { trigger, scope };
|
|
2435
2742
|
}
|
|
2436
2743
|
|
|
2744
|
+
// src/core/streaming-response-builder.ts
|
|
2745
|
+
function buildResponse(state) {
|
|
2746
|
+
const content = [];
|
|
2747
|
+
for (const b of state.blockOrder) {
|
|
2748
|
+
if (b.kind === "text") {
|
|
2749
|
+
const txt = state.textBuffers[b.idx] ?? "";
|
|
2750
|
+
if (txt) content.push({ type: "text", text: txt });
|
|
2751
|
+
} else if (b.kind === "thinking") {
|
|
2752
|
+
const t = state.thinking[b.idx];
|
|
2753
|
+
if (!t) continue;
|
|
2754
|
+
if (!t.textBuf && !t.signature) continue;
|
|
2755
|
+
const block = { type: "thinking", thinking: t.textBuf };
|
|
2756
|
+
if (t.signature) block.signature = t.signature;
|
|
2757
|
+
if (t.providerMeta && Object.keys(t.providerMeta).length > 0) {
|
|
2758
|
+
block.providerMeta = t.providerMeta;
|
|
2759
|
+
}
|
|
2760
|
+
content.push(block);
|
|
2761
|
+
} else {
|
|
2762
|
+
const tb = state.tools.get(b.id);
|
|
2763
|
+
if (tb) {
|
|
2764
|
+
const block = {
|
|
2765
|
+
type: "tool_use",
|
|
2766
|
+
id: b.id,
|
|
2767
|
+
name: tb.name,
|
|
2768
|
+
input: tb.input ?? {}
|
|
2769
|
+
};
|
|
2770
|
+
if (tb.providerMeta && Object.keys(tb.providerMeta).length > 0) {
|
|
2771
|
+
block.providerMeta = tb.providerMeta;
|
|
2772
|
+
}
|
|
2773
|
+
content.push(block);
|
|
2774
|
+
}
|
|
2775
|
+
}
|
|
2776
|
+
}
|
|
2777
|
+
if (content.length === 0) content.push({ type: "text", text: "" });
|
|
2778
|
+
return { content, stopReason: state.stopReason, usage: state.usage, model: state.model };
|
|
2779
|
+
}
|
|
2780
|
+
function createStreamingState(model) {
|
|
2781
|
+
return {
|
|
2782
|
+
model,
|
|
2783
|
+
stopReason: "end_turn",
|
|
2784
|
+
usage: { input: 0, output: 0 },
|
|
2785
|
+
textBuffers: [],
|
|
2786
|
+
currentTextIndex: -1,
|
|
2787
|
+
tools: /* @__PURE__ */ new Map(),
|
|
2788
|
+
thinking: [],
|
|
2789
|
+
currentThinkingIndex: -1,
|
|
2790
|
+
blockOrder: []
|
|
2791
|
+
};
|
|
2792
|
+
}
|
|
2793
|
+
function handleMessageStart(state, model) {
|
|
2794
|
+
state.model = model;
|
|
2795
|
+
}
|
|
2796
|
+
function handleContentBlockStart(state, ev) {
|
|
2797
|
+
const kind = ev.kind ?? "text";
|
|
2798
|
+
if (kind === "text") {
|
|
2799
|
+
state.currentTextIndex = state.textBuffers.length;
|
|
2800
|
+
state.textBuffers.push("");
|
|
2801
|
+
state.blockOrder.push({ kind: "text", idx: state.currentTextIndex });
|
|
2802
|
+
} else if (kind === "tool_use") {
|
|
2803
|
+
const id = ev.id ?? crypto.randomUUID();
|
|
2804
|
+
state.tools.set(id, { name: ev.name ?? "unknown", partial: "" });
|
|
2805
|
+
state.blockOrder.push({ kind: "tool", id });
|
|
2806
|
+
state.currentTextIndex = -1;
|
|
2807
|
+
} else if (kind === "thinking") {
|
|
2808
|
+
state.currentThinkingIndex = state.thinking.length;
|
|
2809
|
+
state.thinking.push({
|
|
2810
|
+
textBuf: "",
|
|
2811
|
+
...ev.providerMeta ? { providerMeta: ev.providerMeta } : {}
|
|
2812
|
+
});
|
|
2813
|
+
state.blockOrder.push({ kind: "thinking", idx: state.currentThinkingIndex });
|
|
2814
|
+
state.currentTextIndex = -1;
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
function handleContentBlockStop(state, ev) {
|
|
2818
|
+
}
|
|
2819
|
+
function handleTextDelta(state, text) {
|
|
2820
|
+
if (state.currentTextIndex === -1) {
|
|
2821
|
+
state.currentTextIndex = state.textBuffers.length;
|
|
2822
|
+
state.textBuffers.push("");
|
|
2823
|
+
state.blockOrder.push({ kind: "text", idx: state.currentTextIndex });
|
|
2824
|
+
}
|
|
2825
|
+
state.textBuffers[state.currentTextIndex] = (state.textBuffers[state.currentTextIndex] ?? "") + text;
|
|
2826
|
+
}
|
|
2827
|
+
function handleToolUseStart(state, ev) {
|
|
2828
|
+
state.currentTextIndex = -1;
|
|
2829
|
+
state.tools.set(ev.id, { name: ev.name, partial: "" });
|
|
2830
|
+
state.blockOrder.push({ kind: "tool", id: ev.id });
|
|
2831
|
+
}
|
|
2832
|
+
function handleToolUseInputDelta(state, ev) {
|
|
2833
|
+
const t = state.tools.get(ev.id);
|
|
2834
|
+
if (t) t.partial += ev.partial;
|
|
2835
|
+
}
|
|
2836
|
+
function safeJsonOrRaw(s) {
|
|
2837
|
+
if (!s) return {};
|
|
2838
|
+
try {
|
|
2839
|
+
return JSON.parse(s);
|
|
2840
|
+
} catch {
|
|
2841
|
+
return { _raw: s };
|
|
2842
|
+
}
|
|
2843
|
+
}
|
|
2844
|
+
function handleToolUseStop(state, ev) {
|
|
2845
|
+
const t = state.tools.get(ev.id);
|
|
2846
|
+
if (t) {
|
|
2847
|
+
t.input = ev.input !== void 0 ? ev.input : safeJsonOrRaw(t.partial);
|
|
2848
|
+
if (ev.providerMeta) t.providerMeta = ev.providerMeta;
|
|
2849
|
+
}
|
|
2850
|
+
state.currentTextIndex = -1;
|
|
2851
|
+
}
|
|
2852
|
+
function handleThinkingStart(state, ev) {
|
|
2853
|
+
state.currentThinkingIndex = state.thinking.length;
|
|
2854
|
+
state.thinking.push({
|
|
2855
|
+
textBuf: "",
|
|
2856
|
+
...ev.providerMeta ? { providerMeta: ev.providerMeta } : {}
|
|
2857
|
+
});
|
|
2858
|
+
state.blockOrder.push({ kind: "thinking", idx: state.currentThinkingIndex });
|
|
2859
|
+
state.currentTextIndex = -1;
|
|
2860
|
+
}
|
|
2861
|
+
function handleThinkingDelta(state, text) {
|
|
2862
|
+
if (state.currentThinkingIndex === -1) {
|
|
2863
|
+
handleThinkingStart(state, {});
|
|
2864
|
+
}
|
|
2865
|
+
const t = state.thinking[state.currentThinkingIndex];
|
|
2866
|
+
if (t) t.textBuf += text;
|
|
2867
|
+
}
|
|
2868
|
+
function handleThinkingSignature(state, signature) {
|
|
2869
|
+
if (state.currentThinkingIndex === -1) {
|
|
2870
|
+
handleThinkingStart(state, {});
|
|
2871
|
+
}
|
|
2872
|
+
const t = state.thinking[state.currentThinkingIndex];
|
|
2873
|
+
if (t) t.signature = signature;
|
|
2874
|
+
}
|
|
2875
|
+
function handleThinkingStop(state) {
|
|
2876
|
+
state.currentThinkingIndex = -1;
|
|
2877
|
+
}
|
|
2878
|
+
function handleMessageStop(state, ev) {
|
|
2879
|
+
state.stopReason = ev.stopReason ?? "end_turn";
|
|
2880
|
+
state.usage = ev.usage ?? { input: 0, output: 0 };
|
|
2881
|
+
}
|
|
2882
|
+
async function streamProviderToResponse(provider, req, signal, ctx, events) {
|
|
2883
|
+
const state = createStreamingState(req.model);
|
|
2884
|
+
const iter = provider.stream(req, { signal })[Symbol.asyncIterator]();
|
|
2885
|
+
try {
|
|
2886
|
+
for (; ; ) {
|
|
2887
|
+
const next = await iter.next();
|
|
2888
|
+
if (next.done) break;
|
|
2889
|
+
const ev = next.value;
|
|
2890
|
+
switch (ev.type) {
|
|
2891
|
+
case "message_start":
|
|
2892
|
+
handleMessageStart(state, ev.model);
|
|
2893
|
+
break;
|
|
2894
|
+
case "content_block_start":
|
|
2895
|
+
handleContentBlockStart(state, ev);
|
|
2896
|
+
break;
|
|
2897
|
+
case "content_block_stop":
|
|
2898
|
+
handleContentBlockStop(state, ev);
|
|
2899
|
+
break;
|
|
2900
|
+
case "text_delta":
|
|
2901
|
+
handleTextDelta(state, ev.text);
|
|
2902
|
+
events.emit("provider.text_delta", { ctx, text: ev.text });
|
|
2903
|
+
break;
|
|
2904
|
+
case "tool_use_start":
|
|
2905
|
+
handleToolUseStart(state, ev);
|
|
2906
|
+
events.emit("provider.tool_use_start", { ctx, id: ev.id, name: ev.name });
|
|
2907
|
+
break;
|
|
2908
|
+
case "tool_use_input_delta":
|
|
2909
|
+
handleToolUseInputDelta(state, ev);
|
|
2910
|
+
break;
|
|
2911
|
+
case "tool_use_stop":
|
|
2912
|
+
handleToolUseStop(state, ev);
|
|
2913
|
+
events.emit("provider.tool_use_stop", { ctx, id: ev.id });
|
|
2914
|
+
break;
|
|
2915
|
+
case "thinking_start":
|
|
2916
|
+
handleThinkingStart(state, ev);
|
|
2917
|
+
break;
|
|
2918
|
+
case "thinking_delta":
|
|
2919
|
+
handleThinkingDelta(state, ev.text);
|
|
2920
|
+
events.emit("provider.thinking_delta", { ctx, text: ev.text });
|
|
2921
|
+
break;
|
|
2922
|
+
case "thinking_signature":
|
|
2923
|
+
handleThinkingSignature(state, ev.signature);
|
|
2924
|
+
break;
|
|
2925
|
+
case "thinking_stop":
|
|
2926
|
+
handleThinkingStop(state);
|
|
2927
|
+
break;
|
|
2928
|
+
case "message_stop":
|
|
2929
|
+
handleMessageStop(state, ev);
|
|
2930
|
+
break;
|
|
2931
|
+
}
|
|
2932
|
+
}
|
|
2933
|
+
} catch (err) {
|
|
2934
|
+
if (signal.aborted) {
|
|
2935
|
+
state.stopReason = "end_turn";
|
|
2936
|
+
return buildResponse(state);
|
|
2937
|
+
}
|
|
2938
|
+
throw err;
|
|
2939
|
+
} finally {
|
|
2940
|
+
try {
|
|
2941
|
+
let drainTimer = null;
|
|
2942
|
+
try {
|
|
2943
|
+
await Promise.race([
|
|
2944
|
+
Promise.resolve(iter.return?.()),
|
|
2945
|
+
new Promise((resolve2) => {
|
|
2946
|
+
drainTimer = setTimeout(resolve2, 500);
|
|
2947
|
+
})
|
|
2948
|
+
]);
|
|
2949
|
+
} finally {
|
|
2950
|
+
if (drainTimer) clearTimeout(drainTimer);
|
|
2951
|
+
}
|
|
2952
|
+
} catch {
|
|
2953
|
+
}
|
|
2954
|
+
}
|
|
2955
|
+
return buildResponse(state);
|
|
2956
|
+
}
|
|
2957
|
+
|
|
2958
|
+
// src/core/provider-runner.ts
|
|
2959
|
+
async function runProviderWithRetry(opts) {
|
|
2960
|
+
const { provider, request, signal, ctx, events, retry, logger, tracer } = opts;
|
|
2961
|
+
let attempt = 0;
|
|
2962
|
+
for (; ; ) {
|
|
2963
|
+
const span = tracer?.startSpan("provider.complete", {
|
|
2964
|
+
"provider.id": provider.id,
|
|
2965
|
+
"provider.model": request.model,
|
|
2966
|
+
"provider.streaming": provider.capabilities.streaming,
|
|
2967
|
+
"provider.attempt": attempt
|
|
2968
|
+
});
|
|
2969
|
+
try {
|
|
2970
|
+
const res = provider.capabilities.streaming ? await streamProviderToResponse(provider, request, signal, ctx, events) : await provider.complete(request, { signal });
|
|
2971
|
+
span?.setAttribute("provider.stopReason", res.stopReason);
|
|
2972
|
+
span?.setAttribute("provider.usage_in", res.usage.input);
|
|
2973
|
+
span?.setAttribute("provider.usage_out", res.usage.output);
|
|
2974
|
+
span?.end();
|
|
2975
|
+
return res;
|
|
2976
|
+
} catch (err) {
|
|
2977
|
+
if (err instanceof Error) span?.recordError(err);
|
|
2978
|
+
span?.end();
|
|
2979
|
+
if (signal.aborted) throw err;
|
|
2980
|
+
const isProviderErr = err instanceof ProviderError;
|
|
2981
|
+
const errAsErr = err instanceof Error ? err : new Error(String(err));
|
|
2982
|
+
const canRetry = retry.shouldRetry(isProviderErr ? err : errAsErr, attempt);
|
|
2983
|
+
const description = isProviderErr ? err.describe() : errAsErr.message;
|
|
2984
|
+
if (!canRetry) {
|
|
2985
|
+
if (isProviderErr) {
|
|
2986
|
+
events.emit("provider.error", {
|
|
2987
|
+
providerId: err.providerId,
|
|
2988
|
+
status: err.status,
|
|
2989
|
+
description,
|
|
2990
|
+
retryable: false
|
|
2991
|
+
});
|
|
2992
|
+
}
|
|
2993
|
+
throw err;
|
|
2994
|
+
}
|
|
2995
|
+
const delay = Math.round(retry.delayMs(attempt));
|
|
2996
|
+
const attemptNum = attempt + 1;
|
|
2997
|
+
logger.warn(`Provider retry ${attemptNum} in ${delay}ms \u2014 ${description}`);
|
|
2998
|
+
if (isProviderErr) {
|
|
2999
|
+
events.emit("provider.retry", {
|
|
3000
|
+
providerId: err.providerId,
|
|
3001
|
+
attempt: attemptNum,
|
|
3002
|
+
delayMs: delay,
|
|
3003
|
+
status: err.status,
|
|
3004
|
+
description
|
|
3005
|
+
});
|
|
3006
|
+
}
|
|
3007
|
+
await new Promise((resolve2, reject) => {
|
|
3008
|
+
let settled = false;
|
|
3009
|
+
const onAbort = () => {
|
|
3010
|
+
if (settled) return;
|
|
3011
|
+
settled = true;
|
|
3012
|
+
clearTimeout(t);
|
|
3013
|
+
reject(new Error("aborted"));
|
|
3014
|
+
};
|
|
3015
|
+
const t = setTimeout(() => {
|
|
3016
|
+
if (settled) return;
|
|
3017
|
+
settled = true;
|
|
3018
|
+
clearTimeout(t);
|
|
3019
|
+
signal.removeEventListener("abort", onAbort);
|
|
3020
|
+
resolve2();
|
|
3021
|
+
}, delay);
|
|
3022
|
+
if (signal.aborted) {
|
|
3023
|
+
onAbort();
|
|
3024
|
+
return;
|
|
3025
|
+
}
|
|
3026
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
3027
|
+
});
|
|
3028
|
+
attempt++;
|
|
3029
|
+
}
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
3032
|
+
|
|
3033
|
+
// src/execution/provider-runner-impl.ts
|
|
3034
|
+
var DefaultProviderRunner = class {
|
|
3035
|
+
async run(opts) {
|
|
3036
|
+
return runProviderWithRetry(opts);
|
|
3037
|
+
}
|
|
3038
|
+
};
|
|
3039
|
+
|
|
2437
3040
|
// src/utils/token-estimate.ts
|
|
2438
3041
|
var RoughTokenEstimate = (text) => Math.max(1, Math.ceil(text.length / 4));
|
|
2439
3042
|
var ESTIMATE_CACHE = /* @__PURE__ */ new Map();
|
|
@@ -3946,6 +4549,10 @@ var FleetBus = class {
|
|
|
3946
4549
|
"iteration.started",
|
|
3947
4550
|
"iteration.completed",
|
|
3948
4551
|
"provider.text_delta",
|
|
4552
|
+
// Subagent extended-thinking output. Forwarded so the FleetPanel /
|
|
4553
|
+
// /fleet log can surface "the planner is thinking…" instead of a
|
|
4554
|
+
// silent gap between iteration.started and the first text_delta.
|
|
4555
|
+
"provider.thinking_delta",
|
|
3949
4556
|
"provider.response",
|
|
3950
4557
|
"provider.retry",
|
|
3951
4558
|
"provider.error",
|
|
@@ -4194,14 +4801,34 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
4194
4801
|
completedResults = [];
|
|
4195
4802
|
totalIterations = 0;
|
|
4196
4803
|
inFlight = 0;
|
|
4804
|
+
/**
|
|
4805
|
+
* Subagents currently being stopped. Set on entry to `stop()`, cleared
|
|
4806
|
+
* once `recordCompletion` lands the terminal TaskResult. Used by
|
|
4807
|
+
* `runDispatched` and `findIdleSubagent` to refuse mid-flight dispatch
|
|
4808
|
+
* to a subagent the caller has already asked to terminate — closes the
|
|
4809
|
+
* assign+terminate race where a fresh task could land on a worker that
|
|
4810
|
+
* was about to be killed.
|
|
4811
|
+
*/
|
|
4812
|
+
terminating = /* @__PURE__ */ new Set();
|
|
4197
4813
|
constructor(config, options = {}) {
|
|
4198
4814
|
super();
|
|
4199
4815
|
this.coordinatorId = config.coordinatorId;
|
|
4200
4816
|
this.config = config;
|
|
4201
4817
|
this.runner = options.runner;
|
|
4202
4818
|
}
|
|
4819
|
+
/**
|
|
4820
|
+
* Replace the runner after construction. Used when the runner depends
|
|
4821
|
+
* on infrastructure (e.g. FleetBus) that isn't available until after
|
|
4822
|
+
* the coordinator's owning Director is built.
|
|
4823
|
+
*/
|
|
4824
|
+
setRunner(runner) {
|
|
4825
|
+
this.runner = runner;
|
|
4826
|
+
}
|
|
4203
4827
|
async spawn(subagent) {
|
|
4204
4828
|
const id = subagent.id || randomUUID();
|
|
4829
|
+
if (this.subagents.has(id)) {
|
|
4830
|
+
throw new Error(`Subagent id "${id}" already exists \u2014 refusing to overwrite`);
|
|
4831
|
+
}
|
|
4205
4832
|
const context = {
|
|
4206
4833
|
subagentId: id,
|
|
4207
4834
|
tasks: [],
|
|
@@ -4245,6 +4872,7 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
4245
4872
|
async stop(subagentId) {
|
|
4246
4873
|
const subagent = this.subagents.get(subagentId);
|
|
4247
4874
|
if (!subagent) return;
|
|
4875
|
+
this.terminating.add(subagentId);
|
|
4248
4876
|
subagent.abortController.abort();
|
|
4249
4877
|
subagent.status = "stopped";
|
|
4250
4878
|
subagent.currentTask = void 0;
|
|
@@ -4252,6 +4880,7 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
4252
4880
|
this.emit("subagent.stopped", { subagentId, reason: "stopped by coordinator" });
|
|
4253
4881
|
}
|
|
4254
4882
|
async stopAll() {
|
|
4883
|
+
this.drainPendingAsAborted("Coordinator stopAll() drained the pending queue");
|
|
4255
4884
|
await Promise.allSettled([...this.subagents.keys()].map((id) => this.stop(id)));
|
|
4256
4885
|
}
|
|
4257
4886
|
getStatus() {
|
|
@@ -4285,7 +4914,14 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
4285
4914
|
tryDispatchNext() {
|
|
4286
4915
|
while (this.canDispatch()) {
|
|
4287
4916
|
const subagentId = this.findIdleSubagent();
|
|
4288
|
-
if (!subagentId)
|
|
4917
|
+
if (!subagentId) {
|
|
4918
|
+
if (this.pendingTasks.length > 0 && !this.hasLiveSubagent()) {
|
|
4919
|
+
this.drainPendingAsAborted(
|
|
4920
|
+
"No live subagent available \u2014 all stopped or mid-termination"
|
|
4921
|
+
);
|
|
4922
|
+
}
|
|
4923
|
+
return;
|
|
4924
|
+
}
|
|
4289
4925
|
const task = this.pendingTasks.shift();
|
|
4290
4926
|
if (!task) return;
|
|
4291
4927
|
this.runDispatched(subagentId, task).catch((err) => {
|
|
@@ -4293,7 +4929,7 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
4293
4929
|
subagentId,
|
|
4294
4930
|
taskId: task.id,
|
|
4295
4931
|
status: "failed",
|
|
4296
|
-
error:
|
|
4932
|
+
error: classifySubagentError(err),
|
|
4297
4933
|
iterations: 0,
|
|
4298
4934
|
toolCalls: 0,
|
|
4299
4935
|
durationMs: 0
|
|
@@ -4307,13 +4943,76 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
4307
4943
|
}
|
|
4308
4944
|
findIdleSubagent() {
|
|
4309
4945
|
for (const [id, s] of this.subagents) {
|
|
4310
|
-
if (s.status === "idle") return id;
|
|
4946
|
+
if (s.status === "idle" && !this.terminating.has(id)) return id;
|
|
4311
4947
|
}
|
|
4312
4948
|
return null;
|
|
4313
4949
|
}
|
|
4950
|
+
/**
|
|
4951
|
+
* Returns true iff at least one spawned subagent could still
|
|
4952
|
+
* process a task. A "live" subagent is one that is not stopped
|
|
4953
|
+
* AND not mid-termination — `running` workers count because they
|
|
4954
|
+
* will eventually finish and become idle.
|
|
4955
|
+
*
|
|
4956
|
+
* When no subagent has ever been spawned, returns `true` so a
|
|
4957
|
+
* pre-spawn `assign()` simply queues (legacy behaviour). The
|
|
4958
|
+
* dead-end detection only fires after `stop()` has retired every
|
|
4959
|
+
* spawned worker.
|
|
4960
|
+
*
|
|
4961
|
+
* Used by `tryDispatchNext` to detect a dead-end pending queue.
|
|
4962
|
+
*/
|
|
4963
|
+
hasLiveSubagent() {
|
|
4964
|
+
if (this.subagents.size === 0) return true;
|
|
4965
|
+
for (const [id, s] of this.subagents) {
|
|
4966
|
+
if (s.status !== "stopped" && !this.terminating.has(id)) return true;
|
|
4967
|
+
}
|
|
4968
|
+
return false;
|
|
4969
|
+
}
|
|
4970
|
+
/**
|
|
4971
|
+
* Drain every pending task with a synthetic `aborted_by_parent`
|
|
4972
|
+
* completion event. Same shape as the `stopAll()` drain — we go
|
|
4973
|
+
* around `recordCompletion` because pending tasks were never
|
|
4974
|
+
* counted in `inFlight` and routing them through would trip the
|
|
4975
|
+
* underflow guard on every task after the first.
|
|
4976
|
+
*/
|
|
4977
|
+
drainPendingAsAborted(message) {
|
|
4978
|
+
const dropped = this.pendingTasks.splice(0, this.pendingTasks.length);
|
|
4979
|
+
for (const t of dropped) {
|
|
4980
|
+
const synthetic = {
|
|
4981
|
+
subagentId: t.subagentId ?? "unassigned",
|
|
4982
|
+
taskId: t.id,
|
|
4983
|
+
status: "stopped",
|
|
4984
|
+
error: {
|
|
4985
|
+
kind: "aborted_by_parent",
|
|
4986
|
+
message,
|
|
4987
|
+
retryable: false
|
|
4988
|
+
},
|
|
4989
|
+
iterations: 0,
|
|
4990
|
+
toolCalls: 0,
|
|
4991
|
+
durationMs: 0
|
|
4992
|
+
};
|
|
4993
|
+
this.completedResults.push(synthetic);
|
|
4994
|
+
this.emit("task.completed", { task: t, result: synthetic });
|
|
4995
|
+
}
|
|
4996
|
+
}
|
|
4314
4997
|
async runDispatched(subagentId, task) {
|
|
4315
4998
|
const subagent = this.subagents.get(subagentId);
|
|
4316
4999
|
if (!subagent) return;
|
|
5000
|
+
if (this.terminating.has(subagentId) || subagent.status === "stopped") {
|
|
5001
|
+
this.recordCompletion({
|
|
5002
|
+
subagentId,
|
|
5003
|
+
taskId: task.id,
|
|
5004
|
+
status: "stopped",
|
|
5005
|
+
error: {
|
|
5006
|
+
kind: "aborted_by_parent",
|
|
5007
|
+
message: "Subagent was terminated before task could start",
|
|
5008
|
+
retryable: false
|
|
5009
|
+
},
|
|
5010
|
+
iterations: 0,
|
|
5011
|
+
toolCalls: 0,
|
|
5012
|
+
durationMs: 0
|
|
5013
|
+
});
|
|
5014
|
+
return;
|
|
5015
|
+
}
|
|
4317
5016
|
subagent.status = "running";
|
|
4318
5017
|
subagent.currentTask = task.id;
|
|
4319
5018
|
task.subagentId = subagentId;
|
|
@@ -4359,7 +5058,9 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
4359
5058
|
subagentId,
|
|
4360
5059
|
taskId: task.id,
|
|
4361
5060
|
status,
|
|
4362
|
-
error:
|
|
5061
|
+
error: classifySubagentError(err, {
|
|
5062
|
+
parentAborted: subagent.abortController.signal.aborted
|
|
5063
|
+
}),
|
|
4363
5064
|
iterations: usage.iterations,
|
|
4364
5065
|
toolCalls: usage.toolCalls,
|
|
4365
5066
|
durationMs: Date.now() - startTime
|
|
@@ -4398,19 +5099,14 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
4398
5099
|
}
|
|
4399
5100
|
const subagent = this.subagents.get(result.subagentId);
|
|
4400
5101
|
if (subagent && subagent.status !== "stopped") {
|
|
4401
|
-
|
|
4402
|
-
subagent.status =
|
|
5102
|
+
result.status === "failed" || result.status === "timeout";
|
|
5103
|
+
subagent.status = "idle";
|
|
4403
5104
|
subagent.currentTask = void 0;
|
|
4404
5105
|
if (subagent.abortController.signal.aborted) {
|
|
4405
5106
|
subagent.abortController = new AbortController();
|
|
4406
5107
|
}
|
|
4407
|
-
if (subagent.status === "error") {
|
|
4408
|
-
queueMicrotask(() => {
|
|
4409
|
-
if (subagent.status === "error") subagent.status = "idle";
|
|
4410
|
-
this.tryDispatchNext();
|
|
4411
|
-
});
|
|
4412
|
-
}
|
|
4413
5108
|
}
|
|
5109
|
+
this.terminating.delete(result.subagentId);
|
|
4414
5110
|
this.emit("task.completed", {
|
|
4415
5111
|
task: subagent?.context.tasks.find((t) => t.id === result.taskId) ?? { id: result.taskId },
|
|
4416
5112
|
result
|
|
@@ -4433,6 +5129,99 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
4433
5129
|
return false;
|
|
4434
5130
|
}
|
|
4435
5131
|
};
|
|
5132
|
+
function classifySubagentError(err, hints = {}) {
|
|
5133
|
+
const cause = err instanceof Error ? { name: err.name, message: err.message, stack: err.stack } : void 0;
|
|
5134
|
+
const baseMessage = err instanceof Error ? err.message : String(err);
|
|
5135
|
+
if (err instanceof ProviderError) {
|
|
5136
|
+
return providerErrorToSubagentError(err, baseMessage, cause);
|
|
5137
|
+
}
|
|
5138
|
+
if (err instanceof BudgetExceededError) {
|
|
5139
|
+
const map = {
|
|
5140
|
+
iterations: "budget_iterations",
|
|
5141
|
+
tool_calls: "budget_tool_calls",
|
|
5142
|
+
tokens: "budget_tokens",
|
|
5143
|
+
cost: "budget_cost",
|
|
5144
|
+
timeout: "budget_timeout"
|
|
5145
|
+
};
|
|
5146
|
+
return {
|
|
5147
|
+
kind: map[err.kind],
|
|
5148
|
+
message: baseMessage,
|
|
5149
|
+
// Budgets are user-configured ceilings, not transient failures —
|
|
5150
|
+
// retrying with the same budget will hit the same ceiling. The
|
|
5151
|
+
// orchestrator must raise the budget or narrow the task first.
|
|
5152
|
+
retryable: false,
|
|
5153
|
+
cause
|
|
5154
|
+
};
|
|
5155
|
+
}
|
|
5156
|
+
if (hints.parentAborted) {
|
|
5157
|
+
return {
|
|
5158
|
+
kind: "aborted_by_parent",
|
|
5159
|
+
message: baseMessage,
|
|
5160
|
+
retryable: false,
|
|
5161
|
+
cause
|
|
5162
|
+
};
|
|
5163
|
+
}
|
|
5164
|
+
const lower = baseMessage.toLowerCase();
|
|
5165
|
+
if (/agent aborted$/i.test(baseMessage)) {
|
|
5166
|
+
return {
|
|
5167
|
+
kind: "aborted_by_parent",
|
|
5168
|
+
message: baseMessage,
|
|
5169
|
+
retryable: false,
|
|
5170
|
+
cause
|
|
5171
|
+
};
|
|
5172
|
+
}
|
|
5173
|
+
if (/agent exhausted iteration limit$/i.test(baseMessage)) {
|
|
5174
|
+
return { kind: "budget_iterations", message: baseMessage, retryable: false, cause };
|
|
5175
|
+
}
|
|
5176
|
+
if (/empty response$/i.test(baseMessage)) {
|
|
5177
|
+
return { kind: "empty_response", message: baseMessage, retryable: false, cause };
|
|
5178
|
+
}
|
|
5179
|
+
if (/^tool failed: /i.test(baseMessage)) {
|
|
5180
|
+
return { kind: "tool_failed", message: baseMessage, retryable: false, cause };
|
|
5181
|
+
}
|
|
5182
|
+
if (lower.includes("bridge transport") || /bridge.*(closed|disconnect)/i.test(baseMessage)) {
|
|
5183
|
+
return { kind: "bridge_failed", message: baseMessage, retryable: false, cause };
|
|
5184
|
+
}
|
|
5185
|
+
if (/context length|max.*tokens?.*exceeded|prompt is too long/i.test(baseMessage)) {
|
|
5186
|
+
return { kind: "context_overflow", message: baseMessage, retryable: false, cause };
|
|
5187
|
+
}
|
|
5188
|
+
return {
|
|
5189
|
+
kind: "unknown",
|
|
5190
|
+
message: baseMessage,
|
|
5191
|
+
retryable: false,
|
|
5192
|
+
cause
|
|
5193
|
+
};
|
|
5194
|
+
}
|
|
5195
|
+
function providerErrorToSubagentError(err, message, cause) {
|
|
5196
|
+
const status = err.status;
|
|
5197
|
+
if (status === 429 || err.body?.type === "rate_limit_error") {
|
|
5198
|
+
return {
|
|
5199
|
+
kind: "provider_rate_limit",
|
|
5200
|
+
message,
|
|
5201
|
+
retryable: true,
|
|
5202
|
+
// Conservative default: 5s. Provider-specific code can override
|
|
5203
|
+
// by emitting an error whose body carries an explicit hint.
|
|
5204
|
+
backoffMs: 5e3,
|
|
5205
|
+
cause
|
|
5206
|
+
};
|
|
5207
|
+
}
|
|
5208
|
+
if (status === 401 || status === 403 || err.body?.type === "authentication_error") {
|
|
5209
|
+
return { kind: "provider_auth", message, retryable: false, cause };
|
|
5210
|
+
}
|
|
5211
|
+
if (status === 408 || status === 0) {
|
|
5212
|
+
return { kind: "provider_timeout", message, retryable: true, cause };
|
|
5213
|
+
}
|
|
5214
|
+
if (status >= 500 && status < 600) {
|
|
5215
|
+
return {
|
|
5216
|
+
kind: "provider_5xx",
|
|
5217
|
+
message,
|
|
5218
|
+
retryable: true,
|
|
5219
|
+
backoffMs: 3e3,
|
|
5220
|
+
cause
|
|
5221
|
+
};
|
|
5222
|
+
}
|
|
5223
|
+
return { kind: "unknown", message, retryable: err.retryable, cause };
|
|
5224
|
+
}
|
|
4436
5225
|
|
|
4437
5226
|
// src/coordination/director.ts
|
|
4438
5227
|
var DirectorBudgetError = class extends Error {
|
|
@@ -4496,6 +5285,27 @@ var Director = class {
|
|
|
4496
5285
|
spawnDepth;
|
|
4497
5286
|
/** Live spawn counter for `maxSpawns` enforcement. */
|
|
4498
5287
|
spawnCount = 0;
|
|
5288
|
+
/** Optional checkpoint mirror — writes the live task graph + roster to disk. */
|
|
5289
|
+
stateCheckpoint;
|
|
5290
|
+
/** Optional session writer for emitting task_* / agent_* lifecycle events. */
|
|
5291
|
+
sessionWriter;
|
|
5292
|
+
/** Debounce timer for periodic manifest writes. */
|
|
5293
|
+
manifestTimer = null;
|
|
5294
|
+
manifestDebounceMs;
|
|
5295
|
+
/** Resolves task descriptions back from `assign()` so completion events
|
|
5296
|
+
* can also carry a human-readable title. */
|
|
5297
|
+
taskDescriptions = /* @__PURE__ */ new Map();
|
|
5298
|
+
/** Snapshot of which subagent owns each task — drives state-checkpoint
|
|
5299
|
+
* status updates without re-walking the manifest. */
|
|
5300
|
+
taskOwners = /* @__PURE__ */ new Map();
|
|
5301
|
+
/**
|
|
5302
|
+
* Handle to the coordinator-side `task.completed` listener so we can
|
|
5303
|
+
* unsubscribe in `shutdown()`. Without this, repeated Director
|
|
5304
|
+
* construction (e.g. tests, hot reloads) accumulates listeners on a
|
|
5305
|
+
* cached coordinator and slowly drifts the EventEmitter past its
|
|
5306
|
+
* default cap.
|
|
5307
|
+
*/
|
|
5308
|
+
taskCompletedListener = null;
|
|
4499
5309
|
constructor(opts) {
|
|
4500
5310
|
this.id = opts.config.coordinatorId || randomUUID();
|
|
4501
5311
|
this.manifestPath = opts.manifestPath;
|
|
@@ -4506,6 +5316,14 @@ var Director = class {
|
|
|
4506
5316
|
this.maxSpawns = opts.maxSpawns ?? Number.POSITIVE_INFINITY;
|
|
4507
5317
|
this.maxSpawnDepth = opts.maxSpawnDepth ?? 2;
|
|
4508
5318
|
this.spawnDepth = opts.spawnDepth ?? 0;
|
|
5319
|
+
this.sessionWriter = opts.sessionWriter ?? null;
|
|
5320
|
+
this.manifestDebounceMs = opts.manifestDebounceMs ?? 2e3;
|
|
5321
|
+
this.stateCheckpoint = opts.stateCheckpointPath ? new DirectorStateCheckpoint(opts.stateCheckpointPath, {
|
|
5322
|
+
directorRunId: this.id,
|
|
5323
|
+
maxSpawns: opts.maxSpawns,
|
|
5324
|
+
spawnDepth: this.spawnDepth,
|
|
5325
|
+
maxSpawnDepth: this.maxSpawnDepth
|
|
5326
|
+
}) : null;
|
|
4509
5327
|
if (this.sharedScratchpadPath) {
|
|
4510
5328
|
void fsp.mkdir(this.sharedScratchpadPath, { recursive: true }).catch(() => void 0);
|
|
4511
5329
|
}
|
|
@@ -4524,7 +5342,7 @@ var Director = class {
|
|
|
4524
5342
|
{ ...opts.config, coordinatorId: this.id },
|
|
4525
5343
|
{ runner: opts.runner }
|
|
4526
5344
|
);
|
|
4527
|
-
this.
|
|
5345
|
+
this.taskCompletedListener = (payload) => {
|
|
4528
5346
|
const r = payload.result;
|
|
4529
5347
|
this.completed.set(r.taskId, r);
|
|
4530
5348
|
const waiter = this.taskWaiters.get(r.taskId);
|
|
@@ -4532,7 +5350,54 @@ var Director = class {
|
|
|
4532
5350
|
waiter.resolve(r);
|
|
4533
5351
|
this.taskWaiters.delete(r.taskId);
|
|
4534
5352
|
}
|
|
4535
|
-
|
|
5353
|
+
const title = this.taskDescriptions.get(r.taskId) ?? payload.task.description ?? r.taskId;
|
|
5354
|
+
const failed = r.status !== "success";
|
|
5355
|
+
const errorString = r.error ? `${r.error.kind}: ${r.error.message}` : void 0;
|
|
5356
|
+
this.stateCheckpoint?.recordTaskStatus(r.taskId, {
|
|
5357
|
+
status: failed ? r.status : "completed",
|
|
5358
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5359
|
+
iterations: r.iterations,
|
|
5360
|
+
toolCalls: r.toolCalls,
|
|
5361
|
+
durationMs: r.durationMs,
|
|
5362
|
+
error: errorString
|
|
5363
|
+
});
|
|
5364
|
+
this.stateCheckpoint?.setUsage(this.usage.snapshot());
|
|
5365
|
+
void this.appendSessionEvent(
|
|
5366
|
+
failed ? {
|
|
5367
|
+
type: "task_failed",
|
|
5368
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5369
|
+
taskId: r.taskId,
|
|
5370
|
+
title,
|
|
5371
|
+
error: errorString ?? r.status
|
|
5372
|
+
} : {
|
|
5373
|
+
type: "task_completed",
|
|
5374
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5375
|
+
taskId: r.taskId,
|
|
5376
|
+
title
|
|
5377
|
+
}
|
|
5378
|
+
);
|
|
5379
|
+
this.scheduleManifest();
|
|
5380
|
+
};
|
|
5381
|
+
this.coordinator.on("task.completed", this.taskCompletedListener);
|
|
5382
|
+
}
|
|
5383
|
+
/** Best-effort session-writer append. Swallows failures — the director
|
|
5384
|
+
* must not break a fleet run because the session JSONL handle closed. */
|
|
5385
|
+
async appendSessionEvent(event) {
|
|
5386
|
+
if (!this.sessionWriter) return;
|
|
5387
|
+
try {
|
|
5388
|
+
await this.sessionWriter.append(event);
|
|
5389
|
+
} catch {
|
|
5390
|
+
}
|
|
5391
|
+
}
|
|
5392
|
+
/** Debounced manifest writer. A burst of spawn/assign/complete events
|
|
5393
|
+
* collapses into one write. Set `manifestDebounceMs` to 0 to disable. */
|
|
5394
|
+
scheduleManifest() {
|
|
5395
|
+
if (!this.manifestPath || this.manifestDebounceMs <= 0) return;
|
|
5396
|
+
if (this.manifestTimer) return;
|
|
5397
|
+
this.manifestTimer = setTimeout(() => {
|
|
5398
|
+
this.manifestTimer = null;
|
|
5399
|
+
void this.writeManifest().catch(() => void 0);
|
|
5400
|
+
}, this.manifestDebounceMs);
|
|
4536
5401
|
}
|
|
4537
5402
|
/**
|
|
4538
5403
|
* Spawn a subagent. Identical to the coordinator's `spawn()` but
|
|
@@ -4571,6 +5436,25 @@ var Director = class {
|
|
|
4571
5436
|
model: config.model,
|
|
4572
5437
|
taskIds: []
|
|
4573
5438
|
});
|
|
5439
|
+
const spawnedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5440
|
+
this.stateCheckpoint?.recordSpawn(
|
|
5441
|
+
{
|
|
5442
|
+
id: result.subagentId,
|
|
5443
|
+
name: config.name,
|
|
5444
|
+
role: config.role,
|
|
5445
|
+
provider: config.provider,
|
|
5446
|
+
model: config.model,
|
|
5447
|
+
spawnedAt
|
|
5448
|
+
},
|
|
5449
|
+
this.spawnCount
|
|
5450
|
+
);
|
|
5451
|
+
void this.appendSessionEvent({
|
|
5452
|
+
type: "agent_spawned",
|
|
5453
|
+
ts: spawnedAt,
|
|
5454
|
+
agentId: result.subagentId,
|
|
5455
|
+
role: config.role ?? config.name
|
|
5456
|
+
});
|
|
5457
|
+
this.scheduleManifest();
|
|
4574
5458
|
return result.subagentId;
|
|
4575
5459
|
}
|
|
4576
5460
|
/**
|
|
@@ -4691,13 +5575,42 @@ var Director = class {
|
|
|
4691
5575
|
* — calling shutdown twice is a no-op on the second invocation.
|
|
4692
5576
|
*/
|
|
4693
5577
|
async shutdown() {
|
|
5578
|
+
if (this.manifestTimer) {
|
|
5579
|
+
clearTimeout(this.manifestTimer);
|
|
5580
|
+
this.manifestTimer = null;
|
|
5581
|
+
}
|
|
5582
|
+
if (this.taskCompletedListener) {
|
|
5583
|
+
this.coordinator.off("task.completed", this.taskCompletedListener);
|
|
5584
|
+
this.taskCompletedListener = null;
|
|
5585
|
+
}
|
|
4694
5586
|
await this.coordinator.stopAll();
|
|
4695
5587
|
for (const b of this.subagentBridges.values()) {
|
|
4696
|
-
await b.stop().catch(() =>
|
|
5588
|
+
await b.stop().catch((err) => this.logShutdownError("subagent_bridge_stop", err));
|
|
4697
5589
|
}
|
|
4698
5590
|
this.subagentBridges.clear();
|
|
4699
|
-
await this.bridge.stop().catch(() =>
|
|
4700
|
-
if (this.manifestPath)
|
|
5591
|
+
await this.bridge.stop().catch((err) => this.logShutdownError("director_bridge_stop", err));
|
|
5592
|
+
if (this.manifestPath)
|
|
5593
|
+
await this.writeManifest().catch((err) => this.logShutdownError("manifest_write", err));
|
|
5594
|
+
if (this.stateCheckpoint) {
|
|
5595
|
+
this.stateCheckpoint.setUsage(this.usage.snapshot());
|
|
5596
|
+
await this.stateCheckpoint.flush().catch((err) => this.logShutdownError("state_checkpoint_flush", err));
|
|
5597
|
+
}
|
|
5598
|
+
}
|
|
5599
|
+
/**
|
|
5600
|
+
* Funnel for shutdown-phase errors. We can't throw — `shutdown()` is
|
|
5601
|
+
* called from process-exit paths where an uncaught throw would lose
|
|
5602
|
+
* the manifest write that comes after. But we MUST NOT silently
|
|
5603
|
+
* swallow either — a persistent bridge-close failure would otherwise
|
|
5604
|
+
* mask a real bug. `process.emitWarning` is the right tier:
|
|
5605
|
+
* surfaces on stderr by default, lets the host plug a warning
|
|
5606
|
+
* listener for structured collection, and never affects exit code.
|
|
5607
|
+
*/
|
|
5608
|
+
logShutdownError(phase, err) {
|
|
5609
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
5610
|
+
process.emitWarning(
|
|
5611
|
+
`Director shutdown phase "${phase}" failed: ${detail}`,
|
|
5612
|
+
"DirectorShutdownWarning"
|
|
5613
|
+
);
|
|
4701
5614
|
}
|
|
4702
5615
|
/**
|
|
4703
5616
|
* Hand a task to the coordinator. Returns the assigned task id so
|
|
@@ -4711,6 +5624,23 @@ var Director = class {
|
|
|
4711
5624
|
if (entry) entry.taskIds.push(taskWithId.id);
|
|
4712
5625
|
}
|
|
4713
5626
|
await this.coordinator.assign(taskWithId);
|
|
5627
|
+
this.taskDescriptions.set(taskWithId.id, taskWithId.description);
|
|
5628
|
+
if (taskWithId.subagentId) this.taskOwners.set(taskWithId.id, taskWithId.subagentId);
|
|
5629
|
+
const assignedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5630
|
+
this.stateCheckpoint?.recordTaskAssigned({
|
|
5631
|
+
taskId: taskWithId.id,
|
|
5632
|
+
subagentId: taskWithId.subagentId,
|
|
5633
|
+
description: taskWithId.description,
|
|
5634
|
+
status: "running",
|
|
5635
|
+
assignedAt
|
|
5636
|
+
});
|
|
5637
|
+
void this.appendSessionEvent({
|
|
5638
|
+
type: "task_created",
|
|
5639
|
+
ts: assignedAt,
|
|
5640
|
+
taskId: taskWithId.id,
|
|
5641
|
+
title: taskWithId.description
|
|
5642
|
+
});
|
|
5643
|
+
this.scheduleManifest();
|
|
4714
5644
|
return taskWithId.id;
|
|
4715
5645
|
}
|
|
4716
5646
|
/**
|
|
@@ -4770,6 +5700,23 @@ var Director = class {
|
|
|
4770
5700
|
snapshot() {
|
|
4771
5701
|
return this.usage.snapshot();
|
|
4772
5702
|
}
|
|
5703
|
+
/**
|
|
5704
|
+
* Look up provider/model metadata for a spawned subagent. Returns
|
|
5705
|
+
* undefined when the subagent id is unknown (not yet spawned, or
|
|
5706
|
+
* already torn down). Callers — notably the TUI fleet panel — use
|
|
5707
|
+
* this to render human-readable provider/model tags next to each
|
|
5708
|
+
* subagent row without reaching into private state.
|
|
5709
|
+
*/
|
|
5710
|
+
getSubagentMeta(id) {
|
|
5711
|
+
const usage = this.subagentMeta.get(id);
|
|
5712
|
+
const manifest = this.manifestEntries.get(id);
|
|
5713
|
+
if (!usage && !manifest) return void 0;
|
|
5714
|
+
return {
|
|
5715
|
+
provider: usage?.provider ?? manifest?.provider,
|
|
5716
|
+
model: usage?.model ?? manifest?.model,
|
|
5717
|
+
name: manifest?.name
|
|
5718
|
+
};
|
|
5719
|
+
}
|
|
4773
5720
|
/**
|
|
4774
5721
|
* Compose the leader/director-agent system prompt: fleet preamble +
|
|
4775
5722
|
* (optional) roster summary + user base prompt. Pass the result to your
|
|
@@ -5069,12 +6016,260 @@ function makeFleetUsageTool(director) {
|
|
|
5069
6016
|
}
|
|
5070
6017
|
};
|
|
5071
6018
|
}
|
|
6019
|
+
function createDelegateTool(opts) {
|
|
6020
|
+
const defaultTimeoutMs = opts.defaultTimeoutMs ?? 4 * 60 * 60 * 1e3;
|
|
6021
|
+
const rosterIds = opts.roster ? Object.keys(opts.roster) : [];
|
|
6022
|
+
const inputSchema = {
|
|
6023
|
+
type: "object",
|
|
6024
|
+
properties: {
|
|
6025
|
+
task: {
|
|
6026
|
+
type: "string",
|
|
6027
|
+
description: "What the subagent should do \u2014 natural language, complete sentence(s). The subagent has its own tool slice, its own LLM call, and returns when its task is done."
|
|
6028
|
+
},
|
|
6029
|
+
role: {
|
|
6030
|
+
type: "string",
|
|
6031
|
+
description: rosterIds.length > 0 ? `Roster role (preferred). One of: ${rosterIds.join(", ")}. Picks a pre-tuned config (prompt, budgets, tools) for that role.` : "No roster is configured \u2014 pass `name` instead.",
|
|
6032
|
+
enum: rosterIds.length > 0 ? rosterIds : void 0
|
|
6033
|
+
},
|
|
6034
|
+
name: {
|
|
6035
|
+
type: "string",
|
|
6036
|
+
description: "Display name for the subagent when not using a roster role. Required when `role` is omitted."
|
|
6037
|
+
},
|
|
6038
|
+
provider: {
|
|
6039
|
+
type: "string",
|
|
6040
|
+
description: 'Provider id (e.g. "anthropic", "openai"). Defaults to the host provider when omitted.'
|
|
6041
|
+
},
|
|
6042
|
+
model: {
|
|
6043
|
+
type: "string",
|
|
6044
|
+
description: "Model id within the provider. Defaults to the host model when omitted."
|
|
6045
|
+
},
|
|
6046
|
+
systemPromptOverride: {
|
|
6047
|
+
type: "string",
|
|
6048
|
+
description: "Optional extra prompt text appended to the role baseline."
|
|
6049
|
+
},
|
|
6050
|
+
timeoutMs: {
|
|
6051
|
+
type: "number",
|
|
6052
|
+
description: `Wall-clock budget for this delegate in milliseconds. No hard cap \u2014 set as high as the task realistically needs (a monorepo audit can take hours, a single-file lint takes seconds). Default ${Math.round(defaultTimeoutMs / 1e3 / 60)} minutes.`
|
|
6053
|
+
},
|
|
6054
|
+
maxIterations: {
|
|
6055
|
+
type: "number",
|
|
6056
|
+
description: "Maximum LLM iterations the subagent may take. Unset = use the role/coordinator default. Raise this for tasks with many tool-think-tool cycles (deep code analysis, multi-file refactors)."
|
|
6057
|
+
},
|
|
6058
|
+
maxToolCalls: {
|
|
6059
|
+
type: "number",
|
|
6060
|
+
description: "Maximum number of tool invocations the subagent may make. Unset = use the role/coordinator default. Raise this for tasks that touch many files (large grep + read + report)."
|
|
6061
|
+
}
|
|
6062
|
+
},
|
|
6063
|
+
required: ["task"]
|
|
6064
|
+
};
|
|
6065
|
+
return {
|
|
6066
|
+
name: "delegate",
|
|
6067
|
+
description: "Hand a discrete piece of work to a dedicated subagent and wait for its result. The subagent has its own context, its own LLM call, and its own budget \u2014 use this when a task is self-contained, would otherwise blow up your context, or benefits from a specialized role (bug-hunter, security-scanner, refactor-planner, audit-log). YOU decide how big the budget is: pass `timeoutMs`, `maxIterations`, and `maxToolCalls` sized to the actual work. There is no hidden cap forcing a 3-minute / 80-iteration limit \u2014 if a monorepo audit needs 2 hours and 500 tool calls, ask for that. Call multiple delegates in parallel through the provider's parallel-tool-call surface to fan work out across roles.",
|
|
6068
|
+
usageHint: "Set `task` to a complete instruction. Either pick `role` from the roster or pass `name` + `provider` + `model`. For non-trivial work, also pass `timeoutMs` (the wall-clock budget you actually need), `maxIterations`, and `maxToolCalls` \u2014 defaults are intentionally generous (4 hours) but the right values depend on scope. Returns the subagent's `TaskResult` \u2014 including the textual `result`, iteration count, tool count, and duration. Auto-promotes the host into director mode on first call.",
|
|
6069
|
+
permission: "auto",
|
|
6070
|
+
mutating: false,
|
|
6071
|
+
inputSchema,
|
|
6072
|
+
async execute(input) {
|
|
6073
|
+
const i = input ?? {};
|
|
6074
|
+
if (typeof i.task !== "string" || !i.task.trim()) {
|
|
6075
|
+
return { ok: false, error: "`task` is required." };
|
|
6076
|
+
}
|
|
6077
|
+
let director = await opts.host.ensureDirector();
|
|
6078
|
+
if (!director) {
|
|
6079
|
+
director = await opts.host.promoteToDirector();
|
|
6080
|
+
}
|
|
6081
|
+
if (!director) {
|
|
6082
|
+
const reason = opts.host.getPromotionBlockReason?.();
|
|
6083
|
+
return {
|
|
6084
|
+
ok: false,
|
|
6085
|
+
error: reason ?? "Director could not be activated \u2014 multi-agent host already running in legacy non-director mode. Restart with `--director` for fleet support."
|
|
6086
|
+
};
|
|
6087
|
+
}
|
|
6088
|
+
const timeoutMs = i.timeoutMs ?? defaultTimeoutMs;
|
|
6089
|
+
let cfg;
|
|
6090
|
+
if (i.role) {
|
|
6091
|
+
const base = opts.roster?.[i.role];
|
|
6092
|
+
if (!base) {
|
|
6093
|
+
return {
|
|
6094
|
+
ok: false,
|
|
6095
|
+
error: `Unknown role "${i.role}". Available: ${rosterIds.join(", ") || "(no roster configured)"}.`
|
|
6096
|
+
};
|
|
6097
|
+
}
|
|
6098
|
+
cfg = { ...base };
|
|
6099
|
+
if (i.systemPromptOverride) cfg.systemPromptOverride = i.systemPromptOverride;
|
|
6100
|
+
if (i.provider) cfg.provider = i.provider;
|
|
6101
|
+
if (i.model) cfg.model = i.model;
|
|
6102
|
+
} else {
|
|
6103
|
+
if (!i.name) {
|
|
6104
|
+
return {
|
|
6105
|
+
ok: false,
|
|
6106
|
+
error: "Either `role` (from the roster) or `name` is required."
|
|
6107
|
+
};
|
|
6108
|
+
}
|
|
6109
|
+
cfg = {
|
|
6110
|
+
name: i.name,
|
|
6111
|
+
provider: i.provider,
|
|
6112
|
+
model: i.model,
|
|
6113
|
+
systemPromptOverride: i.systemPromptOverride
|
|
6114
|
+
};
|
|
6115
|
+
}
|
|
6116
|
+
if (typeof i.maxIterations === "number" && i.maxIterations > 0) {
|
|
6117
|
+
cfg.maxIterations = i.maxIterations;
|
|
6118
|
+
}
|
|
6119
|
+
if (typeof i.maxToolCalls === "number" && i.maxToolCalls > 0) {
|
|
6120
|
+
cfg.maxToolCalls = i.maxToolCalls;
|
|
6121
|
+
}
|
|
6122
|
+
const SUBAGENT_TIMEOUT_BUFFER_MS = 3e4;
|
|
6123
|
+
const desiredSubTimeout = Math.max(3e4, timeoutMs - SUBAGENT_TIMEOUT_BUFFER_MS);
|
|
6124
|
+
if (!cfg.timeoutMs || cfg.timeoutMs > desiredSubTimeout) {
|
|
6125
|
+
cfg.timeoutMs = desiredSubTimeout;
|
|
6126
|
+
}
|
|
6127
|
+
try {
|
|
6128
|
+
const subagentId = await director.spawn(cfg);
|
|
6129
|
+
const taskId = await director.assign({
|
|
6130
|
+
id: "",
|
|
6131
|
+
description: i.task,
|
|
6132
|
+
subagentId
|
|
6133
|
+
});
|
|
6134
|
+
const result = await Promise.race([
|
|
6135
|
+
director.awaitTasks([taskId]).then((r) => r[0]),
|
|
6136
|
+
new Promise(
|
|
6137
|
+
(resolve2) => setTimeout(() => resolve2({ __timeout: true }), timeoutMs)
|
|
6138
|
+
)
|
|
6139
|
+
]);
|
|
6140
|
+
if ("__timeout" in result) {
|
|
6141
|
+
const partial2 = await readSubagentPartial(opts, subagentId);
|
|
6142
|
+
return {
|
|
6143
|
+
ok: false,
|
|
6144
|
+
stopReason: "host_timeout",
|
|
6145
|
+
error: `Subagent did not finish within ${timeoutMs}ms.`,
|
|
6146
|
+
hint: "Reduce scope of the next delegate, raise timeoutMs, or use spawn_subagent + await_tasks for long-running work.",
|
|
6147
|
+
subagentId,
|
|
6148
|
+
taskId,
|
|
6149
|
+
partial: partial2
|
|
6150
|
+
};
|
|
6151
|
+
}
|
|
6152
|
+
const baseStopReason = result.status === "success" ? "end_turn" : result.status === "timeout" ? "subagent_timeout" : result.status === "stopped" ? "aborted" : "budget_exhausted";
|
|
6153
|
+
const partial = result.status === "success" ? void 0 : await readSubagentPartial(opts, subagentId);
|
|
6154
|
+
const errorKind = result.error?.kind;
|
|
6155
|
+
const retryable = result.error?.retryable;
|
|
6156
|
+
const backoffMs = result.error?.backoffMs;
|
|
6157
|
+
return {
|
|
6158
|
+
ok: result.status === "success",
|
|
6159
|
+
status: result.status,
|
|
6160
|
+
stopReason: baseStopReason,
|
|
6161
|
+
errorKind,
|
|
6162
|
+
retryable,
|
|
6163
|
+
backoffMs,
|
|
6164
|
+
subagentId: result.subagentId,
|
|
6165
|
+
taskId: result.taskId,
|
|
6166
|
+
result: result.result,
|
|
6167
|
+
error: result.error,
|
|
6168
|
+
iterations: result.iterations,
|
|
6169
|
+
toolCalls: result.toolCalls,
|
|
6170
|
+
durationMs: result.durationMs,
|
|
6171
|
+
...partial ? { partial } : {},
|
|
6172
|
+
...hintForKind(errorKind, retryable, backoffMs) ? { hint: hintForKind(errorKind, retryable, backoffMs) } : {}
|
|
6173
|
+
};
|
|
6174
|
+
} catch (err) {
|
|
6175
|
+
return {
|
|
6176
|
+
ok: false,
|
|
6177
|
+
stopReason: "error",
|
|
6178
|
+
error: err instanceof Error ? err.message : String(err)
|
|
6179
|
+
};
|
|
6180
|
+
}
|
|
6181
|
+
}
|
|
6182
|
+
};
|
|
6183
|
+
}
|
|
6184
|
+
function hintForKind(kind, retryable, backoffMs) {
|
|
6185
|
+
if (!kind) return void 0;
|
|
6186
|
+
switch (kind) {
|
|
6187
|
+
case "provider_rate_limit":
|
|
6188
|
+
return `Provider rate-limited. Retry safe after ${backoffMs ?? 5e3}ms backoff. Consider a smaller model or fewer parallel delegates.`;
|
|
6189
|
+
case "provider_5xx":
|
|
6190
|
+
return `Provider server error. Retry safe after ${backoffMs ?? 3e3}ms backoff \u2014 usually transient.`;
|
|
6191
|
+
case "provider_timeout":
|
|
6192
|
+
return "Provider network timeout. Retry safe; reduce input size if it persists.";
|
|
6193
|
+
case "provider_auth":
|
|
6194
|
+
return "Provider rejected credentials. Cannot retry \u2014 fix the API key / config and re-invoke.";
|
|
6195
|
+
case "context_overflow":
|
|
6196
|
+
return "Subagent context exceeded the model limit. Narrow the task, use a larger-context model, or split into multiple delegates.";
|
|
6197
|
+
case "budget_iterations":
|
|
6198
|
+
case "budget_tool_calls":
|
|
6199
|
+
case "budget_tokens":
|
|
6200
|
+
case "budget_cost":
|
|
6201
|
+
return "Subagent exhausted its budget. Raise the matching `max*` field on the next delegate or narrow task scope.";
|
|
6202
|
+
case "budget_timeout":
|
|
6203
|
+
return "Subagent hit its wall-clock budget. Raise `timeoutMs` on the next delegate or split the task.";
|
|
6204
|
+
case "aborted_by_parent":
|
|
6205
|
+
return "Subagent was aborted (user Ctrl+C, parent unwound, or sibling failure cascade). Not retryable until the abort condition is resolved.";
|
|
6206
|
+
case "empty_response":
|
|
6207
|
+
return "Subagent ended its turn with no text and no tool calls. Almost always a prompt / config issue \u2014 clarify the task or check the model.";
|
|
6208
|
+
case "tool_failed":
|
|
6209
|
+
return "A tool inside the subagent returned ok:false. Inspect `partial.lastAssistantText` for the agent reasoning, then retry with corrected inputs.";
|
|
6210
|
+
case "bridge_failed":
|
|
6211
|
+
return "Parent-child bridge transport failed. This is rare \u2014 restart the session and retry.";
|
|
6212
|
+
default:
|
|
6213
|
+
return retryable ? "Failure classified as retryable. Try again with the same input." : void 0;
|
|
6214
|
+
}
|
|
6215
|
+
}
|
|
6216
|
+
async function readSubagentPartial(opts, subagentId) {
|
|
6217
|
+
if (!opts.sessionsRoot) return void 0;
|
|
6218
|
+
const candidates = [];
|
|
6219
|
+
if (opts.directorRunId) {
|
|
6220
|
+
candidates.push(path3.join(opts.sessionsRoot, opts.directorRunId, `${subagentId}.jsonl`));
|
|
6221
|
+
} else {
|
|
6222
|
+
try {
|
|
6223
|
+
const runDirs = await fsp.readdir(opts.sessionsRoot);
|
|
6224
|
+
for (const r of runDirs) {
|
|
6225
|
+
candidates.push(path3.join(opts.sessionsRoot, r, `${subagentId}.jsonl`));
|
|
6226
|
+
}
|
|
6227
|
+
} catch {
|
|
6228
|
+
return void 0;
|
|
6229
|
+
}
|
|
6230
|
+
}
|
|
6231
|
+
for (const file of candidates) {
|
|
6232
|
+
let raw;
|
|
6233
|
+
try {
|
|
6234
|
+
raw = await fsp.readFile(file, "utf8");
|
|
6235
|
+
} catch {
|
|
6236
|
+
continue;
|
|
6237
|
+
}
|
|
6238
|
+
const lines = raw.split("\n").filter((l) => l.trim());
|
|
6239
|
+
let lastAssistantText;
|
|
6240
|
+
let lastStopReason;
|
|
6241
|
+
let toolUses = 0;
|
|
6242
|
+
for (const line of lines) {
|
|
6243
|
+
try {
|
|
6244
|
+
const ev = JSON.parse(line);
|
|
6245
|
+
if (ev.type === "tool_use") toolUses += 1;
|
|
6246
|
+
if (ev.type === "llm_response") {
|
|
6247
|
+
if (typeof ev.stopReason === "string") lastStopReason = ev.stopReason;
|
|
6248
|
+
if (Array.isArray(ev.content)) {
|
|
6249
|
+
const txt = ev.content.filter((b) => b.type === "text").map((b) => b.text ?? "").join("\n").trim();
|
|
6250
|
+
if (txt) lastAssistantText = txt;
|
|
6251
|
+
}
|
|
6252
|
+
}
|
|
6253
|
+
} catch {
|
|
6254
|
+
}
|
|
6255
|
+
}
|
|
6256
|
+
return {
|
|
6257
|
+
lastAssistantText,
|
|
6258
|
+
lastStopReason,
|
|
6259
|
+
toolUsesObserved: toolUses,
|
|
6260
|
+
events: lines.length
|
|
6261
|
+
};
|
|
6262
|
+
}
|
|
6263
|
+
return void 0;
|
|
6264
|
+
}
|
|
5072
6265
|
|
|
5073
6266
|
// src/coordination/agent-subagent-runner.ts
|
|
5074
6267
|
function makeAgentSubagentRunner(opts) {
|
|
5075
6268
|
const format = opts.formatTaskInput ?? defaultFormatTaskInput;
|
|
5076
6269
|
return async (task, ctx) => {
|
|
5077
|
-
const
|
|
6270
|
+
const factoryResult = await opts.factory(ctx.config);
|
|
6271
|
+
const { agent, events } = factoryResult;
|
|
6272
|
+
const detachFleet = opts.fleetBus?.attach(ctx.subagentId, events, task.id);
|
|
5078
6273
|
const aborter = new AbortController();
|
|
5079
6274
|
let budgetError = null;
|
|
5080
6275
|
const onBudgetError = (err) => {
|
|
@@ -5088,13 +6283,19 @@ function makeAgentSubagentRunner(opts) {
|
|
|
5088
6283
|
budgetError.message += ` (caused by: ${err.message})`;
|
|
5089
6284
|
}
|
|
5090
6285
|
};
|
|
6286
|
+
let lastToolFailed = null;
|
|
5091
6287
|
const unsub = [];
|
|
5092
6288
|
unsub.push(
|
|
5093
|
-
events.on("tool.
|
|
6289
|
+
events.on("tool.executed", (e) => {
|
|
5094
6290
|
try {
|
|
5095
6291
|
ctx.budget.recordToolCall();
|
|
5096
|
-
} catch (
|
|
5097
|
-
onBudgetError(
|
|
6292
|
+
} catch (eb) {
|
|
6293
|
+
onBudgetError(eb);
|
|
6294
|
+
}
|
|
6295
|
+
if (e.ok === false) {
|
|
6296
|
+
lastToolFailed = e.name;
|
|
6297
|
+
} else if (e.ok === true) {
|
|
6298
|
+
lastToolFailed = null;
|
|
5098
6299
|
}
|
|
5099
6300
|
}),
|
|
5100
6301
|
events.on("provider.response", (e) => {
|
|
@@ -5111,6 +6312,26 @@ function makeAgentSubagentRunner(opts) {
|
|
|
5111
6312
|
} catch (e) {
|
|
5112
6313
|
onBudgetError(e);
|
|
5113
6314
|
}
|
|
6315
|
+
}),
|
|
6316
|
+
// D3: cooperative timeout enforcement DURING a long tool call.
|
|
6317
|
+
// The iteration-loop checkTimeout() only fires between agent
|
|
6318
|
+
// iterations — a single `bash sleep 3600` call would otherwise
|
|
6319
|
+
// park inside one tool execution while the timeout silently
|
|
6320
|
+
// passes, relying solely on the coordinator's hard Promise.race
|
|
6321
|
+
// to interrupt. Tools that emit `tool.progress` (bash chunks,
|
|
6322
|
+
// fetch byte progress, spawn-stream stdout) give us a heartbeat
|
|
6323
|
+
// we can hang the check on. When the budget trips here:
|
|
6324
|
+
// 1. onBudgetError sets budgetError + aborter.abort()
|
|
6325
|
+
// 2. aborter signal propagates to agent.run → tool executor
|
|
6326
|
+
// 3. tool's own signal listener kills the child process
|
|
6327
|
+
// Cheap: O(1) per progress event, and the budget short-circuits
|
|
6328
|
+
// when timeoutMs is unset (most subagents have one set anyway).
|
|
6329
|
+
events.on("tool.progress", () => {
|
|
6330
|
+
try {
|
|
6331
|
+
ctx.budget.checkTimeout();
|
|
6332
|
+
} catch (e) {
|
|
6333
|
+
onBudgetError(e);
|
|
6334
|
+
}
|
|
5114
6335
|
})
|
|
5115
6336
|
);
|
|
5116
6337
|
const onParentAbort = () => aborter.abort();
|
|
@@ -5119,8 +6340,15 @@ function makeAgentSubagentRunner(opts) {
|
|
|
5119
6340
|
try {
|
|
5120
6341
|
result = await agent.run(format(task, ctx.config), { signal: aborter.signal });
|
|
5121
6342
|
} finally {
|
|
6343
|
+
detachFleet?.();
|
|
5122
6344
|
ctx.signal.removeEventListener("abort", onParentAbort);
|
|
5123
6345
|
for (const u of unsub) u();
|
|
6346
|
+
if (factoryResult.dispose) {
|
|
6347
|
+
try {
|
|
6348
|
+
await factoryResult.dispose();
|
|
6349
|
+
} catch {
|
|
6350
|
+
}
|
|
6351
|
+
}
|
|
5124
6352
|
}
|
|
5125
6353
|
if (budgetError) throw budgetError;
|
|
5126
6354
|
if (result.status === "failed") {
|
|
@@ -5133,6 +6361,13 @@ function makeAgentSubagentRunner(opts) {
|
|
|
5133
6361
|
throw new Error("agent exhausted iteration limit");
|
|
5134
6362
|
}
|
|
5135
6363
|
const usage = ctx.budget.usage();
|
|
6364
|
+
const finalText = (result.finalText ?? "").trim();
|
|
6365
|
+
if (finalText.length === 0 && usage.toolCalls === 0) {
|
|
6366
|
+
throw new Error("empty response");
|
|
6367
|
+
}
|
|
6368
|
+
if (finalText.length === 0 && lastToolFailed !== null) {
|
|
6369
|
+
throw new Error(`tool failed: ${lastToolFailed}`);
|
|
6370
|
+
}
|
|
5136
6371
|
return {
|
|
5137
6372
|
result: result.finalText,
|
|
5138
6373
|
iterations: result.iterations,
|
|
@@ -5198,10 +6433,12 @@ Working rules:
|
|
|
5198
6433
|
- Never fabricate numbers \u2014 read the actual logs first
|
|
5199
6434
|
- Always include file:line references for errors
|
|
5200
6435
|
- If sessionPath is missing, ask the director to provide it
|
|
5201
|
-
- Report confidence level: high (>90% accuracy), medium, low
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
6436
|
+
- Report confidence level: high (>90% accuracy), medium, low`
|
|
6437
|
+
// No hardcoded budgets — the orchestrator (delegate tool or
|
|
6438
|
+
// spawn_subagent) decides per-task how much room a subagent gets.
|
|
6439
|
+
// A monorepo audit needs hours; a single-file lint check needs
|
|
6440
|
+
// seconds. Pinning a number here forces the orchestrator to fight
|
|
6441
|
+
// the role's default instead of just asking for what it needs.
|
|
5205
6442
|
};
|
|
5206
6443
|
var BUG_HUNTER_AGENT = {
|
|
5207
6444
|
id: "bug-hunter",
|
|
@@ -5241,10 +6478,8 @@ Working rules:
|
|
|
5241
6478
|
- Never scan node_modules \u2014 it's noise
|
|
5242
6479
|
- Always include file:line for every finding
|
|
5243
6480
|
- If >30% of findings are false positives, note the confidence level
|
|
5244
|
-
- Ask director for clarification if paths are ambiguous
|
|
5245
|
-
|
|
5246
|
-
maxToolCalls: 300,
|
|
5247
|
-
timeoutMs: 18e4
|
|
6481
|
+
- Ask director for clarification if paths are ambiguous`
|
|
6482
|
+
// Budgets are set by the orchestrator per task — see fleet.ts header.
|
|
5248
6483
|
};
|
|
5249
6484
|
var REFACTOR_PLANNER_AGENT = {
|
|
5250
6485
|
id: "refactor-planner",
|
|
@@ -5284,10 +6519,8 @@ Working rules:
|
|
|
5284
6519
|
- Always include rollback strategy \u2014 every refactor can fail
|
|
5285
6520
|
- Merge tasks that take <1h into a single phase
|
|
5286
6521
|
- Respect team constraints (reviewer availability, parallelization)
|
|
5287
|
-
- Never plan without analyzing the actual code first
|
|
5288
|
-
|
|
5289
|
-
maxToolCalls: 250,
|
|
5290
|
-
timeoutMs: 15e4
|
|
6522
|
+
- Never plan without analyzing the actual code first`
|
|
6523
|
+
// Budgets are set by the orchestrator per task — see fleet.ts header.
|
|
5291
6524
|
};
|
|
5292
6525
|
var SECURITY_SCANNER_AGENT = {
|
|
5293
6526
|
id: "security-scanner",
|
|
@@ -5335,10 +6568,8 @@ Working rules:
|
|
|
5335
6568
|
- Never scan node_modules \u2014 use npm audit instead
|
|
5336
6569
|
- Always provide remediation steps, not just findings
|
|
5337
6570
|
- Verify regex-based secrets before flagging (false positive risk)
|
|
5338
|
-
- When in doubt, flag as medium rather than ignoring potential issues
|
|
5339
|
-
|
|
5340
|
-
maxToolCalls: 280,
|
|
5341
|
-
timeoutMs: 16e4
|
|
6571
|
+
- When in doubt, flag as medium rather than ignoring potential issues`
|
|
6572
|
+
// Budgets are set by the orchestrator per task — see fleet.ts header.
|
|
5342
6573
|
};
|
|
5343
6574
|
var FLEET_ROSTER = {
|
|
5344
6575
|
"audit-log": AUDIT_LOG_AGENT,
|
|
@@ -6959,7 +8190,7 @@ async function startMetricsServer(opts) {
|
|
|
6959
8190
|
const tls = opts.tls;
|
|
6960
8191
|
const useHttps = !!(tls?.cert && tls?.key);
|
|
6961
8192
|
const host = opts.host ?? "127.0.0.1";
|
|
6962
|
-
const
|
|
8193
|
+
const path15 = opts.path ?? "/metrics";
|
|
6963
8194
|
const healthPath = opts.healthPath ?? "/healthz";
|
|
6964
8195
|
const healthRegistry = opts.healthRegistry;
|
|
6965
8196
|
const listener = (req, res) => {
|
|
@@ -6969,7 +8200,7 @@ async function startMetricsServer(opts) {
|
|
|
6969
8200
|
return;
|
|
6970
8201
|
}
|
|
6971
8202
|
const url = req.url.split("?")[0];
|
|
6972
|
-
if (url ===
|
|
8203
|
+
if (url === path15) {
|
|
6973
8204
|
let body;
|
|
6974
8205
|
try {
|
|
6975
8206
|
body = renderPrometheus(opts.sink.snapshot());
|
|
@@ -7033,7 +8264,7 @@ async function startMetricsServer(opts) {
|
|
|
7033
8264
|
const protocol = useHttps ? "https" : "http";
|
|
7034
8265
|
return {
|
|
7035
8266
|
port: boundPort,
|
|
7036
|
-
url: `${protocol}://${host}:${boundPort}${
|
|
8267
|
+
url: `${protocol}://${host}:${boundPort}${path15}`,
|
|
7037
8268
|
close: () => new Promise((resolve2, reject) => {
|
|
7038
8269
|
server.close((err) => err ? reject(err) : resolve2());
|
|
7039
8270
|
})
|
|
@@ -7579,6 +8810,6 @@ var allServers = () => ({
|
|
|
7579
8810
|
sentinel: { ...sentinelServer(), enabled: false }
|
|
7580
8811
|
});
|
|
7581
8812
|
|
|
7582
|
-
export { ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, AutoCompactionMiddleware, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, ConfigMigrationError, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_SUBAGENT_BASELINE, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPermissionPolicy, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionStore, DefaultSkillLoader, DefaultTaskStore, Director, DirectorBudgetError, DoneConditionChecker, FLEET_ROSTER, FleetBus, FleetUsageAggregator, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, IntelligentCompactor, LLMSelector, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, SECURITY_SCANNER_AGENT, SelectiveCompactor, SessionAnalyzer, SpecDrivenDev, SpecParser, SubagentBudget, TaskFlow, TaskGenerator, TaskTracker, ToolExecutor, allServers, awsServer, blockServer, braveSearchServer, buildOtlpMetricsRequest, buildOtlpTracesRequest, classifyFamily, composeDirectorPrompt, composeSubagentPrompt, context7Server, contextManagerTool, createContextManagerTool, createMessage, decryptConfigSecrets, encryptConfigSecrets, everArtServer, filesystemServer, githubServer, googleMapsServer, loadProjectModes, loadUserModes, makeAgentSubagentRunner, makeDirectorSessionFactory, migratePlaintextSecrets, renderPrometheus, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, sentinelServer, slackServer, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, wireMetricsToEvents };
|
|
8813
|
+
export { ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, ConfigMigrationError, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_SUBAGENT_BASELINE, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPermissionPolicy, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionStore, DefaultSkillLoader, DefaultTaskStore, Director, DirectorBudgetError, DirectorStateCheckpoint, DoneConditionChecker, FLEET_ROSTER, FleetBus, FleetUsageAggregator, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, IntelligentCompactor, LLMSelector, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, SECURITY_SCANNER_AGENT, SelectiveCompactor, SessionAnalyzer, SpecDrivenDev, SpecParser, SubagentBudget, TaskFlow, TaskGenerator, TaskTracker, ToolExecutor, addPlanItem, allServers, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildOtlpMetricsRequest, buildOtlpTracesRequest, classifyFamily, clearPlan, composeDirectorPrompt, composeSubagentPrompt, context7Server, contextManagerTool, createContextManagerTool, createDelegateTool, createMessage, decryptConfigSecrets, emptyPlan, encryptConfigSecrets, everArtServer, filesystemServer, formatPlan, githubServer, googleMapsServer, loadDirectorState, loadPlan, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeDirectorSessionFactory, migratePlaintextSecrets, removePlanItem, renderPrometheus, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, savePlan, saveTodosCheckpoint, sentinelServer, setPlanItemStatus, slackServer, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, wireMetricsToEvents };
|
|
7583
8814
|
//# sourceMappingURL=index.js.map
|
|
7584
8815
|
//# sourceMappingURL=index.js.map
|