@wrongstack/core 0.5.5 → 0.5.7
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-B07AYFBk.d.ts → agent-bridge-BKNiE1VH.d.ts} +1 -1
- package/dist/{compactor-BWhJXxsW.d.ts → compactor-RIPuTtWK.d.ts} +1 -1
- package/dist/{config-BgM0BIpz.d.ts → config-BGGuP_Ar.d.ts} +1 -1
- package/dist/{context-CLZXPPYk.d.ts → context-CDRyrkKQ.d.ts} +7 -0
- package/dist/coordination/index.d.ts +10 -9
- package/dist/coordination/index.js +558 -61
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +19 -18
- package/dist/defaults/index.js +521 -64
- package/dist/defaults/index.js.map +1 -1
- package/dist/director-state-BmYi3DGA.d.ts +108 -0
- package/dist/{events-qnDZbrtb.d.ts → events-DPQKFX7W.d.ts} +1 -1
- package/dist/execution/index.d.ts +18 -11
- package/dist/execution/index.js +19 -3
- package/dist/execution/index.js.map +1 -1
- package/dist/extension/index.d.ts +6 -6
- package/dist/{index-De4R4rQ7.d.ts → index-Bf9Bpkdc.d.ts} +452 -9
- package/dist/{index-DPLJw_ZI.d.ts → index-j2WyAyML.d.ts} +20 -5
- package/dist/index.d.ts +108 -27
- package/dist/index.js +819 -90
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +6 -6
- package/dist/kernel/index.d.ts +9 -9
- package/dist/{mcp-servers-CSMfaBuL.d.ts → mcp-servers-DBdh3cee.d.ts} +3 -3
- package/dist/models/index.d.ts +2 -2
- package/dist/{multi-agent-Cv8wk47i.d.ts → multi-agent-Cpp7FXUl.d.ts} +11 -3
- package/dist/observability/index.d.ts +2 -2
- package/dist/{path-resolver-DiCUvEg6.d.ts → path-resolver--g_hKJ7V.d.ts} +2 -2
- package/dist/{director-state-BUxlqkOa.d.ts → plan-templates-Bveo2W8n.d.ts} +37 -76
- package/dist/{provider-runner-3SHqk9zB.d.ts → provider-runner-hl4Il3xS.d.ts} +3 -3
- package/dist/{retry-policy-LLUxJmYY.d.ts → retry-policy-LKS8MHsB.d.ts} +1 -1
- package/dist/sdd/index.d.ts +3 -3
- package/dist/{secret-scrubber-Z_VXuXQT.d.ts → secret-scrubber-BzQR5BiL.d.ts} +1 -1
- package/dist/{secret-scrubber-BhJTNr9v.d.ts → secret-scrubber-CfMdAJ_l.d.ts} +1 -1
- package/dist/security/index.d.ts +3 -3
- package/dist/{selector-DB2-byKH.d.ts → selector-C7HqnZJU.d.ts} +1 -1
- package/dist/{session-reader-4jxsYLZ8.d.ts → session-reader-CzfRA6Vk.d.ts} +1 -1
- package/dist/storage/index.d.ts +6 -5
- package/dist/storage/index.js +206 -2
- package/dist/storage/index.js.map +1 -1
- package/dist/{system-prompt-DI4Dtc1I.d.ts → system-prompt-Dl2QY1_B.d.ts} +1 -1
- package/dist/{tool-executor-DSvmOBe6.d.ts → tool-executor-DbAFkHdP.d.ts} +4 -4
- package/dist/types/index.d.ts +15 -15
- package/dist/utils/index.d.ts +1 -1
- package/package.json +1 -1
|
@@ -3,7 +3,12 @@ import * as fsp4 from 'fs/promises';
|
|
|
3
3
|
import * as path4 from 'path';
|
|
4
4
|
import { EventEmitter } from 'events';
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
7
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
8
|
+
}) : x)(function(x) {
|
|
9
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
10
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
11
|
+
});
|
|
7
12
|
async function atomicWrite(targetPath, content, opts = {}) {
|
|
8
13
|
const dir = path4.dirname(targetPath);
|
|
9
14
|
await fsp4.mkdir(dir, { recursive: true });
|
|
@@ -70,15 +75,48 @@ async function renameWithRetry(from, to) {
|
|
|
70
75
|
}
|
|
71
76
|
|
|
72
77
|
// src/storage/director-state.ts
|
|
78
|
+
async function acquireDirectorStateLock(lockPath, processId = process.pid) {
|
|
79
|
+
let existing;
|
|
80
|
+
try {
|
|
81
|
+
existing = await fsp4.readFile(lockPath, "utf8");
|
|
82
|
+
} catch {
|
|
83
|
+
}
|
|
84
|
+
if (existing) {
|
|
85
|
+
try {
|
|
86
|
+
const lock2 = JSON.parse(existing);
|
|
87
|
+
try {
|
|
88
|
+
process.kill(lock2.pid, 0);
|
|
89
|
+
return false;
|
|
90
|
+
} catch {
|
|
91
|
+
}
|
|
92
|
+
} catch {
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const lock = {
|
|
96
|
+
pid: processId,
|
|
97
|
+
hostname: __require("os").hostname(),
|
|
98
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
99
|
+
};
|
|
100
|
+
await atomicWrite(lockPath, JSON.stringify(lock), { mode: 384 });
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
async function releaseDirectorStateLock(lockPath) {
|
|
104
|
+
try {
|
|
105
|
+
await fsp4.unlink(lockPath);
|
|
106
|
+
} catch {
|
|
107
|
+
}
|
|
108
|
+
}
|
|
73
109
|
var DirectorStateCheckpoint = class {
|
|
74
110
|
snapshot;
|
|
75
111
|
filePath;
|
|
112
|
+
lockPath;
|
|
76
113
|
timer = null;
|
|
77
114
|
debounceMs;
|
|
78
115
|
writing = false;
|
|
79
116
|
rewriteRequested = false;
|
|
80
117
|
constructor(filePath, init, debounceMs = 250) {
|
|
81
118
|
this.filePath = filePath;
|
|
119
|
+
this.lockPath = `${filePath}.lock`;
|
|
82
120
|
this.debounceMs = debounceMs;
|
|
83
121
|
this.snapshot = {
|
|
84
122
|
version: 1,
|
|
@@ -88,10 +126,36 @@ var DirectorStateCheckpoint = class {
|
|
|
88
126
|
maxSpawns: init.maxSpawns,
|
|
89
127
|
spawnDepth: init.spawnDepth,
|
|
90
128
|
maxSpawnDepth: init.maxSpawnDepth,
|
|
129
|
+
directorBudget: init.directorBudget,
|
|
91
130
|
subagents: [],
|
|
92
131
|
tasks: []
|
|
93
132
|
};
|
|
94
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Attempt to acquire the lock for this checkpoint. Call this before
|
|
136
|
+
* resuming a crashed director run. If it returns false, another
|
|
137
|
+
* director process is still running this fleet — do not resume.
|
|
138
|
+
*/
|
|
139
|
+
async acquireLock() {
|
|
140
|
+
return acquireDirectorStateLock(this.lockPath);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Release the lock on graceful shutdown. Call `flush()` first to ensure
|
|
144
|
+
* the final checkpoint state is on disk before removing the lock.
|
|
145
|
+
* Without this, the next resume will see a stale-lock and refuse.
|
|
146
|
+
*/
|
|
147
|
+
async releaseLock() {
|
|
148
|
+
return releaseDirectorStateLock(this.lockPath);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Resume from a snapshot previously loaded via `loadDirectorState()`.
|
|
152
|
+
* Use this when `--resume <runId>` is triggered — the snapshot has
|
|
153
|
+
* the full fleet state (subagents, tasks) from before the crash; the
|
|
154
|
+
* checkpoint continues from there.
|
|
155
|
+
*/
|
|
156
|
+
resume(snapshot) {
|
|
157
|
+
this.snapshot = snapshot;
|
|
158
|
+
}
|
|
95
159
|
current() {
|
|
96
160
|
return this.snapshot;
|
|
97
161
|
}
|
|
@@ -1567,7 +1631,10 @@ function makeSpawnTool(director, roster) {
|
|
|
1567
1631
|
const subagentId = await director.spawn(cfg);
|
|
1568
1632
|
return { subagentId, provider: cfg.provider, model: cfg.model, name: cfg.name };
|
|
1569
1633
|
} catch (err) {
|
|
1570
|
-
if (err instanceof
|
|
1634
|
+
if (err instanceof FleetSpawnBudgetError) {
|
|
1635
|
+
return { error: err.message, kind: err.kind, limit: err.limit, observed: err.observed };
|
|
1636
|
+
}
|
|
1637
|
+
if (err instanceof FleetCostCapError) {
|
|
1571
1638
|
return { error: err.message, kind: err.kind, limit: err.limit, observed: err.observed };
|
|
1572
1639
|
}
|
|
1573
1640
|
return { error: err instanceof Error ? err.message : String(err) };
|
|
@@ -1707,9 +1774,68 @@ function makeFleetUsageTool(director) {
|
|
|
1707
1774
|
}
|
|
1708
1775
|
};
|
|
1709
1776
|
}
|
|
1777
|
+
function makeFleetSessionTool(director) {
|
|
1778
|
+
return {
|
|
1779
|
+
name: "fleet_session",
|
|
1780
|
+
description: "Read a subagent's JSONL transcript and extract its last assistant text, stop reason, and tool-use count. Use this to see what a running or timed-out subagent actually produced.",
|
|
1781
|
+
permission: "auto",
|
|
1782
|
+
mutating: false,
|
|
1783
|
+
inputSchema: {
|
|
1784
|
+
type: "object",
|
|
1785
|
+
properties: {
|
|
1786
|
+
subagentId: { type: "string", description: "Subagent id to read the transcript of." },
|
|
1787
|
+
/** Number of trailing lines to return (last N JSONL lines). Default: all. */
|
|
1788
|
+
tail: { type: "number", description: "Number of trailing JSONL lines to return. Omit for the full transcript." }
|
|
1789
|
+
},
|
|
1790
|
+
required: ["subagentId"]
|
|
1791
|
+
},
|
|
1792
|
+
async execute(input) {
|
|
1793
|
+
const i = input;
|
|
1794
|
+
const result = await director.readSession(i.subagentId, i.tail);
|
|
1795
|
+
if (!result) {
|
|
1796
|
+
return {
|
|
1797
|
+
error: `fleet_session: transcript unavailable for "${i.subagentId}". Is sessionsRoot configured?`
|
|
1798
|
+
};
|
|
1799
|
+
}
|
|
1800
|
+
return result;
|
|
1801
|
+
}
|
|
1802
|
+
};
|
|
1803
|
+
}
|
|
1804
|
+
function makeFleetHealthTool(director) {
|
|
1805
|
+
return {
|
|
1806
|
+
name: "fleet_health",
|
|
1807
|
+
description: "Per-subagent health report: budget pressure (pct of limits consumed), last activity timestamp, and current status. Use to decide whether to assign more work to a subagent or spawn a fresh one.",
|
|
1808
|
+
permission: "auto",
|
|
1809
|
+
mutating: false,
|
|
1810
|
+
inputSchema: { type: "object", properties: {}, required: [] },
|
|
1811
|
+
async execute() {
|
|
1812
|
+
const status = director.status();
|
|
1813
|
+
const snapshot = director.snapshot();
|
|
1814
|
+
const subagents = status.subagents ?? [];
|
|
1815
|
+
const perSubagent = snapshot.perSubagent ?? {};
|
|
1816
|
+
return {
|
|
1817
|
+
subagents: subagents.map((s) => {
|
|
1818
|
+
const usage = perSubagent[s.id];
|
|
1819
|
+
return {
|
|
1820
|
+
id: s.id,
|
|
1821
|
+
status: s.status,
|
|
1822
|
+
lastEventAt: usage?.lastEventAt,
|
|
1823
|
+
// Budget pressure: fraction of each limit consumed if we have it.
|
|
1824
|
+
// BudgetWarning events carry used/limit ratios; surface them here.
|
|
1825
|
+
budgetPressure: {
|
|
1826
|
+
iterations: usage?.iterations,
|
|
1827
|
+
toolCalls: usage?.toolCalls,
|
|
1828
|
+
costUsd: usage?.cost
|
|
1829
|
+
}
|
|
1830
|
+
};
|
|
1831
|
+
})
|
|
1832
|
+
};
|
|
1833
|
+
}
|
|
1834
|
+
};
|
|
1835
|
+
}
|
|
1710
1836
|
|
|
1711
1837
|
// src/coordination/director.ts
|
|
1712
|
-
var
|
|
1838
|
+
var FleetSpawnBudgetError = class extends Error {
|
|
1713
1839
|
kind;
|
|
1714
1840
|
limit;
|
|
1715
1841
|
observed;
|
|
@@ -1717,16 +1843,48 @@ var DirectorBudgetError = class extends Error {
|
|
|
1717
1843
|
super(
|
|
1718
1844
|
kind === "max_spawns" ? `Director spawn budget exceeded: tried to spawn #${observed} but maxSpawns is ${limit}` : `Director spawn depth budget exceeded: this director is at depth ${observed} and maxSpawnDepth is ${limit}`
|
|
1719
1845
|
);
|
|
1720
|
-
this.name = "
|
|
1846
|
+
this.name = "FleetSpawnBudgetError";
|
|
1721
1847
|
this.kind = kind;
|
|
1722
1848
|
this.limit = limit;
|
|
1723
1849
|
this.observed = observed;
|
|
1724
1850
|
}
|
|
1725
1851
|
};
|
|
1852
|
+
var FleetCostCapError = class extends Error {
|
|
1853
|
+
kind;
|
|
1854
|
+
limit;
|
|
1855
|
+
observed;
|
|
1856
|
+
constructor(limit, observed) {
|
|
1857
|
+
super(
|
|
1858
|
+
`Director cost cap exceeded: total fleet spend ${observed.toFixed(4)} exceeds maxCostUsd ${limit.toFixed(4)}`
|
|
1859
|
+
);
|
|
1860
|
+
this.name = "FleetCostCapError";
|
|
1861
|
+
this.kind = "max_cost_usd";
|
|
1862
|
+
this.limit = limit;
|
|
1863
|
+
this.observed = observed;
|
|
1864
|
+
}
|
|
1865
|
+
};
|
|
1726
1866
|
var Director = class {
|
|
1867
|
+
/** Alias for the ICoordinator contract. `id` is retained for backward compatibility. */
|
|
1868
|
+
get coordinatorId() {
|
|
1869
|
+
return this.id;
|
|
1870
|
+
}
|
|
1727
1871
|
id;
|
|
1872
|
+
/**
|
|
1873
|
+
* The fleet event bus. Backed by `fleetManager?.fleet` when a FleetManager
|
|
1874
|
+
* is injected; otherwise own FleetBus instance (preserves existing behavior).
|
|
1875
|
+
*/
|
|
1728
1876
|
fleet;
|
|
1877
|
+
/**
|
|
1878
|
+
* Usage rollup. Backed by `fleetManager?.usage` when a FleetManager is
|
|
1879
|
+
* injected; otherwise own FleetUsageAggregator.
|
|
1880
|
+
*/
|
|
1729
1881
|
usage;
|
|
1882
|
+
/**
|
|
1883
|
+
* Optional fleet-level policy container. When provided the Director
|
|
1884
|
+
* delegates spawn budgeting, manifest entries, and checkpointing to it
|
|
1885
|
+
* instead of managing those internally. All other behavior is unchanged.
|
|
1886
|
+
*/
|
|
1887
|
+
fleetManager;
|
|
1730
1888
|
/**
|
|
1731
1889
|
* Director-side bridge endpoint. Subagents are wired to the same
|
|
1732
1890
|
* in-memory transport so the director can `ask()` them synchronously
|
|
@@ -1777,6 +1935,14 @@ var Director = class {
|
|
|
1777
1935
|
/** Debounce timer for periodic manifest writes. */
|
|
1778
1936
|
manifestTimer = null;
|
|
1779
1937
|
manifestDebounceMs;
|
|
1938
|
+
/** Fleet-wide cost cap (entire fleet total, distinct from SubagentBudget limits). Infinity means no cap. */
|
|
1939
|
+
maxFleetCostUsd;
|
|
1940
|
+
/** Max auto-extensions per subagent per budget kind before denying. */
|
|
1941
|
+
maxBudgetExtensions;
|
|
1942
|
+
/** Sessions root for direct subagent JSONL reads (fleet_session tool). */
|
|
1943
|
+
sessionsRoot;
|
|
1944
|
+
/** Director run id for JSONL path resolution. */
|
|
1945
|
+
directorRunId;
|
|
1780
1946
|
/** Resolves task descriptions back from `assign()` so completion events
|
|
1781
1947
|
* can also carry a human-readable title. */
|
|
1782
1948
|
taskDescriptions = /* @__PURE__ */ new Map();
|
|
@@ -1803,12 +1969,18 @@ var Director = class {
|
|
|
1803
1969
|
this.spawnDepth = opts.spawnDepth ?? 0;
|
|
1804
1970
|
this.sessionWriter = opts.sessionWriter ?? null;
|
|
1805
1971
|
this.manifestDebounceMs = opts.manifestDebounceMs ?? 2e3;
|
|
1972
|
+
this.maxFleetCostUsd = opts.directorBudget?.maxCostUsd ?? Number.POSITIVE_INFINITY;
|
|
1973
|
+
this.maxBudgetExtensions = opts.maxBudgetExtensions ?? 2;
|
|
1974
|
+
this.sessionsRoot = opts.sessionsRoot;
|
|
1975
|
+
this.directorRunId = opts.directorRunId ?? this.id;
|
|
1806
1976
|
this.stateCheckpoint = opts.stateCheckpointPath ? new DirectorStateCheckpoint(opts.stateCheckpointPath, {
|
|
1807
1977
|
directorRunId: this.id,
|
|
1808
1978
|
maxSpawns: opts.maxSpawns,
|
|
1809
1979
|
spawnDepth: this.spawnDepth,
|
|
1810
|
-
maxSpawnDepth: this.maxSpawnDepth
|
|
1811
|
-
|
|
1980
|
+
maxSpawnDepth: this.maxSpawnDepth,
|
|
1981
|
+
directorBudget: opts.directorBudget
|
|
1982
|
+
}, opts.checkpointDebounceMs ?? 250) : null;
|
|
1983
|
+
this.fleetManager = opts.fleetManager;
|
|
1812
1984
|
if (this.sharedScratchpadPath) {
|
|
1813
1985
|
void fsp4.mkdir(this.sharedScratchpadPath, { recursive: true }).catch(() => void 0);
|
|
1814
1986
|
}
|
|
@@ -1817,12 +1989,17 @@ var Director = class {
|
|
|
1817
1989
|
{ agentId: this.id, coordinatorId: this.id },
|
|
1818
1990
|
this.transport
|
|
1819
1991
|
);
|
|
1820
|
-
this.
|
|
1821
|
-
|
|
1822
|
-
this.
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1992
|
+
if (this.fleetManager) {
|
|
1993
|
+
this.fleet = this.fleetManager.fleet;
|
|
1994
|
+
this.usage = this.fleetManager.usage;
|
|
1995
|
+
} else {
|
|
1996
|
+
this.fleet = new FleetBus();
|
|
1997
|
+
this.usage = new FleetUsageAggregator(
|
|
1998
|
+
this.fleet,
|
|
1999
|
+
(id) => this.priceLookups.get(id),
|
|
2000
|
+
(id) => this.subagentMeta.get(id)
|
|
2001
|
+
);
|
|
2002
|
+
}
|
|
1826
2003
|
this.coordinator = new DefaultMultiAgentCoordinator(
|
|
1827
2004
|
{ ...opts.config, coordinatorId: this.id },
|
|
1828
2005
|
{ runner: opts.runner }
|
|
@@ -1869,7 +2046,7 @@ var Director = class {
|
|
|
1869
2046
|
const payload = e.payload;
|
|
1870
2047
|
const guardKey = `${e.subagentId}:${payload.kind}`;
|
|
1871
2048
|
const prior = extendCounts.get(guardKey) ?? 0;
|
|
1872
|
-
if (prior >=
|
|
2049
|
+
if (prior >= this.maxBudgetExtensions) {
|
|
1873
2050
|
payload.deny();
|
|
1874
2051
|
extendCounts.delete(guardKey);
|
|
1875
2052
|
return;
|
|
@@ -1924,52 +2101,73 @@ var Director = class {
|
|
|
1924
2101
|
* it the `cost` column in `usage.snapshot()` stays at 0.
|
|
1925
2102
|
*/
|
|
1926
2103
|
async spawn(config, priceLookup) {
|
|
1927
|
-
if (this.
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
2104
|
+
if (this.fleetManager) {
|
|
2105
|
+
const rejection = this.fleetManager.canSpawn(config);
|
|
2106
|
+
if (rejection) {
|
|
2107
|
+
if (rejection.kind === "max_spawn_depth") throw new FleetSpawnBudgetError("max_spawn_depth", rejection.limit, rejection.observed);
|
|
2108
|
+
if (rejection.kind === "max_spawns") throw new FleetSpawnBudgetError("max_spawns", rejection.limit, rejection.observed);
|
|
2109
|
+
if (rejection.kind === "max_cost_usd") throw new FleetCostCapError(rejection.limit, rejection.observed);
|
|
2110
|
+
}
|
|
2111
|
+
} else {
|
|
2112
|
+
if (this.spawnDepth >= this.maxSpawnDepth) {
|
|
2113
|
+
throw new FleetSpawnBudgetError("max_spawn_depth", this.maxSpawnDepth, this.spawnDepth);
|
|
2114
|
+
}
|
|
2115
|
+
if (this.spawnCount >= this.maxSpawns) {
|
|
2116
|
+
throw new FleetSpawnBudgetError("max_spawns", this.maxSpawns, this.spawnCount + 1);
|
|
2117
|
+
}
|
|
2118
|
+
if (this.maxFleetCostUsd < Number.POSITIVE_INFINITY) {
|
|
2119
|
+
const totalCost = this.usage.snapshot().total?.cost ?? 0;
|
|
2120
|
+
if (totalCost >= this.maxFleetCostUsd) {
|
|
2121
|
+
throw new FleetCostCapError(this.maxFleetCostUsd, totalCost);
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
1932
2124
|
}
|
|
1933
2125
|
const result = await this.coordinator.spawn(config);
|
|
1934
|
-
this.
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
2126
|
+
if (this.fleetManager) {
|
|
2127
|
+
this.fleetManager.recordSpawn(result.subagentId, config, priceLookup);
|
|
2128
|
+
} else {
|
|
2129
|
+
this.spawnCount += 1;
|
|
2130
|
+
this.subagentMeta.set(result.subagentId, {
|
|
2131
|
+
provider: config.provider,
|
|
2132
|
+
model: config.model
|
|
2133
|
+
});
|
|
2134
|
+
if (priceLookup) this.priceLookups.set(result.subagentId, priceLookup);
|
|
2135
|
+
}
|
|
1940
2136
|
const subagentBridge = new InMemoryAgentBridge(
|
|
1941
2137
|
{ agentId: result.subagentId, coordinatorId: this.id },
|
|
1942
2138
|
this.transport
|
|
1943
2139
|
);
|
|
1944
2140
|
this.coordinator.setSubagentBridge(result.subagentId, subagentBridge);
|
|
1945
2141
|
this.subagentBridges.set(result.subagentId, subagentBridge);
|
|
1946
|
-
this.
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
role: config.role,
|
|
1950
|
-
provider: config.provider,
|
|
1951
|
-
model: config.model,
|
|
1952
|
-
taskIds: []
|
|
1953
|
-
});
|
|
1954
|
-
const spawnedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1955
|
-
this.stateCheckpoint?.recordSpawn(
|
|
1956
|
-
{
|
|
1957
|
-
id: result.subagentId,
|
|
2142
|
+
if (!this.fleetManager) {
|
|
2143
|
+
this.manifestEntries.set(result.subagentId, {
|
|
2144
|
+
subagentId: result.subagentId,
|
|
1958
2145
|
name: config.name,
|
|
1959
2146
|
role: config.role,
|
|
1960
2147
|
provider: config.provider,
|
|
1961
2148
|
model: config.model,
|
|
1962
|
-
|
|
1963
|
-
}
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
2149
|
+
taskIds: []
|
|
2150
|
+
});
|
|
2151
|
+
const spawnedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2152
|
+
this.stateCheckpoint?.recordSpawn(
|
|
2153
|
+
{
|
|
2154
|
+
id: result.subagentId,
|
|
2155
|
+
name: config.name,
|
|
2156
|
+
role: config.role,
|
|
2157
|
+
provider: config.provider,
|
|
2158
|
+
model: config.model,
|
|
2159
|
+
spawnedAt
|
|
2160
|
+
},
|
|
2161
|
+
this.spawnCount
|
|
2162
|
+
);
|
|
2163
|
+
void this.appendSessionEvent({
|
|
2164
|
+
type: "agent_spawned",
|
|
2165
|
+
ts: spawnedAt,
|
|
2166
|
+
agentId: result.subagentId,
|
|
2167
|
+
role: config.role ?? config.name
|
|
2168
|
+
});
|
|
2169
|
+
this.scheduleManifest();
|
|
2170
|
+
}
|
|
1973
2171
|
return result.subagentId;
|
|
1974
2172
|
}
|
|
1975
2173
|
/**
|
|
@@ -2109,6 +2307,7 @@ var Director = class {
|
|
|
2109
2307
|
if (this.stateCheckpoint) {
|
|
2110
2308
|
this.stateCheckpoint.setUsage(this.usage.snapshot());
|
|
2111
2309
|
await this.stateCheckpoint.flush().catch((err) => this.logShutdownError("state_checkpoint_flush", err));
|
|
2310
|
+
await this.stateCheckpoint.releaseLock().catch((err) => this.logShutdownError("state_checkpoint_lock_release", err));
|
|
2112
2311
|
}
|
|
2113
2312
|
}
|
|
2114
2313
|
/**
|
|
@@ -2135,8 +2334,12 @@ var Director = class {
|
|
|
2135
2334
|
async assign(task) {
|
|
2136
2335
|
const taskWithId = task.id ? task : { ...task, id: randomUUID() };
|
|
2137
2336
|
if (task.subagentId) {
|
|
2138
|
-
|
|
2139
|
-
|
|
2337
|
+
if (this.fleetManager) {
|
|
2338
|
+
this.fleetManager.addTaskToSubagent(task.subagentId, taskWithId.id);
|
|
2339
|
+
} else {
|
|
2340
|
+
const entry = this.manifestEntries.get(task.subagentId);
|
|
2341
|
+
if (entry) entry.taskIds.push(taskWithId.id);
|
|
2342
|
+
}
|
|
2140
2343
|
}
|
|
2141
2344
|
await this.coordinator.assign(taskWithId);
|
|
2142
2345
|
this.taskDescriptions.set(taskWithId.id, taskWithId.description);
|
|
@@ -2212,6 +2415,58 @@ var Director = class {
|
|
|
2212
2415
|
completedResults() {
|
|
2213
2416
|
return Array.from(this.completed.values());
|
|
2214
2417
|
}
|
|
2418
|
+
/**
|
|
2419
|
+
* Inject a previously-saved checkpoint snapshot. Call this right after
|
|
2420
|
+
* constructing a Director during a `--resume` run so the in-memory state
|
|
2421
|
+
* (subagents, tasks, waiters) reflects the pre-crash reality instead of
|
|
2422
|
+
* starting from a blank slate. The director then resumes from there —
|
|
2423
|
+
* completing any in-flight tasks and ignoring tasks that already reached
|
|
2424
|
+
* a terminal state in the prior run.
|
|
2425
|
+
*/
|
|
2426
|
+
setCheckpointState(snapshot) {
|
|
2427
|
+
this.stateCheckpoint?.resume(snapshot);
|
|
2428
|
+
}
|
|
2429
|
+
/**
|
|
2430
|
+
* Read a subagent's JSONL transcript directly from disk (no bridge
|
|
2431
|
+
* round-trip needed). Returns the last assistant text, stop reason,
|
|
2432
|
+
* tool-use count, and line count — or null if the file is unavailable.
|
|
2433
|
+
* Requires `sessionsRoot` to be set on construction.
|
|
2434
|
+
*/
|
|
2435
|
+
async readSession(subagentId, tail) {
|
|
2436
|
+
if (!this.sessionsRoot) return null;
|
|
2437
|
+
const filePath = path4.join(this.sessionsRoot, this.directorRunId, `${subagentId}.jsonl`);
|
|
2438
|
+
let raw;
|
|
2439
|
+
try {
|
|
2440
|
+
raw = await fsp4.readFile(filePath, "utf8");
|
|
2441
|
+
} catch {
|
|
2442
|
+
return null;
|
|
2443
|
+
}
|
|
2444
|
+
const lines = raw.split("\n").filter((l) => l.trim());
|
|
2445
|
+
const targetLines = tail ? lines.slice(-tail) : lines;
|
|
2446
|
+
let lastAssistantText;
|
|
2447
|
+
let lastStopReason;
|
|
2448
|
+
let toolUses = 0;
|
|
2449
|
+
for (const line of targetLines) {
|
|
2450
|
+
try {
|
|
2451
|
+
const ev = JSON.parse(line);
|
|
2452
|
+
if (ev.type === "assistant" && typeof ev.text === "string") {
|
|
2453
|
+
lastAssistantText = ev.text;
|
|
2454
|
+
} else if (ev.type === "stop" && ev.stopReason) {
|
|
2455
|
+
lastStopReason = ev.stopReason;
|
|
2456
|
+
} else if (ev.type === "tool_use") {
|
|
2457
|
+
toolUses++;
|
|
2458
|
+
}
|
|
2459
|
+
} catch {
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
return {
|
|
2463
|
+
lastAssistantText,
|
|
2464
|
+
lastStopReason,
|
|
2465
|
+
toolUsesObserved: toolUses,
|
|
2466
|
+
events: targetLines.length,
|
|
2467
|
+
path: filePath
|
|
2468
|
+
};
|
|
2469
|
+
}
|
|
2215
2470
|
snapshot() {
|
|
2216
2471
|
return this.usage.snapshot();
|
|
2217
2472
|
}
|
|
@@ -2282,18 +2537,37 @@ var Director = class {
|
|
|
2282
2537
|
* still permission-checked normally.
|
|
2283
2538
|
*/
|
|
2284
2539
|
tools(roster) {
|
|
2540
|
+
const effectiveRoster = roster ?? this.roster;
|
|
2285
2541
|
const t = [
|
|
2286
|
-
makeSpawnTool(this,
|
|
2542
|
+
makeSpawnTool(this, effectiveRoster),
|
|
2287
2543
|
makeAssignTool(this),
|
|
2288
2544
|
makeAwaitTasksTool(this),
|
|
2289
2545
|
makeAskTool(this),
|
|
2290
2546
|
makeRollUpTool(this),
|
|
2291
2547
|
makeTerminateTool(this),
|
|
2292
2548
|
makeFleetStatusTool(this),
|
|
2293
|
-
makeFleetUsageTool(this)
|
|
2549
|
+
makeFleetUsageTool(this),
|
|
2550
|
+
makeFleetSessionTool(this),
|
|
2551
|
+
makeFleetHealthTool(this)
|
|
2294
2552
|
];
|
|
2295
2553
|
return t;
|
|
2296
2554
|
}
|
|
2555
|
+
/**
|
|
2556
|
+
* Attempt to acquire the checkpoint lock. Must be called before
|
|
2557
|
+
* resuming — if another director process is alive, this returns
|
|
2558
|
+
* false and the caller should not proceed with the resume.
|
|
2559
|
+
*/
|
|
2560
|
+
async acquireCheckpointLock() {
|
|
2561
|
+
return this.stateCheckpoint ? this.stateCheckpoint.acquireLock() : true;
|
|
2562
|
+
}
|
|
2563
|
+
/**
|
|
2564
|
+
* Resume from a prior checkpoint snapshot (loaded via
|
|
2565
|
+
* `loadDirectorState()`). Re-attach to the fleet mid-flight so
|
|
2566
|
+
* subsequent spawn/assign calls update the checkpoint normally.
|
|
2567
|
+
*/
|
|
2568
|
+
resumeFromCheckpoint(snapshot) {
|
|
2569
|
+
this.stateCheckpoint?.resume(snapshot);
|
|
2570
|
+
}
|
|
2297
2571
|
};
|
|
2298
2572
|
function createDelegateTool(opts) {
|
|
2299
2573
|
const defaultTimeoutMs = opts.defaultTimeoutMs ?? 4 * 60 * 60 * 1e3;
|
|
@@ -2399,7 +2673,7 @@ function createDelegateTool(opts) {
|
|
|
2399
2673
|
if (typeof i.maxToolCalls === "number" && i.maxToolCalls > 0) {
|
|
2400
2674
|
cfg.maxToolCalls = i.maxToolCalls;
|
|
2401
2675
|
}
|
|
2402
|
-
const SUBAGENT_TIMEOUT_BUFFER_MS = 3e4;
|
|
2676
|
+
const SUBAGENT_TIMEOUT_BUFFER_MS = opts.subagentTimeoutBufferMs ?? 3e4;
|
|
2403
2677
|
const desiredSubTimeout = Math.max(3e4, timeoutMs - SUBAGENT_TIMEOUT_BUFFER_MS);
|
|
2404
2678
|
if (!cfg.timeoutMs || cfg.timeoutMs > desiredSubTimeout) {
|
|
2405
2679
|
cfg.timeoutMs = desiredSubTimeout;
|
|
@@ -2451,7 +2725,7 @@ function createDelegateTool(opts) {
|
|
|
2451
2725
|
toolCalls: result.toolCalls,
|
|
2452
2726
|
durationMs: result.durationMs,
|
|
2453
2727
|
...partial ? { partial } : {},
|
|
2454
|
-
...hintForKind(errorKind, retryable, backoffMs) ? { hint: hintForKind(errorKind, retryable, backoffMs) } : {}
|
|
2728
|
+
...hintForKind(errorKind, retryable, backoffMs, partial) ? { hint: hintForKind(errorKind, retryable, backoffMs, partial) } : {}
|
|
2455
2729
|
};
|
|
2456
2730
|
} catch (err) {
|
|
2457
2731
|
return {
|
|
@@ -2471,7 +2745,7 @@ function instantiateRosterConfig2(role, base) {
|
|
|
2471
2745
|
id: `${role}-${randomUUID().slice(0, 8)}`
|
|
2472
2746
|
};
|
|
2473
2747
|
}
|
|
2474
|
-
function hintForKind(kind, retryable, backoffMs) {
|
|
2748
|
+
function hintForKind(kind, retryable, backoffMs, partial) {
|
|
2475
2749
|
if (!kind) return void 0;
|
|
2476
2750
|
switch (kind) {
|
|
2477
2751
|
case "provider_rate_limit":
|
|
@@ -2487,16 +2761,40 @@ function hintForKind(kind, retryable, backoffMs) {
|
|
|
2487
2761
|
case "budget_iterations":
|
|
2488
2762
|
case "budget_tool_calls":
|
|
2489
2763
|
case "budget_tokens":
|
|
2490
|
-
case "budget_cost":
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2764
|
+
case "budget_cost": {
|
|
2765
|
+
const base = "Subagent exhausted its budget. The coordinator may auto-extend; otherwise raise the matching `max*` field (e.g. maxToolCalls: 600) on the next delegate, or split the task.";
|
|
2766
|
+
if (partial?.lastAssistantText) {
|
|
2767
|
+
return `${base}
|
|
2768
|
+
|
|
2769
|
+
Partial output produced before budget hit:
|
|
2770
|
+
${partial.lastAssistantText}`;
|
|
2771
|
+
}
|
|
2772
|
+
return base;
|
|
2773
|
+
}
|
|
2774
|
+
case "budget_timeout": {
|
|
2775
|
+
const base = "Subagent hit its wall-clock budget. Raise `timeoutMs` on the next delegate or split the task.";
|
|
2776
|
+
if (partial?.lastAssistantText) {
|
|
2777
|
+
return `${base}
|
|
2778
|
+
|
|
2779
|
+
Partial output produced before timeout:
|
|
2780
|
+
${partial.lastAssistantText}`;
|
|
2781
|
+
}
|
|
2782
|
+
return base;
|
|
2783
|
+
}
|
|
2494
2784
|
case "aborted_by_parent":
|
|
2495
2785
|
return "Subagent was aborted (user Ctrl+C, parent unwound, or sibling failure cascade). Not retryable until the abort condition is resolved.";
|
|
2496
2786
|
case "empty_response":
|
|
2497
2787
|
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.";
|
|
2498
|
-
case "tool_failed":
|
|
2499
|
-
|
|
2788
|
+
case "tool_failed": {
|
|
2789
|
+
const base = "A tool inside the subagent returned ok:false. Retry with corrected inputs.";
|
|
2790
|
+
if (partial?.lastAssistantText) {
|
|
2791
|
+
return `${base}
|
|
2792
|
+
|
|
2793
|
+
Agent reasoning before failure:
|
|
2794
|
+
${partial.lastAssistantText}`;
|
|
2795
|
+
}
|
|
2796
|
+
return base;
|
|
2797
|
+
}
|
|
2500
2798
|
case "bridge_failed":
|
|
2501
2799
|
return "Parent-child bridge transport failed. This is rare \u2014 restart the session and retry.";
|
|
2502
2800
|
default:
|
|
@@ -3229,6 +3527,205 @@ function makeDirectorSessionFactory(opts) {
|
|
|
3229
3527
|
};
|
|
3230
3528
|
}
|
|
3231
3529
|
|
|
3232
|
-
|
|
3530
|
+
// src/coordination/null-fleet-bus.ts
|
|
3531
|
+
var NULL_FLEET_BUS = new FleetBus();
|
|
3532
|
+
var FleetManager = class {
|
|
3533
|
+
/** The fleet-wide event bus. */
|
|
3534
|
+
fleet;
|
|
3535
|
+
/** Usage rollup across all subagents. */
|
|
3536
|
+
usage;
|
|
3537
|
+
manifestPath;
|
|
3538
|
+
sessionsRoot;
|
|
3539
|
+
directorRunId;
|
|
3540
|
+
/** Spawn cap (lifetime total). Infinity means unlimited. */
|
|
3541
|
+
maxSpawns;
|
|
3542
|
+
/** Nesting cap. */
|
|
3543
|
+
maxSpawnDepth;
|
|
3544
|
+
/** This director's depth in a director chain. Root = 0. */
|
|
3545
|
+
spawnDepth;
|
|
3546
|
+
/** Live spawn counter. */
|
|
3547
|
+
spawnCount = 0;
|
|
3548
|
+
stateCheckpoint;
|
|
3549
|
+
sessionWriter;
|
|
3550
|
+
manifestTimer = null;
|
|
3551
|
+
manifestDebounceMs;
|
|
3552
|
+
/** Fleet-wide cost cap. Infinity = no cap. Distinct from SubagentBudget limits,
|
|
3553
|
+
* which track per-subagent spend — this field caps the entire fleet total. */
|
|
3554
|
+
maxFleetCostUsd;
|
|
3555
|
+
manifestEntries = /* @__PURE__ */ new Map();
|
|
3556
|
+
/** Pending tasks with their descriptions — populated by `addPendingTask`
|
|
3557
|
+
* and cleared by `removePendingTask`. Replaces the host-side `pending`
|
|
3558
|
+
* Map so task descriptions live in one place (FleetManager). */
|
|
3559
|
+
pendingTasks = /* @__PURE__ */ new Map();
|
|
3560
|
+
subagentMeta = /* @__PURE__ */ new Map();
|
|
3561
|
+
priceLookups = /* @__PURE__ */ new Map();
|
|
3562
|
+
constructor(opts = {}) {
|
|
3563
|
+
this.manifestPath = opts.manifestPath;
|
|
3564
|
+
this.sessionsRoot = opts.sessionsRoot;
|
|
3565
|
+
this.directorRunId = opts.directorRunId ?? randomUUID();
|
|
3566
|
+
this.maxSpawns = opts.maxSpawns ?? Number.POSITIVE_INFINITY;
|
|
3567
|
+
this.maxSpawnDepth = opts.maxSpawnDepth ?? 2;
|
|
3568
|
+
this.spawnDepth = opts.spawnDepth ?? 0;
|
|
3569
|
+
this.sessionWriter = opts.sessionWriter ?? null;
|
|
3570
|
+
this.manifestDebounceMs = opts.manifestDebounceMs ?? 2e3;
|
|
3571
|
+
this.maxFleetCostUsd = opts.directorBudget?.maxCostUsd ?? Number.POSITIVE_INFINITY;
|
|
3572
|
+
this.stateCheckpoint = opts.stateCheckpointPath ? new DirectorStateCheckpoint(
|
|
3573
|
+
opts.stateCheckpointPath,
|
|
3574
|
+
{
|
|
3575
|
+
directorRunId: this.directorRunId,
|
|
3576
|
+
maxSpawns: opts.maxSpawns,
|
|
3577
|
+
spawnDepth: this.spawnDepth,
|
|
3578
|
+
maxSpawnDepth: this.maxSpawnDepth,
|
|
3579
|
+
directorBudget: opts.directorBudget
|
|
3580
|
+
},
|
|
3581
|
+
opts.checkpointDebounceMs ?? 250
|
|
3582
|
+
) : null;
|
|
3583
|
+
this.fleet = new FleetBus();
|
|
3584
|
+
this.usage = new FleetUsageAggregator(
|
|
3585
|
+
this.fleet,
|
|
3586
|
+
(id) => this.priceLookups.get(id),
|
|
3587
|
+
(id) => this.subagentMeta.get(id)
|
|
3588
|
+
);
|
|
3589
|
+
}
|
|
3590
|
+
// -----------------------------------------------------------------------
|
|
3591
|
+
// IFleetManager surface
|
|
3592
|
+
// -----------------------------------------------------------------------
|
|
3593
|
+
get fleetBus() {
|
|
3594
|
+
return this.fleet;
|
|
3595
|
+
}
|
|
3596
|
+
snapshot() {
|
|
3597
|
+
return this.usage.snapshot();
|
|
3598
|
+
}
|
|
3599
|
+
getSubagentMeta(id) {
|
|
3600
|
+
return this.subagentMeta.get(id);
|
|
3601
|
+
}
|
|
3602
|
+
/**
|
|
3603
|
+
* Returns null if the spawn is allowed, or an object describing
|
|
3604
|
+
* which cap was exceeded. Does NOT throw — the caller decides
|
|
3605
|
+
* how to surface the rejection.
|
|
3606
|
+
*/
|
|
3607
|
+
canSpawn(config) {
|
|
3608
|
+
if (this.spawnDepth >= this.maxSpawnDepth) {
|
|
3609
|
+
return { kind: "max_spawn_depth", limit: this.maxSpawnDepth, observed: this.spawnDepth };
|
|
3610
|
+
}
|
|
3611
|
+
if (this.spawnCount >= this.maxSpawns) {
|
|
3612
|
+
return { kind: "max_spawns", limit: this.maxSpawns, observed: this.spawnCount + 1 };
|
|
3613
|
+
}
|
|
3614
|
+
if (this.maxFleetCostUsd < Number.POSITIVE_INFINITY) {
|
|
3615
|
+
const totalCost = this.usage.snapshot().total?.cost ?? 0;
|
|
3616
|
+
if (totalCost >= this.maxFleetCostUsd) {
|
|
3617
|
+
return { kind: "max_cost_usd", limit: this.maxFleetCostUsd, observed: totalCost };
|
|
3618
|
+
}
|
|
3619
|
+
}
|
|
3620
|
+
return null;
|
|
3621
|
+
}
|
|
3622
|
+
/**
|
|
3623
|
+
* Records a spawn: increments counter, stores metadata, updates state checkpoint,
|
|
3624
|
+
* and schedules a debounced manifest write. Call AFTER the coordinator
|
|
3625
|
+
* has successfully spawned the subagent.
|
|
3626
|
+
*
|
|
3627
|
+
* @param subagentId The subagent's id (from coordinator.spawn result)
|
|
3628
|
+
* @param config The SubagentConfig that was used
|
|
3629
|
+
* @param priceLookup Optional per-subagent pricing data
|
|
3630
|
+
*/
|
|
3631
|
+
recordSpawn(subagentId, config, priceLookup) {
|
|
3632
|
+
this.spawnCount += 1;
|
|
3633
|
+
this.subagentMeta.set(subagentId, {
|
|
3634
|
+
provider: config.provider,
|
|
3635
|
+
model: config.model
|
|
3636
|
+
});
|
|
3637
|
+
if (priceLookup) this.priceLookups.set(subagentId, priceLookup);
|
|
3638
|
+
this.manifestEntries.set(subagentId, {
|
|
3639
|
+
subagentId,
|
|
3640
|
+
name: config.name,
|
|
3641
|
+
role: config.role,
|
|
3642
|
+
provider: config.provider,
|
|
3643
|
+
model: config.model,
|
|
3644
|
+
taskIds: []
|
|
3645
|
+
});
|
|
3646
|
+
this.stateCheckpoint?.recordSpawn({
|
|
3647
|
+
id: subagentId,
|
|
3648
|
+
name: config.name,
|
|
3649
|
+
role: config.role,
|
|
3650
|
+
provider: config.provider,
|
|
3651
|
+
model: config.model,
|
|
3652
|
+
spawnedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3653
|
+
}, this.spawnCount);
|
|
3654
|
+
void this.appendSessionEvent({
|
|
3655
|
+
type: "agent_spawned",
|
|
3656
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3657
|
+
agentId: subagentId,
|
|
3658
|
+
role: config.role ?? config.name
|
|
3659
|
+
});
|
|
3660
|
+
this.scheduleManifest();
|
|
3661
|
+
}
|
|
3662
|
+
async writeManifest() {
|
|
3663
|
+
if (!this.manifestPath) return null;
|
|
3664
|
+
const manifest = {
|
|
3665
|
+
version: 1,
|
|
3666
|
+
directorRunId: this.directorRunId,
|
|
3667
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3668
|
+
children: Array.from(this.manifestEntries.values()).map((entry) => ({
|
|
3669
|
+
id: entry.subagentId,
|
|
3670
|
+
name: entry.name,
|
|
3671
|
+
role: entry.role,
|
|
3672
|
+
provider: entry.provider,
|
|
3673
|
+
model: entry.model,
|
|
3674
|
+
taskIds: entry.taskIds
|
|
3675
|
+
})),
|
|
3676
|
+
usage: this.usage.snapshot()
|
|
3677
|
+
};
|
|
3678
|
+
await fsp4.mkdir(path4.dirname(this.manifestPath), { recursive: true });
|
|
3679
|
+
await fsp4.writeFile(this.manifestPath, JSON.stringify(manifest, null, 2), { mode: 384 });
|
|
3680
|
+
return this.manifestPath;
|
|
3681
|
+
}
|
|
3682
|
+
/**
|
|
3683
|
+
* Attach task ids to an already-spawned subagent. Called by
|
|
3684
|
+
* `Director.assign()` after the coordinator assigns a task.
|
|
3685
|
+
*/
|
|
3686
|
+
addTaskToSubagent(subagentId, taskId) {
|
|
3687
|
+
const entry = this.manifestEntries.get(subagentId);
|
|
3688
|
+
if (entry) entry.taskIds.push(taskId);
|
|
3689
|
+
}
|
|
3690
|
+
/**
|
|
3691
|
+
* Debounced manifest write. Call after any state mutation
|
|
3692
|
+
* (spawn, assign, complete) so a burst collapses into one write.
|
|
3693
|
+
*/
|
|
3694
|
+
scheduleManifest() {
|
|
3695
|
+
if (!this.manifestPath || this.manifestDebounceMs <= 0) return;
|
|
3696
|
+
if (this.manifestTimer) return;
|
|
3697
|
+
this.manifestTimer = setTimeout(() => {
|
|
3698
|
+
this.manifestTimer = null;
|
|
3699
|
+
void this.writeManifest().catch(() => void 0);
|
|
3700
|
+
}, this.manifestDebounceMs);
|
|
3701
|
+
}
|
|
3702
|
+
/** Best-effort session event writer. Swallows failures. */
|
|
3703
|
+
async appendSessionEvent(event) {
|
|
3704
|
+
if (!this.sessionWriter) return;
|
|
3705
|
+
try {
|
|
3706
|
+
await this.sessionWriter.append(event);
|
|
3707
|
+
} catch {
|
|
3708
|
+
}
|
|
3709
|
+
}
|
|
3710
|
+
// -----------------------------------------------------------------------
|
|
3711
|
+
// Pending task management — eliminates host-side state duplication
|
|
3712
|
+
// -----------------------------------------------------------------------
|
|
3713
|
+
addPendingTask(taskId, subagentId, description) {
|
|
3714
|
+
this.pendingTasks.set(taskId, { subagentId, description });
|
|
3715
|
+
}
|
|
3716
|
+
removePendingTask(taskId) {
|
|
3717
|
+
this.pendingTasks.delete(taskId);
|
|
3718
|
+
}
|
|
3719
|
+
getFleetStatus() {
|
|
3720
|
+
const pending = Array.from(this.pendingTasks.entries()).map(([taskId, v]) => ({
|
|
3721
|
+
taskId,
|
|
3722
|
+
description: v.description,
|
|
3723
|
+
subagentId: v.subagentId
|
|
3724
|
+
}));
|
|
3725
|
+
return { pending, live: [] };
|
|
3726
|
+
}
|
|
3727
|
+
};
|
|
3728
|
+
|
|
3729
|
+
export { ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, BUG_HUNTER_AGENT, BudgetExceededError, BudgetThresholdSignal, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_SUBAGENT_BASELINE, DefaultMultiAgentCoordinator, Director, FLEET_ROSTER, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, InMemoryAgentBridge, InMemoryBridgeTransport, NULL_FLEET_BUS, REFACTOR_PLANNER_AGENT, SECURITY_SCANNER_AGENT, SubagentBudget, composeDirectorPrompt, composeSubagentPrompt, createDelegateTool, createMessage, makeAgentSubagentRunner, makeDirectorSessionFactory, rosterSummaryFromConfigs };
|
|
3233
3730
|
//# sourceMappingURL=index.js.map
|
|
3234
3731
|
//# sourceMappingURL=index.js.map
|