@wrongstack/core 0.5.6 → 0.6.0
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 +9 -9
- package/dist/coordination/index.js +339 -80
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +20 -19
- package/dist/defaults/index.js +731 -180
- package/dist/defaults/index.js.map +1 -1
- package/dist/{events-qnDZbrtb.d.ts → events-DPQKFX7W.d.ts} +1 -1
- package/dist/execution/index.d.ts +152 -12
- package/dist/execution/index.js +474 -5
- package/dist/execution/index.js.map +1 -1
- package/dist/extension/index.d.ts +6 -6
- package/dist/extension/index.js +2 -1
- package/dist/extension/index.js.map +1 -1
- package/dist/goal-store-BQ3YX1h1.d.ts +75 -0
- package/dist/{index-DPLJw_ZI.d.ts → index-B0qTujQW.d.ts} +52 -5
- package/dist/{index-BDnUCRvL.d.ts → index-DkdRz6yK.d.ts} +351 -15
- package/dist/index.d.ts +109 -28
- package/dist/index.js +1143 -234
- 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/models/index.js +2 -3
- package/dist/models/index.js.map +1 -1
- 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/{plan-templates-DaxTCPfk.d.ts → plan-templates-CKJs_sYh.d.ts} +9 -8
- 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 +6 -4
- package/dist/sdd/index.js +102 -68
- package/dist/sdd/index.js.map +1 -1
- 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/security/index.js +82 -82
- package/dist/security/index.js.map +1 -1
- 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/skills/index.js +102 -38
- package/dist/skills/index.js.map +1 -1
- package/dist/storage/index.d.ts +6 -5
- package/dist/storage/index.js +131 -22
- 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-B03CRwu-.d.ts} +4 -4
- package/dist/types/index.d.ts +15 -15
- package/dist/types/index.js +100 -102
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +5 -0
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,23 +1,109 @@
|
|
|
1
1
|
import * as crypto2 from 'crypto';
|
|
2
2
|
import { randomBytes, createCipheriv, createDecipheriv, randomUUID, createHash } from 'crypto';
|
|
3
|
-
import * as fs from 'fs';
|
|
4
3
|
import * as fsp2 from 'fs/promises';
|
|
5
|
-
import { readFile, readdir,
|
|
4
|
+
import { readFile, readdir, stat, mkdir } from 'fs/promises';
|
|
6
5
|
import * as path6 from 'path';
|
|
7
6
|
import { join, extname, relative } from 'path';
|
|
7
|
+
import * as fs2 from 'fs';
|
|
8
8
|
import * as os4 from 'os';
|
|
9
|
+
import { execFile, spawn } from 'child_process';
|
|
10
|
+
import { promisify } from 'util';
|
|
9
11
|
import { EventEmitter } from 'events';
|
|
10
12
|
import { createGunzip } from 'zlib';
|
|
11
13
|
import { Readable } from 'stream';
|
|
12
14
|
import { pipeline } from 'stream/promises';
|
|
13
|
-
import { spawn } from 'child_process';
|
|
14
15
|
|
|
16
|
+
var __defProp = Object.defineProperty;
|
|
17
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
15
18
|
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
16
19
|
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
17
20
|
}) : x)(function(x) {
|
|
18
21
|
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
19
22
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
20
23
|
});
|
|
24
|
+
var __esm = (fn, res) => function __init() {
|
|
25
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
26
|
+
};
|
|
27
|
+
var __export = (target, all) => {
|
|
28
|
+
for (var name in all)
|
|
29
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// src/utils/atomic-write.ts
|
|
33
|
+
var atomic_write_exports = {};
|
|
34
|
+
__export(atomic_write_exports, {
|
|
35
|
+
atomicWrite: () => atomicWrite,
|
|
36
|
+
ensureDir: () => ensureDir
|
|
37
|
+
});
|
|
38
|
+
async function atomicWrite(targetPath, content, opts = {}) {
|
|
39
|
+
const dir = path6.dirname(targetPath);
|
|
40
|
+
await fsp2.mkdir(dir, { recursive: true });
|
|
41
|
+
const tmp = path6.join(dir, `.${path6.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
|
|
42
|
+
try {
|
|
43
|
+
if (typeof content === "string") {
|
|
44
|
+
await fsp2.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
|
|
45
|
+
} else {
|
|
46
|
+
await fsp2.writeFile(tmp, content, { flag: "wx" });
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const fh = await fsp2.open(tmp, "r+");
|
|
50
|
+
try {
|
|
51
|
+
await fh.sync();
|
|
52
|
+
} finally {
|
|
53
|
+
await fh.close();
|
|
54
|
+
}
|
|
55
|
+
} catch {
|
|
56
|
+
}
|
|
57
|
+
let mode;
|
|
58
|
+
try {
|
|
59
|
+
const stat8 = await fsp2.stat(targetPath);
|
|
60
|
+
mode = stat8.mode & 511;
|
|
61
|
+
} catch {
|
|
62
|
+
mode = opts.mode;
|
|
63
|
+
}
|
|
64
|
+
if (mode !== void 0) {
|
|
65
|
+
await fsp2.chmod(tmp, mode);
|
|
66
|
+
}
|
|
67
|
+
await renameWithRetry(tmp, targetPath);
|
|
68
|
+
} catch (err) {
|
|
69
|
+
try {
|
|
70
|
+
await fsp2.unlink(tmp);
|
|
71
|
+
} catch {
|
|
72
|
+
}
|
|
73
|
+
throw err;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async function ensureDir(dir) {
|
|
77
|
+
await fsp2.mkdir(dir, { recursive: true });
|
|
78
|
+
}
|
|
79
|
+
async function renameWithRetry(from, to) {
|
|
80
|
+
if (process.platform !== "win32") {
|
|
81
|
+
await fsp2.rename(from, to);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const delays = [10, 25, 60, 120, 250];
|
|
85
|
+
let lastErr;
|
|
86
|
+
for (let i = 0; i <= delays.length; i++) {
|
|
87
|
+
try {
|
|
88
|
+
await fsp2.rename(from, to);
|
|
89
|
+
return;
|
|
90
|
+
} catch (err) {
|
|
91
|
+
lastErr = err;
|
|
92
|
+
const code = err?.code;
|
|
93
|
+
if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {
|
|
94
|
+
throw err;
|
|
95
|
+
}
|
|
96
|
+
await new Promise((resolve5) => setTimeout(resolve5, delays[i]));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
throw lastErr;
|
|
100
|
+
}
|
|
101
|
+
var TRANSIENT_RENAME_CODES;
|
|
102
|
+
var init_atomic_write = __esm({
|
|
103
|
+
"src/utils/atomic-write.ts"() {
|
|
104
|
+
TRANSIENT_RENAME_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES", "ENOTEMPTY"]);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
21
107
|
|
|
22
108
|
// src/types/errors.ts
|
|
23
109
|
var WrongStackError = class extends Error {
|
|
@@ -823,6 +909,7 @@ function providerStatusToCode(status, type) {
|
|
|
823
909
|
var ENCRYPTED_PREFIX = "enc:v1:";
|
|
824
910
|
|
|
825
911
|
// src/security/secret-vault.ts
|
|
912
|
+
init_atomic_write();
|
|
826
913
|
var KEY_BYTES = 32;
|
|
827
914
|
var IV_BYTES = 12;
|
|
828
915
|
var TAG_BYTES = 16;
|
|
@@ -867,7 +954,7 @@ var DefaultSecretVault = class {
|
|
|
867
954
|
loadOrCreateKey() {
|
|
868
955
|
if (this.key) return this.key;
|
|
869
956
|
try {
|
|
870
|
-
const buf =
|
|
957
|
+
const buf = fs2.readFileSync(this.keyFile);
|
|
871
958
|
if (buf.length !== KEY_BYTES) {
|
|
872
959
|
throw new Error(`SecretVault: key file ${this.keyFile} has wrong size`);
|
|
873
960
|
}
|
|
@@ -876,13 +963,13 @@ var DefaultSecretVault = class {
|
|
|
876
963
|
} catch (err) {
|
|
877
964
|
if (err.code !== "ENOENT") throw err;
|
|
878
965
|
}
|
|
879
|
-
|
|
966
|
+
fs2.mkdirSync(path6.dirname(this.keyFile), { recursive: true });
|
|
880
967
|
const key = randomBytes(KEY_BYTES);
|
|
881
968
|
try {
|
|
882
|
-
|
|
969
|
+
fs2.writeFileSync(this.keyFile, key, { mode: 384, flag: "wx" });
|
|
883
970
|
} catch (err) {
|
|
884
971
|
if (err.code !== "EEXIST") throw err;
|
|
885
|
-
const buf =
|
|
972
|
+
const buf = fs2.readFileSync(this.keyFile);
|
|
886
973
|
if (buf.length !== KEY_BYTES) {
|
|
887
974
|
throw new Error(`SecretVault: key file ${this.keyFile} has wrong size`);
|
|
888
975
|
}
|
|
@@ -944,7 +1031,7 @@ async function rewriteConfigEncrypted(configPath, vault, patch) {
|
|
|
944
1031
|
const merged = deepMerge(current, patch ?? {});
|
|
945
1032
|
const encrypted = encryptConfigSecrets(merged, vault);
|
|
946
1033
|
await fsp2.mkdir(path6.dirname(configPath), { recursive: true });
|
|
947
|
-
await
|
|
1034
|
+
await atomicWrite(configPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
948
1035
|
try {
|
|
949
1036
|
await fsp2.chmod(configPath, 384);
|
|
950
1037
|
} catch {
|
|
@@ -966,7 +1053,7 @@ async function migratePlaintextSecrets(configPath, vault) {
|
|
|
966
1053
|
const counter = { n: 0 };
|
|
967
1054
|
const migrated = walkCount(parsed, vault, counter);
|
|
968
1055
|
if (counter.n === 0) return { migrated: 0, file: configPath };
|
|
969
|
-
await
|
|
1056
|
+
await atomicWrite(configPath, JSON.stringify(migrated, null, 2), { mode: 384 });
|
|
970
1057
|
try {
|
|
971
1058
|
await fsp2.chmod(configPath, 384);
|
|
972
1059
|
} catch {
|
|
@@ -1076,7 +1163,7 @@ var DefaultLogger = class _DefaultLogger {
|
|
|
1076
1163
|
this.pretty = opts.pretty ?? true;
|
|
1077
1164
|
if (this.file) {
|
|
1078
1165
|
try {
|
|
1079
|
-
|
|
1166
|
+
fs2.mkdirSync(path6.dirname(this.file), { recursive: true });
|
|
1080
1167
|
} catch {
|
|
1081
1168
|
}
|
|
1082
1169
|
}
|
|
@@ -1115,7 +1202,7 @@ var DefaultLogger = class _DefaultLogger {
|
|
|
1115
1202
|
}
|
|
1116
1203
|
if (this.file) {
|
|
1117
1204
|
try {
|
|
1118
|
-
|
|
1205
|
+
fs2.appendFileSync(this.file, `${JSON.stringify(entry)}
|
|
1119
1206
|
`);
|
|
1120
1207
|
} catch {
|
|
1121
1208
|
}
|
|
@@ -1597,7 +1684,7 @@ var DefaultPathResolver = class {
|
|
|
1597
1684
|
while (dir !== root) {
|
|
1598
1685
|
for (const marker of PROJECT_MARKERS) {
|
|
1599
1686
|
try {
|
|
1600
|
-
|
|
1687
|
+
fs2.accessSync(path6.join(dir, marker));
|
|
1601
1688
|
return dir;
|
|
1602
1689
|
} catch {
|
|
1603
1690
|
}
|
|
@@ -1612,7 +1699,7 @@ var DefaultPathResolver = class {
|
|
|
1612
1699
|
const abs = path6.isAbsolute(input) ? input : path6.resolve(this.cwd, input);
|
|
1613
1700
|
let real;
|
|
1614
1701
|
try {
|
|
1615
|
-
real =
|
|
1702
|
+
real = fs2.realpathSync(abs);
|
|
1616
1703
|
} catch {
|
|
1617
1704
|
real = path6.normalize(abs);
|
|
1618
1705
|
}
|
|
@@ -1666,7 +1753,7 @@ function buildRecoveryStrategies(opts) {
|
|
|
1666
1753
|
async attempt(err) {
|
|
1667
1754
|
if (!(err instanceof ProviderError) || err.status !== 429) return null;
|
|
1668
1755
|
const delayMs = err.body?.retryAfterMs ?? 5e3;
|
|
1669
|
-
const delay = Math.
|
|
1756
|
+
const delay = Math.min(6e4, Math.max(1e3, delayMs));
|
|
1670
1757
|
await new Promise((r) => setTimeout(r, delay));
|
|
1671
1758
|
return { action: "retry", reason: "rate_limit_backoff" };
|
|
1672
1759
|
}
|
|
@@ -1819,15 +1906,15 @@ var PATTERNS = [
|
|
|
1819
1906
|
{ type: "redis_uri", regex: /redis:\/\/[^\s"'`]+/g },
|
|
1820
1907
|
{
|
|
1821
1908
|
type: "bearer_token",
|
|
1822
|
-
//
|
|
1823
|
-
//
|
|
1824
|
-
regex: /(
|
|
1909
|
+
// Anchored with alternation instead of negative lookahead — avoids V8
|
|
1910
|
+
// backtracking risk on adversarial input. Bounded at 512 chars.
|
|
1911
|
+
regex: /(?:^|[^A-Za-z0-9_.~+/-])Bearer\s+[A-Za-z0-9._~+/-]{20,512}=*(?:$|[^A-Za-z0-9_.~+/-])/g
|
|
1825
1912
|
},
|
|
1826
1913
|
{
|
|
1827
1914
|
type: "high_entropy_env",
|
|
1828
|
-
//
|
|
1829
|
-
//
|
|
1830
|
-
regex:
|
|
1915
|
+
// Anchored with alternation instead of lookbehind to avoid backtracking.
|
|
1916
|
+
// Value bounded at 512 chars.
|
|
1917
|
+
regex: /(?:^|\s)([A-Z_]{4,}(?:KEY|TOKEN|SECRET|PASSWORD|PWD))\s*[:=]\s*['"]?([A-Za-z0-9_/+=-]{20,512})['"]?(?:\s|$)/g
|
|
1831
1918
|
}
|
|
1832
1919
|
];
|
|
1833
1920
|
var SCRUB_CHUNK_BYTES = 64 * 1024;
|
|
@@ -1879,72 +1966,9 @@ var DefaultSecretScrubber = class {
|
|
|
1879
1966
|
return visit(obj);
|
|
1880
1967
|
}
|
|
1881
1968
|
};
|
|
1882
|
-
async function atomicWrite(targetPath, content, opts = {}) {
|
|
1883
|
-
const dir = path6.dirname(targetPath);
|
|
1884
|
-
await fsp2.mkdir(dir, { recursive: true });
|
|
1885
|
-
const tmp = path6.join(dir, `.${path6.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
|
|
1886
|
-
try {
|
|
1887
|
-
if (typeof content === "string") {
|
|
1888
|
-
await fsp2.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
|
|
1889
|
-
} else {
|
|
1890
|
-
await fsp2.writeFile(tmp, content, { flag: "wx" });
|
|
1891
|
-
}
|
|
1892
|
-
try {
|
|
1893
|
-
const fh = await fsp2.open(tmp, "r+");
|
|
1894
|
-
try {
|
|
1895
|
-
await fh.sync();
|
|
1896
|
-
} finally {
|
|
1897
|
-
await fh.close();
|
|
1898
|
-
}
|
|
1899
|
-
} catch {
|
|
1900
|
-
}
|
|
1901
|
-
let mode;
|
|
1902
|
-
try {
|
|
1903
|
-
const stat8 = await fsp2.stat(targetPath);
|
|
1904
|
-
mode = stat8.mode & 511;
|
|
1905
|
-
} catch {
|
|
1906
|
-
mode = opts.mode;
|
|
1907
|
-
}
|
|
1908
|
-
if (mode !== void 0) {
|
|
1909
|
-
await fsp2.chmod(tmp, mode);
|
|
1910
|
-
}
|
|
1911
|
-
await renameWithRetry(tmp, targetPath);
|
|
1912
|
-
} catch (err) {
|
|
1913
|
-
try {
|
|
1914
|
-
await fsp2.unlink(tmp);
|
|
1915
|
-
} catch {
|
|
1916
|
-
}
|
|
1917
|
-
throw err;
|
|
1918
|
-
}
|
|
1919
|
-
}
|
|
1920
|
-
async function ensureDir(dir) {
|
|
1921
|
-
await fsp2.mkdir(dir, { recursive: true });
|
|
1922
|
-
}
|
|
1923
|
-
var TRANSIENT_RENAME_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES", "ENOTEMPTY"]);
|
|
1924
|
-
async function renameWithRetry(from, to) {
|
|
1925
|
-
if (process.platform !== "win32") {
|
|
1926
|
-
await fsp2.rename(from, to);
|
|
1927
|
-
return;
|
|
1928
|
-
}
|
|
1929
|
-
const delays = [10, 25, 60, 120, 250];
|
|
1930
|
-
let lastErr;
|
|
1931
|
-
for (let i = 0; i <= delays.length; i++) {
|
|
1932
|
-
try {
|
|
1933
|
-
await fsp2.rename(from, to);
|
|
1934
|
-
return;
|
|
1935
|
-
} catch (err) {
|
|
1936
|
-
lastErr = err;
|
|
1937
|
-
const code = err?.code;
|
|
1938
|
-
if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {
|
|
1939
|
-
throw err;
|
|
1940
|
-
}
|
|
1941
|
-
await new Promise((resolve5) => setTimeout(resolve5, delays[i]));
|
|
1942
|
-
}
|
|
1943
|
-
}
|
|
1944
|
-
throw lastErr;
|
|
1945
|
-
}
|
|
1946
1969
|
|
|
1947
1970
|
// src/models/models-registry.ts
|
|
1971
|
+
init_atomic_write();
|
|
1948
1972
|
var DEFAULT_URL = "https://models.dev/api.json";
|
|
1949
1973
|
var DEFAULT_TTL_SECONDS = 24 * 3600;
|
|
1950
1974
|
var FAMILY_BY_NPM = {
|
|
@@ -3297,6 +3321,9 @@ function renderPlainText(meta, events) {
|
|
|
3297
3321
|
return lines.join("\n");
|
|
3298
3322
|
}
|
|
3299
3323
|
|
|
3324
|
+
// src/utils/index.ts
|
|
3325
|
+
init_atomic_write();
|
|
3326
|
+
|
|
3300
3327
|
// src/utils/safe-json.ts
|
|
3301
3328
|
function safeParse(input, maxBytes = 5e6) {
|
|
3302
3329
|
if (input.length > maxBytes) {
|
|
@@ -3725,6 +3752,11 @@ function looksSecret(name) {
|
|
|
3725
3752
|
function buildChildEnv(optsOrSessionId) {
|
|
3726
3753
|
const opts = typeof optsOrSessionId === "string" ? { sessionId: optsOrSessionId } : optsOrSessionId ?? {};
|
|
3727
3754
|
const passthrough = process.env["WRONGSTACK_CHILD_ENV_PASSTHROUGH"] === "1" || process.env["WRONGSTACK_BASH_ENV_PASSTHROUGH"] === "1";
|
|
3755
|
+
if (passthrough && !process.env["CI"]) {
|
|
3756
|
+
console.warn(
|
|
3757
|
+
"[WrongStack] WARNING: WRONGSTACK_*_ENV_PASSTHROUGH=1 is active \u2014\n all parent env vars (including API keys) forwarded to child processes.\n Do not use on shared or multi-tenant systems."
|
|
3758
|
+
);
|
|
3759
|
+
}
|
|
3728
3760
|
const out = {};
|
|
3729
3761
|
for (const [k, v] of Object.entries(process.env)) {
|
|
3730
3762
|
if (v === void 0) continue;
|
|
@@ -3755,11 +3787,11 @@ function validateAgainstSchema(value, schema) {
|
|
|
3755
3787
|
walk2(value, schema, "", errors);
|
|
3756
3788
|
return { ok: errors.length === 0, errors };
|
|
3757
3789
|
}
|
|
3758
|
-
function walk2(value, schema,
|
|
3790
|
+
function walk2(value, schema, path26, errors) {
|
|
3759
3791
|
if (schema.enum !== void 0) {
|
|
3760
3792
|
if (!schema.enum.some((e) => deepEqual(e, value))) {
|
|
3761
3793
|
errors.push({
|
|
3762
|
-
path:
|
|
3794
|
+
path: path26 || "<root>",
|
|
3763
3795
|
message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
|
|
3764
3796
|
});
|
|
3765
3797
|
return;
|
|
@@ -3768,7 +3800,7 @@ function walk2(value, schema, path24, errors) {
|
|
|
3768
3800
|
if (typeof schema.type === "string") {
|
|
3769
3801
|
if (!checkType(value, schema.type)) {
|
|
3770
3802
|
errors.push({
|
|
3771
|
-
path:
|
|
3803
|
+
path: path26 || "<root>",
|
|
3772
3804
|
message: `expected ${schema.type}, got ${describeType(value)}`
|
|
3773
3805
|
});
|
|
3774
3806
|
return;
|
|
@@ -3778,19 +3810,19 @@ function walk2(value, schema, path24, errors) {
|
|
|
3778
3810
|
const obj = value;
|
|
3779
3811
|
for (const req of schema.required ?? []) {
|
|
3780
3812
|
if (!(req in obj)) {
|
|
3781
|
-
errors.push({ path: joinPath(
|
|
3813
|
+
errors.push({ path: joinPath(path26, req), message: "required property missing" });
|
|
3782
3814
|
}
|
|
3783
3815
|
}
|
|
3784
3816
|
if (schema.properties) {
|
|
3785
3817
|
for (const [key, subSchema] of Object.entries(schema.properties)) {
|
|
3786
3818
|
if (key in obj) {
|
|
3787
|
-
walk2(obj[key], subSchema, joinPath(
|
|
3819
|
+
walk2(obj[key], subSchema, joinPath(path26, key), errors);
|
|
3788
3820
|
}
|
|
3789
3821
|
}
|
|
3790
3822
|
}
|
|
3791
3823
|
}
|
|
3792
3824
|
if (schema.type === "array" && Array.isArray(value) && schema.items) {
|
|
3793
|
-
value.forEach((item, i) => walk2(item, schema.items, `${
|
|
3825
|
+
value.forEach((item, i) => walk2(item, schema.items, `${path26}[${i}]`, errors));
|
|
3794
3826
|
}
|
|
3795
3827
|
}
|
|
3796
3828
|
function checkType(value, type) {
|
|
@@ -3842,6 +3874,9 @@ function deepEqual(a, b) {
|
|
|
3842
3874
|
}
|
|
3843
3875
|
return false;
|
|
3844
3876
|
}
|
|
3877
|
+
|
|
3878
|
+
// src/storage/session-store.ts
|
|
3879
|
+
init_atomic_write();
|
|
3845
3880
|
var DefaultSessionStore = class {
|
|
3846
3881
|
dir;
|
|
3847
3882
|
events;
|
|
@@ -3885,19 +3920,25 @@ var DefaultSessionStore = class {
|
|
|
3885
3920
|
{ cause: err }
|
|
3886
3921
|
);
|
|
3887
3922
|
}
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
handle,
|
|
3891
|
-
(/* @__PURE__ */ new Date()).toISOString(),
|
|
3892
|
-
{
|
|
3923
|
+
try {
|
|
3924
|
+
const writer = new FileSessionWriter(
|
|
3893
3925
|
id,
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3926
|
+
handle,
|
|
3927
|
+
(/* @__PURE__ */ new Date()).toISOString(),
|
|
3928
|
+
{
|
|
3929
|
+
id,
|
|
3930
|
+
model: data.metadata.model,
|
|
3931
|
+
provider: data.metadata.provider
|
|
3932
|
+
},
|
|
3933
|
+
this.events,
|
|
3934
|
+
{ resumed: true, dir: this.dir, filePath: file }
|
|
3935
|
+
);
|
|
3936
|
+
return { writer, data };
|
|
3937
|
+
} catch (err) {
|
|
3938
|
+
await handle.close().catch(() => {
|
|
3939
|
+
});
|
|
3940
|
+
throw err;
|
|
3941
|
+
}
|
|
3901
3942
|
}
|
|
3902
3943
|
async load(id) {
|
|
3903
3944
|
const file = path6.join(this.dir, `${id}.jsonl`);
|
|
@@ -3943,7 +3984,7 @@ var DefaultSessionStore = class {
|
|
|
3943
3984
|
const full = path6.join(this.dir, `${id}.jsonl`);
|
|
3944
3985
|
const stat8 = await fsp2.stat(full);
|
|
3945
3986
|
const summary = await this.summarize(id, stat8.mtime.toISOString());
|
|
3946
|
-
await
|
|
3987
|
+
await atomicWrite(manifest, JSON.stringify(summary), { mode: 384 }).catch((err) => {
|
|
3947
3988
|
console.warn(
|
|
3948
3989
|
`[session-store] Failed to write manifest for "${id}":`,
|
|
3949
3990
|
err instanceof Error ? err.message : String(err)
|
|
@@ -4171,7 +4212,7 @@ var FileSessionWriter = class {
|
|
|
4171
4212
|
this.closed = true;
|
|
4172
4213
|
if (this.manifestFile) {
|
|
4173
4214
|
try {
|
|
4174
|
-
await
|
|
4215
|
+
await atomicWrite(this.manifestFile, JSON.stringify(this.summary), { mode: 384 });
|
|
4175
4216
|
} catch {
|
|
4176
4217
|
}
|
|
4177
4218
|
}
|
|
@@ -4267,6 +4308,9 @@ function userInputTitle(content) {
|
|
|
4267
4308
|
const text = content.filter((b) => b.type === "text").map((b) => b.text).join(" ");
|
|
4268
4309
|
return (text || "(non-text input)").slice(0, 60);
|
|
4269
4310
|
}
|
|
4311
|
+
|
|
4312
|
+
// src/storage/queue-store.ts
|
|
4313
|
+
init_atomic_write();
|
|
4270
4314
|
var QueueStore = class {
|
|
4271
4315
|
file;
|
|
4272
4316
|
constructor(opts) {
|
|
@@ -4317,6 +4361,9 @@ function isPersistedQueueItem(v) {
|
|
|
4317
4361
|
const o = v;
|
|
4318
4362
|
return typeof o["displayText"] === "string" && Array.isArray(o["blocks"]);
|
|
4319
4363
|
}
|
|
4364
|
+
|
|
4365
|
+
// src/storage/attachment-store.ts
|
|
4366
|
+
init_atomic_write();
|
|
4320
4367
|
var DEFAULT_SPOOL_THRESHOLD = 256 * 1024;
|
|
4321
4368
|
var PLACEHOLDER_RE = /\[(pasted|image|file) #(\d+)\]/g;
|
|
4322
4369
|
var DefaultAttachmentStore = class {
|
|
@@ -4338,7 +4385,9 @@ var DefaultAttachmentStore = class {
|
|
|
4338
4385
|
if (this.spoolDir && bytes >= this.spoolThreshold) {
|
|
4339
4386
|
await fsp2.mkdir(this.spoolDir, { recursive: true });
|
|
4340
4387
|
spooledPath = path6.join(this.spoolDir, `${id}.bin`);
|
|
4341
|
-
await
|
|
4388
|
+
await atomicWrite(spooledPath, input.data, {
|
|
4389
|
+
encoding: input.kind === "image" ? "base64" : "utf8"
|
|
4390
|
+
});
|
|
4342
4391
|
data = void 0;
|
|
4343
4392
|
}
|
|
4344
4393
|
const att = {
|
|
@@ -4437,6 +4486,9 @@ function mergeAdjacentText(blocks) {
|
|
|
4437
4486
|
}
|
|
4438
4487
|
return out;
|
|
4439
4488
|
}
|
|
4489
|
+
|
|
4490
|
+
// src/storage/memory-store.ts
|
|
4491
|
+
init_atomic_write();
|
|
4440
4492
|
var MAX_BYTES_TOTAL = 32e3;
|
|
4441
4493
|
var DefaultMemoryStore = class {
|
|
4442
4494
|
files;
|
|
@@ -4449,12 +4501,13 @@ var DefaultMemoryStore = class {
|
|
|
4449
4501
|
* issue order. Different scopes still proceed in parallel.
|
|
4450
4502
|
*
|
|
4451
4503
|
* The chain tracks only the last pending write. If a write fails, its
|
|
4452
|
-
* error is caught and swallowed
|
|
4453
|
-
*
|
|
4454
|
-
*
|
|
4455
|
-
* backup whose worst case is losing a memory consolidation pass.
|
|
4504
|
+
* error is caught and swallowed so the chain stays alive for subsequent
|
|
4505
|
+
* calls. The error is stored in `writeErrors` so callers can learn about
|
|
4506
|
+
* it on the next read operation.
|
|
4456
4507
|
*/
|
|
4457
4508
|
writeChain = /* @__PURE__ */ new Map();
|
|
4509
|
+
/** Last write error per scope — surfaced as warnings on the next readAll(). */
|
|
4510
|
+
writeErrors = /* @__PURE__ */ new Map();
|
|
4458
4511
|
constructor(opts) {
|
|
4459
4512
|
this.files = {
|
|
4460
4513
|
"project-agents": opts.paths.inProjectAgentsFile,
|
|
@@ -4464,10 +4517,16 @@ var DefaultMemoryStore = class {
|
|
|
4464
4517
|
}
|
|
4465
4518
|
async runSerialized(scope, work) {
|
|
4466
4519
|
const prior = this.writeChain.get(scope) ?? Promise.resolve();
|
|
4520
|
+
prior.catch((err) => {
|
|
4521
|
+
this.writeErrors.set(scope, err);
|
|
4522
|
+
});
|
|
4467
4523
|
const next = prior.catch(() => void 0).then(work);
|
|
4468
4524
|
this.writeChain.set(scope, next);
|
|
4469
4525
|
try {
|
|
4470
4526
|
return await next;
|
|
4527
|
+
} catch (err) {
|
|
4528
|
+
this.writeErrors.set(scope, err);
|
|
4529
|
+
throw err;
|
|
4471
4530
|
} finally {
|
|
4472
4531
|
if (this.writeChain.get(scope) === next) {
|
|
4473
4532
|
this.writeChain.delete(scope);
|
|
@@ -4477,6 +4536,10 @@ var DefaultMemoryStore = class {
|
|
|
4477
4536
|
async readAll() {
|
|
4478
4537
|
const parts = [];
|
|
4479
4538
|
for (const scope of ["project-agents", "project-memory", "user-memory"]) {
|
|
4539
|
+
const writeErr = this.writeErrors.get(scope);
|
|
4540
|
+
if (writeErr) {
|
|
4541
|
+
parts.push(`> \u26A0\uFE0F Memory write error (${labelOf(scope)}): ${writeErr.message}`);
|
|
4542
|
+
}
|
|
4480
4543
|
const body = await this.read(scope);
|
|
4481
4544
|
if (body.trim()) parts.push(`## ${labelOf(scope)}
|
|
4482
4545
|
|
|
@@ -4969,6 +5032,9 @@ function runConfigMigrations(input, targetVersion, migrations) {
|
|
|
4969
5032
|
return { config: current, applied, shouldPersist };
|
|
4970
5033
|
}
|
|
4971
5034
|
var DEFAULT_CONFIG_MIGRATIONS = [];
|
|
5035
|
+
|
|
5036
|
+
// src/storage/recovery-lock.ts
|
|
5037
|
+
init_atomic_write();
|
|
4972
5038
|
var LOCK_FILE = "active.json";
|
|
4973
5039
|
var DEFAULT_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
4974
5040
|
var RecoveryLock = class {
|
|
@@ -5189,6 +5255,9 @@ var SessionAnalyzer = class {
|
|
|
5189
5255
|
return last - first;
|
|
5190
5256
|
}
|
|
5191
5257
|
};
|
|
5258
|
+
|
|
5259
|
+
// src/storage/todos-checkpoint.ts
|
|
5260
|
+
init_atomic_write();
|
|
5192
5261
|
async function loadTodosCheckpoint(filePath) {
|
|
5193
5262
|
let raw;
|
|
5194
5263
|
try {
|
|
@@ -5257,6 +5326,9 @@ function attachTodosCheckpoint(state, filePath, sessionId) {
|
|
|
5257
5326
|
}
|
|
5258
5327
|
};
|
|
5259
5328
|
}
|
|
5329
|
+
|
|
5330
|
+
// src/storage/plan-store.ts
|
|
5331
|
+
init_atomic_write();
|
|
5260
5332
|
async function loadPlan(filePath) {
|
|
5261
5333
|
let raw;
|
|
5262
5334
|
try {
|
|
@@ -5491,6 +5563,9 @@ ${cat}:`);
|
|
|
5491
5563
|
}
|
|
5492
5564
|
return lines.join("\n");
|
|
5493
5565
|
}
|
|
5566
|
+
|
|
5567
|
+
// src/storage/director-state.ts
|
|
5568
|
+
init_atomic_write();
|
|
5494
5569
|
async function loadDirectorState(filePath) {
|
|
5495
5570
|
let raw;
|
|
5496
5571
|
try {
|
|
@@ -5669,6 +5744,9 @@ var DirectorStateCheckpoint = class {
|
|
|
5669
5744
|
}
|
|
5670
5745
|
}
|
|
5671
5746
|
};
|
|
5747
|
+
|
|
5748
|
+
// src/security/permission-policy.ts
|
|
5749
|
+
init_atomic_write();
|
|
5672
5750
|
var DefaultPermissionPolicy = class {
|
|
5673
5751
|
policy = {};
|
|
5674
5752
|
loaded = false;
|
|
@@ -7070,6 +7148,14 @@ var DoneConditionChecker = class {
|
|
|
7070
7148
|
};
|
|
7071
7149
|
}
|
|
7072
7150
|
break;
|
|
7151
|
+
case "directive":
|
|
7152
|
+
if (this.condition.maxIterations && state.iterations >= this.condition.maxIterations) {
|
|
7153
|
+
return { done: true, reason: `max iterations (${this.condition.maxIterations}) reached`, ...state };
|
|
7154
|
+
}
|
|
7155
|
+
if (this.condition.maxToolCalls && state.toolCalls >= this.condition.maxToolCalls) {
|
|
7156
|
+
return { done: true, reason: `max tool calls (${this.condition.maxToolCalls}) reached`, ...state };
|
|
7157
|
+
}
|
|
7158
|
+
break;
|
|
7073
7159
|
}
|
|
7074
7160
|
return { done: false, iterations: state.iterations, toolCalls: state.toolCalls };
|
|
7075
7161
|
}
|
|
@@ -7089,10 +7175,16 @@ var AutonomousRunner = class {
|
|
|
7089
7175
|
const offToolExecuted = this.opts.agent.events?.on?.("tool.executed", () => {
|
|
7090
7176
|
this.toolCalls++;
|
|
7091
7177
|
});
|
|
7178
|
+
const offIterationCompleted = this.opts.agent.events?.on?.("iteration.completed", () => {
|
|
7179
|
+
if (this.opts.enableAutonomousContinue && this.opts.doneCondition.type === "directive") {
|
|
7180
|
+
this.iterations++;
|
|
7181
|
+
}
|
|
7182
|
+
});
|
|
7092
7183
|
try {
|
|
7093
7184
|
return await this.runLoop();
|
|
7094
7185
|
} finally {
|
|
7095
7186
|
offToolExecuted?.();
|
|
7187
|
+
offIterationCompleted?.();
|
|
7096
7188
|
}
|
|
7097
7189
|
}
|
|
7098
7190
|
async runLoop() {
|
|
@@ -7116,12 +7208,14 @@ var AutonomousRunner = class {
|
|
|
7116
7208
|
const ctrl = new AbortController();
|
|
7117
7209
|
const timeout = setTimeout(() => ctrl.abort(), this.opts.iterationTimeoutMs ?? 3e4);
|
|
7118
7210
|
try {
|
|
7211
|
+
const isDirectiveMode = this.opts.doneCondition.type === "directive" && this.opts.enableAutonomousContinue;
|
|
7119
7212
|
const result = await this.opts.agent.run("", {
|
|
7120
7213
|
signal: ctrl.signal,
|
|
7121
|
-
maxIterations: 1,
|
|
7122
|
-
executionStrategy: "sequential"
|
|
7214
|
+
maxIterations: isDirectiveMode ? this.opts.doneCondition.maxIterations ?? 100 : 1,
|
|
7215
|
+
executionStrategy: "sequential",
|
|
7216
|
+
autonomousContinue: isDirectiveMode ? true : void 0
|
|
7123
7217
|
});
|
|
7124
|
-
this.iterations++;
|
|
7218
|
+
if (!isDirectiveMode) this.iterations++;
|
|
7125
7219
|
if (result.status === "done") {
|
|
7126
7220
|
this.lastOutput = result.finalText;
|
|
7127
7221
|
}
|
|
@@ -7156,23 +7250,469 @@ var AutonomousRunner = class {
|
|
|
7156
7250
|
toolCalls: this.toolCalls,
|
|
7157
7251
|
reason: e instanceof Error ? e.message : String(e)
|
|
7158
7252
|
};
|
|
7159
|
-
this.opts.onDone?.(failedResult);
|
|
7160
|
-
return failedResult;
|
|
7161
|
-
} finally {
|
|
7162
|
-
clearTimeout(timeout);
|
|
7253
|
+
this.opts.onDone?.(failedResult);
|
|
7254
|
+
return failedResult;
|
|
7255
|
+
} finally {
|
|
7256
|
+
clearTimeout(timeout);
|
|
7257
|
+
}
|
|
7258
|
+
}
|
|
7259
|
+
return {
|
|
7260
|
+
status: "aborted",
|
|
7261
|
+
iterations: this.iterations,
|
|
7262
|
+
toolCalls: this.toolCalls,
|
|
7263
|
+
reason: "stopped externally"
|
|
7264
|
+
};
|
|
7265
|
+
}
|
|
7266
|
+
stop() {
|
|
7267
|
+
this.stopped = true;
|
|
7268
|
+
}
|
|
7269
|
+
};
|
|
7270
|
+
|
|
7271
|
+
// src/storage/goal-store.ts
|
|
7272
|
+
init_atomic_write();
|
|
7273
|
+
var MAX_JOURNAL_ENTRIES = 500;
|
|
7274
|
+
function goalFilePath(projectRoot) {
|
|
7275
|
+
return path6.join(projectRoot, ".wrongstack", "goal.json");
|
|
7276
|
+
}
|
|
7277
|
+
async function loadGoal(filePath) {
|
|
7278
|
+
let raw;
|
|
7279
|
+
try {
|
|
7280
|
+
raw = await fsp2.readFile(filePath, "utf8");
|
|
7281
|
+
} catch {
|
|
7282
|
+
return null;
|
|
7283
|
+
}
|
|
7284
|
+
try {
|
|
7285
|
+
const parsed = JSON.parse(raw);
|
|
7286
|
+
if (parsed?.version !== 1 || typeof parsed.goal !== "string" || !Array.isArray(parsed.journal)) {
|
|
7287
|
+
return null;
|
|
7288
|
+
}
|
|
7289
|
+
return parsed;
|
|
7290
|
+
} catch {
|
|
7291
|
+
return null;
|
|
7292
|
+
}
|
|
7293
|
+
}
|
|
7294
|
+
async function saveGoal(filePath, goal) {
|
|
7295
|
+
await atomicWrite(filePath, JSON.stringify(goal, null, 2), { mode: 384 });
|
|
7296
|
+
}
|
|
7297
|
+
function emptyGoal(goal) {
|
|
7298
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7299
|
+
return {
|
|
7300
|
+
version: 1,
|
|
7301
|
+
goal,
|
|
7302
|
+
setAt: now,
|
|
7303
|
+
lastActivityAt: now,
|
|
7304
|
+
iterations: 0,
|
|
7305
|
+
engineState: "idle",
|
|
7306
|
+
journal: []
|
|
7307
|
+
};
|
|
7308
|
+
}
|
|
7309
|
+
function appendJournal(goal, entry) {
|
|
7310
|
+
const iteration = goal.iterations + 1;
|
|
7311
|
+
const at = (/* @__PURE__ */ new Date()).toISOString();
|
|
7312
|
+
const full = { ...entry, iteration, at };
|
|
7313
|
+
const journal = [...goal.journal, full];
|
|
7314
|
+
const trimmed = journal.length > MAX_JOURNAL_ENTRIES ? journal.slice(journal.length - MAX_JOURNAL_ENTRIES) : journal;
|
|
7315
|
+
return {
|
|
7316
|
+
...goal,
|
|
7317
|
+
iterations: iteration,
|
|
7318
|
+
lastActivityAt: at,
|
|
7319
|
+
journal: trimmed
|
|
7320
|
+
};
|
|
7321
|
+
}
|
|
7322
|
+
function summarizeUsage(goal) {
|
|
7323
|
+
let totalCostUsd = 0;
|
|
7324
|
+
let totalInputTokens = 0;
|
|
7325
|
+
let totalOutputTokens = 0;
|
|
7326
|
+
let iterationsWithUsage = 0;
|
|
7327
|
+
for (const e of goal.journal) {
|
|
7328
|
+
if (typeof e.costUsd === "number") totalCostUsd += e.costUsd;
|
|
7329
|
+
if (e.tokens) {
|
|
7330
|
+
totalInputTokens += e.tokens.input;
|
|
7331
|
+
totalOutputTokens += e.tokens.output;
|
|
7332
|
+
}
|
|
7333
|
+
if (typeof e.costUsd === "number" || e.tokens) iterationsWithUsage++;
|
|
7334
|
+
}
|
|
7335
|
+
return { totalCostUsd, totalInputTokens, totalOutputTokens, iterationsWithUsage };
|
|
7336
|
+
}
|
|
7337
|
+
function formatGoal(goal, journalLimit = 10) {
|
|
7338
|
+
const lines = [];
|
|
7339
|
+
lines.push(`Goal: ${goal.goal}`);
|
|
7340
|
+
lines.push(`Set: ${goal.setAt}`);
|
|
7341
|
+
lines.push(`Last activity: ${goal.lastActivityAt}`);
|
|
7342
|
+
lines.push(`Iterations: ${goal.iterations}`);
|
|
7343
|
+
lines.push(`Engine: ${goal.engineState}`);
|
|
7344
|
+
const usage = summarizeUsage(goal);
|
|
7345
|
+
if (usage.iterationsWithUsage > 0) {
|
|
7346
|
+
lines.push(
|
|
7347
|
+
`Spent: $${usage.totalCostUsd.toFixed(4)} (in ${usage.totalInputTokens} / out ${usage.totalOutputTokens} tokens across ${usage.iterationsWithUsage} iterations)`
|
|
7348
|
+
);
|
|
7349
|
+
}
|
|
7350
|
+
if (goal.journal.length > 0) {
|
|
7351
|
+
lines.push("");
|
|
7352
|
+
lines.push(`Recent journal (last ${Math.min(journalLimit, goal.journal.length)}):`);
|
|
7353
|
+
const tail = goal.journal.slice(-journalLimit);
|
|
7354
|
+
for (const e of tail) {
|
|
7355
|
+
const mark = e.status === "success" ? "\u2713" : e.status === "failure" ? "\u2717" : e.status === "aborted" ? "\u2298" : "\xB7";
|
|
7356
|
+
const note = e.note ? ` \u2014 ${e.note}` : "";
|
|
7357
|
+
const cost = typeof e.costUsd === "number" ? ` ($${e.costUsd.toFixed(4)})` : "";
|
|
7358
|
+
lines.push(` #${e.iteration} ${mark} [${e.source}] ${e.task}${cost}${note}`);
|
|
7359
|
+
}
|
|
7360
|
+
}
|
|
7361
|
+
return lines.join("\n");
|
|
7362
|
+
}
|
|
7363
|
+
|
|
7364
|
+
// src/execution/eternal-autonomy.ts
|
|
7365
|
+
var execFileP = promisify(execFile);
|
|
7366
|
+
var EternalAutonomyEngine = class {
|
|
7367
|
+
constructor(opts) {
|
|
7368
|
+
this.opts = opts;
|
|
7369
|
+
this.goalPath = goalFilePath(opts.projectRoot);
|
|
7370
|
+
}
|
|
7371
|
+
opts;
|
|
7372
|
+
state = "idle";
|
|
7373
|
+
stopRequested = false;
|
|
7374
|
+
consecutiveFailures = 0;
|
|
7375
|
+
currentCtrl = null;
|
|
7376
|
+
iterationsSinceCompact = 0;
|
|
7377
|
+
goalPath;
|
|
7378
|
+
/** Current engine state — readable for UIs. */
|
|
7379
|
+
get currentState() {
|
|
7380
|
+
return this.state;
|
|
7381
|
+
}
|
|
7382
|
+
/** Synchronously request stop. Resolves once the running iteration aborts. */
|
|
7383
|
+
stop() {
|
|
7384
|
+
this.stopRequested = true;
|
|
7385
|
+
this.currentCtrl?.abort();
|
|
7386
|
+
void this.persistEngineState("stopped").catch(() => {
|
|
7387
|
+
});
|
|
7388
|
+
this.state = "stopped";
|
|
7389
|
+
}
|
|
7390
|
+
/**
|
|
7391
|
+
* Mark the engine as 'running' on disk + reset stop state so a new
|
|
7392
|
+
* batch of `runOneIteration()` calls can proceed. Called by the REPL
|
|
7393
|
+
* when the user invokes `/autonomy eternal`. Idempotent.
|
|
7394
|
+
*/
|
|
7395
|
+
async prime() {
|
|
7396
|
+
this.stopRequested = false;
|
|
7397
|
+
this.state = "running";
|
|
7398
|
+
await this.persistEngineState("running").catch(() => {
|
|
7399
|
+
});
|
|
7400
|
+
}
|
|
7401
|
+
/**
|
|
7402
|
+
* Main loop. Returns when stop() is called or the goal file is removed.
|
|
7403
|
+
* Does NOT throw — every iteration is wrapped to keep the loop alive.
|
|
7404
|
+
*/
|
|
7405
|
+
async run() {
|
|
7406
|
+
this.state = "running";
|
|
7407
|
+
await this.persistEngineState("running");
|
|
7408
|
+
try {
|
|
7409
|
+
while (!this.stopRequested) {
|
|
7410
|
+
let iterationOk = false;
|
|
7411
|
+
try {
|
|
7412
|
+
iterationOk = await this.runOneIteration();
|
|
7413
|
+
} catch (err) {
|
|
7414
|
+
this.consecutiveFailures++;
|
|
7415
|
+
this.opts.onError?.(err instanceof Error ? err : new Error(String(err)), this.consecutiveFailures);
|
|
7416
|
+
await this.appendFailure("engine error", err instanceof Error ? err.message : String(err));
|
|
7417
|
+
}
|
|
7418
|
+
if (iterationOk) {
|
|
7419
|
+
this.consecutiveFailures = 0;
|
|
7420
|
+
}
|
|
7421
|
+
if (this.stopRequested) break;
|
|
7422
|
+
await sleep(this.opts.cycleGapMs ?? 1e3);
|
|
7423
|
+
}
|
|
7424
|
+
} finally {
|
|
7425
|
+
this.state = "stopped";
|
|
7426
|
+
await this.persistEngineState("stopped").catch(() => {
|
|
7427
|
+
});
|
|
7428
|
+
}
|
|
7429
|
+
}
|
|
7430
|
+
/**
|
|
7431
|
+
* Execute a single sense-decide-execute-reflect cycle.
|
|
7432
|
+
* Returns true on success, false on handled failure / no-op.
|
|
7433
|
+
*
|
|
7434
|
+
* Exposed publicly so the REPL can pace iterations from its main loop
|
|
7435
|
+
* — running the engine and the REPL as a single sequential consumer of
|
|
7436
|
+
* `agent.run()` avoids race conditions on the shared Context.
|
|
7437
|
+
*/
|
|
7438
|
+
async runOneIteration() {
|
|
7439
|
+
const goal = await loadGoal(this.goalPath);
|
|
7440
|
+
if (!goal) {
|
|
7441
|
+
this.stopRequested = true;
|
|
7442
|
+
return false;
|
|
7443
|
+
}
|
|
7444
|
+
const action = await this.decide(goal);
|
|
7445
|
+
if (!action) {
|
|
7446
|
+
await sleep(5e3);
|
|
7447
|
+
return false;
|
|
7448
|
+
}
|
|
7449
|
+
const ctrl = new AbortController();
|
|
7450
|
+
this.currentCtrl = ctrl;
|
|
7451
|
+
const timer = setTimeout(
|
|
7452
|
+
() => ctrl.abort(),
|
|
7453
|
+
this.opts.iterationTimeoutMs ?? 5 * 6e4
|
|
7454
|
+
);
|
|
7455
|
+
let status = "success";
|
|
7456
|
+
let note;
|
|
7457
|
+
const tc = this.opts.agent.ctx?.tokenCounter;
|
|
7458
|
+
const beforeUsage = tc?.total?.();
|
|
7459
|
+
const beforeCost = tc?.estimateCost?.().total;
|
|
7460
|
+
try {
|
|
7461
|
+
const result = await this.opts.agent.run(
|
|
7462
|
+
[{ type: "text", text: action.directive }],
|
|
7463
|
+
{ signal: ctrl.signal }
|
|
7464
|
+
);
|
|
7465
|
+
if (result.status === "aborted") {
|
|
7466
|
+
status = "aborted";
|
|
7467
|
+
note = "stopped by user";
|
|
7468
|
+
} else if (result.status === "failed") {
|
|
7469
|
+
status = "failure";
|
|
7470
|
+
note = result.error?.describe?.() ?? "agent run failed";
|
|
7471
|
+
} else if (result.status === "max_iterations") {
|
|
7472
|
+
status = "failure";
|
|
7473
|
+
note = `max iterations (${result.iterations})`;
|
|
7474
|
+
} else {
|
|
7475
|
+
status = "success";
|
|
7476
|
+
const tail = (result.finalText ?? "").slice(0, 240).replace(/\s+/g, " ").trim();
|
|
7477
|
+
if (tail) note = tail;
|
|
7478
|
+
}
|
|
7479
|
+
} catch (err) {
|
|
7480
|
+
const isAbort = err instanceof Error && (err.name === "AbortError" || err.message.includes("abort"));
|
|
7481
|
+
status = isAbort ? "aborted" : "failure";
|
|
7482
|
+
note = err instanceof Error ? err.message : String(err);
|
|
7483
|
+
} finally {
|
|
7484
|
+
clearTimeout(timer);
|
|
7485
|
+
this.currentCtrl = null;
|
|
7486
|
+
}
|
|
7487
|
+
const afterUsage = tc?.total?.();
|
|
7488
|
+
const afterCost = tc?.estimateCost?.().total;
|
|
7489
|
+
const tokens = beforeUsage && afterUsage ? {
|
|
7490
|
+
input: Math.max(0, afterUsage.input - beforeUsage.input),
|
|
7491
|
+
output: Math.max(0, afterUsage.output - beforeUsage.output)
|
|
7492
|
+
} : void 0;
|
|
7493
|
+
const costUsd = typeof beforeCost === "number" && typeof afterCost === "number" ? Math.max(0, afterCost - beforeCost) : void 0;
|
|
7494
|
+
await this.appendIterationEntry({
|
|
7495
|
+
source: action.source,
|
|
7496
|
+
task: action.task,
|
|
7497
|
+
status,
|
|
7498
|
+
note,
|
|
7499
|
+
tokens,
|
|
7500
|
+
costUsd
|
|
7501
|
+
});
|
|
7502
|
+
let iterationIndex = 0;
|
|
7503
|
+
try {
|
|
7504
|
+
const reloaded = await loadGoal(this.goalPath);
|
|
7505
|
+
iterationIndex = reloaded?.iterations ?? 0;
|
|
7506
|
+
} catch {
|
|
7507
|
+
}
|
|
7508
|
+
this.opts.onIteration?.({
|
|
7509
|
+
at: (this.opts.now?.() ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
7510
|
+
iteration: iterationIndex,
|
|
7511
|
+
source: action.source,
|
|
7512
|
+
task: action.task,
|
|
7513
|
+
status,
|
|
7514
|
+
note,
|
|
7515
|
+
tokens,
|
|
7516
|
+
costUsd
|
|
7517
|
+
});
|
|
7518
|
+
if (status === "failure") {
|
|
7519
|
+
this.consecutiveFailures++;
|
|
7520
|
+
return false;
|
|
7521
|
+
}
|
|
7522
|
+
if (status === "aborted") {
|
|
7523
|
+
if (this.stopRequested) return false;
|
|
7524
|
+
this.consecutiveFailures++;
|
|
7525
|
+
return false;
|
|
7526
|
+
}
|
|
7527
|
+
this.iterationsSinceCompact++;
|
|
7528
|
+
await this.maybeCompact().catch((err) => {
|
|
7529
|
+
this.opts.onError?.(
|
|
7530
|
+
err instanceof Error ? err : new Error(String(err)),
|
|
7531
|
+
this.consecutiveFailures
|
|
7532
|
+
);
|
|
7533
|
+
});
|
|
7534
|
+
return true;
|
|
7535
|
+
}
|
|
7536
|
+
/**
|
|
7537
|
+
* Run compaction when either trigger fires:
|
|
7538
|
+
* - We've done >= compactEveryNIterations since the last compact.
|
|
7539
|
+
* - Current request tokens exceed aggressiveCompactRatio * maxContext.
|
|
7540
|
+
*
|
|
7541
|
+
* The second check uses *aggressive* mode to free more headroom; the
|
|
7542
|
+
* cadence check uses non-aggressive (cheaper).
|
|
7543
|
+
*/
|
|
7544
|
+
async maybeCompact() {
|
|
7545
|
+
const compactor = this.opts.compactor;
|
|
7546
|
+
if (!compactor) return;
|
|
7547
|
+
const ctx = this.opts.agent.ctx;
|
|
7548
|
+
if (!ctx) return;
|
|
7549
|
+
const cadence = this.opts.compactEveryNIterations ?? 25;
|
|
7550
|
+
const threshold = this.opts.aggressiveCompactRatio ?? 0.85;
|
|
7551
|
+
const maxCtx = this.opts.maxContextTokens;
|
|
7552
|
+
let aggressive = false;
|
|
7553
|
+
let shouldRun = false;
|
|
7554
|
+
if (this.iterationsSinceCompact >= cadence) {
|
|
7555
|
+
shouldRun = true;
|
|
7556
|
+
}
|
|
7557
|
+
if (maxCtx && maxCtx > 0) {
|
|
7558
|
+
const used = ctx.tokenCounter?.currentRequestTokens?.();
|
|
7559
|
+
if (used) {
|
|
7560
|
+
const total = used.input + used.cacheRead;
|
|
7561
|
+
if (total / maxCtx >= threshold) {
|
|
7562
|
+
shouldRun = true;
|
|
7563
|
+
aggressive = true;
|
|
7564
|
+
}
|
|
7565
|
+
}
|
|
7566
|
+
}
|
|
7567
|
+
if (!shouldRun) return;
|
|
7568
|
+
const report = await compactor.compact(ctx, { aggressive });
|
|
7569
|
+
this.iterationsSinceCompact = 0;
|
|
7570
|
+
const saved = report.before - report.after;
|
|
7571
|
+
await this.appendIterationEntry({
|
|
7572
|
+
source: "manual",
|
|
7573
|
+
task: `compaction (${aggressive ? "aggressive" : "cadence"})`,
|
|
7574
|
+
status: "success",
|
|
7575
|
+
note: `saved ~${saved} tokens (${report.before}\u2192${report.after})`
|
|
7576
|
+
});
|
|
7577
|
+
}
|
|
7578
|
+
/**
|
|
7579
|
+
* Hybrid idea source.
|
|
7580
|
+
* 1. Pending todos on the agent's context.
|
|
7581
|
+
* 2. Dirty git working tree → propose a "review and finish this" task.
|
|
7582
|
+
* 3. Otherwise: brainstorm via the LLM against the goal.
|
|
7583
|
+
*
|
|
7584
|
+
* After failureBudget consecutive failures, force brainstorm so the
|
|
7585
|
+
* engine doesn't loop on the same broken todo or stuck git state.
|
|
7586
|
+
*/
|
|
7587
|
+
async decide(goal) {
|
|
7588
|
+
const forceBrainstorm = this.consecutiveFailures >= (this.opts.failureBudget ?? 3);
|
|
7589
|
+
if (!forceBrainstorm) {
|
|
7590
|
+
const todo = this.pickPendingTodo();
|
|
7591
|
+
if (todo) {
|
|
7592
|
+
return {
|
|
7593
|
+
source: "todo",
|
|
7594
|
+
task: todo.content,
|
|
7595
|
+
directive: this.buildDirective(goal, "todo", todo.content)
|
|
7596
|
+
};
|
|
7597
|
+
}
|
|
7598
|
+
const gitTask = await this.pickGitTask();
|
|
7599
|
+
if (gitTask) {
|
|
7600
|
+
return {
|
|
7601
|
+
source: "git",
|
|
7602
|
+
task: gitTask,
|
|
7603
|
+
directive: this.buildDirective(goal, "git", gitTask)
|
|
7604
|
+
};
|
|
7163
7605
|
}
|
|
7164
7606
|
}
|
|
7607
|
+
const brainstormed = await this.brainstormTask(goal);
|
|
7608
|
+
if (!brainstormed) return null;
|
|
7165
7609
|
return {
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
reason: "stopped externally"
|
|
7610
|
+
source: "brainstorm",
|
|
7611
|
+
task: brainstormed,
|
|
7612
|
+
directive: this.buildDirective(goal, "brainstorm", brainstormed)
|
|
7170
7613
|
};
|
|
7171
7614
|
}
|
|
7172
|
-
|
|
7173
|
-
|
|
7615
|
+
pickPendingTodo() {
|
|
7616
|
+
const todos = this.opts.agent.ctx.todos;
|
|
7617
|
+
if (!Array.isArray(todos)) return null;
|
|
7618
|
+
return todos.find((t2) => t2.status === "pending") ?? null;
|
|
7619
|
+
}
|
|
7620
|
+
async pickGitTask() {
|
|
7621
|
+
let out;
|
|
7622
|
+
try {
|
|
7623
|
+
out = await (this.opts.gitStatusReader?.() ?? this.readGitStatus());
|
|
7624
|
+
} catch {
|
|
7625
|
+
return null;
|
|
7626
|
+
}
|
|
7627
|
+
const dirty = out.trim();
|
|
7628
|
+
if (!dirty) return null;
|
|
7629
|
+
const lines = dirty.split("\n").slice(0, 8);
|
|
7630
|
+
const preview = lines.join(", ");
|
|
7631
|
+
return `Inspect the dirty working tree and either finish the in-progress work or revert it. Files: ${preview}`;
|
|
7632
|
+
}
|
|
7633
|
+
async readGitStatus() {
|
|
7634
|
+
const { stdout } = await execFileP("git", ["status", "--porcelain"], {
|
|
7635
|
+
cwd: this.opts.projectRoot,
|
|
7636
|
+
timeout: 5e3
|
|
7637
|
+
});
|
|
7638
|
+
return stdout;
|
|
7639
|
+
}
|
|
7640
|
+
async brainstormTask(goal) {
|
|
7641
|
+
const lastFew = goal.journal.slice(-5).map((e) => ` - [${e.status}] ${e.task}`).join("\n");
|
|
7642
|
+
const directive = [
|
|
7643
|
+
"You are deciding the next action in an autonomous loop pursuing a long-running goal.",
|
|
7644
|
+
"",
|
|
7645
|
+
`Goal: ${goal.goal}`,
|
|
7646
|
+
"",
|
|
7647
|
+
lastFew ? `Recent iterations:
|
|
7648
|
+
${lastFew}` : "No prior iterations yet.",
|
|
7649
|
+
"",
|
|
7650
|
+
"Output ONE concrete, immediately-actionable task that advances the goal.",
|
|
7651
|
+
"Constraints:",
|
|
7652
|
+
"- One sentence, imperative form, under 200 chars.",
|
|
7653
|
+
"- No preamble, no explanation, no markdown \u2014 just the task line.",
|
|
7654
|
+
"- If recent iterations show repeated failures on the same target, pivot.",
|
|
7655
|
+
"- If the goal appears fully accomplished, output exactly: DONE"
|
|
7656
|
+
].join("\n");
|
|
7657
|
+
try {
|
|
7658
|
+
const ctrl = new AbortController();
|
|
7659
|
+
const timer = setTimeout(() => ctrl.abort(), 6e4);
|
|
7660
|
+
try {
|
|
7661
|
+
const result = await this.opts.agent.run(
|
|
7662
|
+
[{ type: "text", text: directive }],
|
|
7663
|
+
{ signal: ctrl.signal, maxIterations: 1 }
|
|
7664
|
+
);
|
|
7665
|
+
if (result.status !== "done") return null;
|
|
7666
|
+
const text = (result.finalText ?? "").trim();
|
|
7667
|
+
if (!text || text === "DONE") return null;
|
|
7668
|
+
const firstLine = text.split("\n").find((l) => l.trim().length > 0)?.trim();
|
|
7669
|
+
if (!firstLine) return null;
|
|
7670
|
+
return firstLine.slice(0, 240);
|
|
7671
|
+
} finally {
|
|
7672
|
+
clearTimeout(timer);
|
|
7673
|
+
}
|
|
7674
|
+
} catch {
|
|
7675
|
+
return null;
|
|
7676
|
+
}
|
|
7677
|
+
}
|
|
7678
|
+
buildDirective(goal, source, task) {
|
|
7679
|
+
return [
|
|
7680
|
+
"[ETERNAL AUTONOMY \u2014 iteration directive]",
|
|
7681
|
+
"",
|
|
7682
|
+
`Goal: ${goal.goal}`,
|
|
7683
|
+
`Source: ${source}`,
|
|
7684
|
+
`Task: ${task}`,
|
|
7685
|
+
"",
|
|
7686
|
+
"Execute this task end-to-end using the tools available to you. Make the",
|
|
7687
|
+
"changes, run tests if relevant, and commit / push as appropriate. Do not",
|
|
7688
|
+
"ask for confirmation \u2014 YOLO mode is active. When the task is done, stop;",
|
|
7689
|
+
"the loop will pick the next action."
|
|
7690
|
+
].join("\n");
|
|
7691
|
+
}
|
|
7692
|
+
async appendIterationEntry(entry) {
|
|
7693
|
+
const current = await loadGoal(this.goalPath);
|
|
7694
|
+
if (!current) {
|
|
7695
|
+
return;
|
|
7696
|
+
}
|
|
7697
|
+
const updated = appendJournal(current, entry);
|
|
7698
|
+
await saveGoal(this.goalPath, updated);
|
|
7699
|
+
}
|
|
7700
|
+
async appendFailure(task, note) {
|
|
7701
|
+
await this.appendIterationEntry({ source: "manual", task, status: "failure", note });
|
|
7702
|
+
}
|
|
7703
|
+
async persistEngineState(state) {
|
|
7704
|
+
const current = await loadGoal(this.goalPath);
|
|
7705
|
+
if (!current) return;
|
|
7706
|
+
if (current.engineState === state) return;
|
|
7707
|
+
await saveGoal(this.goalPath, { ...current, engineState: state });
|
|
7174
7708
|
}
|
|
7175
7709
|
};
|
|
7710
|
+
function sleep(ms) {
|
|
7711
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
7712
|
+
}
|
|
7713
|
+
|
|
7714
|
+
// src/coordination/director.ts
|
|
7715
|
+
init_atomic_write();
|
|
7176
7716
|
|
|
7177
7717
|
// src/coordination/director-prompts.ts
|
|
7178
7718
|
var DEFAULT_DIRECTOR_PREAMBLE = `You are the Director of a multi-agent fleet. You orchestrate worker
|
|
@@ -8318,10 +8858,10 @@ function makeSpawnTool(director, roster) {
|
|
|
8318
8858
|
const subagentId = await director.spawn(cfg);
|
|
8319
8859
|
return { subagentId, provider: cfg.provider, model: cfg.model, name: cfg.name };
|
|
8320
8860
|
} catch (err) {
|
|
8321
|
-
if (err instanceof
|
|
8861
|
+
if (err instanceof FleetSpawnBudgetError) {
|
|
8322
8862
|
return { error: err.message, kind: err.kind, limit: err.limit, observed: err.observed };
|
|
8323
8863
|
}
|
|
8324
|
-
if (err instanceof
|
|
8864
|
+
if (err instanceof FleetCostCapError) {
|
|
8325
8865
|
return { error: err.message, kind: err.kind, limit: err.limit, observed: err.observed };
|
|
8326
8866
|
}
|
|
8327
8867
|
return { error: err instanceof Error ? err.message : String(err) };
|
|
@@ -8522,7 +9062,7 @@ function makeFleetHealthTool(director) {
|
|
|
8522
9062
|
}
|
|
8523
9063
|
|
|
8524
9064
|
// src/coordination/director.ts
|
|
8525
|
-
var
|
|
9065
|
+
var FleetSpawnBudgetError = class extends Error {
|
|
8526
9066
|
kind;
|
|
8527
9067
|
limit;
|
|
8528
9068
|
observed;
|
|
@@ -8530,13 +9070,13 @@ var DirectorBudgetError = class extends Error {
|
|
|
8530
9070
|
super(
|
|
8531
9071
|
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}`
|
|
8532
9072
|
);
|
|
8533
|
-
this.name = "
|
|
9073
|
+
this.name = "FleetSpawnBudgetError";
|
|
8534
9074
|
this.kind = kind;
|
|
8535
9075
|
this.limit = limit;
|
|
8536
9076
|
this.observed = observed;
|
|
8537
9077
|
}
|
|
8538
9078
|
};
|
|
8539
|
-
var
|
|
9079
|
+
var FleetCostCapError = class extends Error {
|
|
8540
9080
|
kind;
|
|
8541
9081
|
limit;
|
|
8542
9082
|
observed;
|
|
@@ -8544,16 +9084,34 @@ var DirectorCostCapError = class extends Error {
|
|
|
8544
9084
|
super(
|
|
8545
9085
|
`Director cost cap exceeded: total fleet spend ${observed.toFixed(4)} exceeds maxCostUsd ${limit.toFixed(4)}`
|
|
8546
9086
|
);
|
|
8547
|
-
this.name = "
|
|
9087
|
+
this.name = "FleetCostCapError";
|
|
8548
9088
|
this.kind = "max_cost_usd";
|
|
8549
9089
|
this.limit = limit;
|
|
8550
9090
|
this.observed = observed;
|
|
8551
9091
|
}
|
|
8552
9092
|
};
|
|
8553
9093
|
var Director = class {
|
|
9094
|
+
/** Alias for the ICoordinator contract. `id` is retained for backward compatibility. */
|
|
9095
|
+
get coordinatorId() {
|
|
9096
|
+
return this.id;
|
|
9097
|
+
}
|
|
8554
9098
|
id;
|
|
9099
|
+
/**
|
|
9100
|
+
* The fleet event bus. Backed by `fleetManager?.fleet` when a FleetManager
|
|
9101
|
+
* is injected; otherwise own FleetBus instance (preserves existing behavior).
|
|
9102
|
+
*/
|
|
8555
9103
|
fleet;
|
|
9104
|
+
/**
|
|
9105
|
+
* Usage rollup. Backed by `fleetManager?.usage` when a FleetManager is
|
|
9106
|
+
* injected; otherwise own FleetUsageAggregator.
|
|
9107
|
+
*/
|
|
8556
9108
|
usage;
|
|
9109
|
+
/**
|
|
9110
|
+
* Optional fleet-level policy container. When provided the Director
|
|
9111
|
+
* delegates spawn budgeting, manifest entries, and checkpointing to it
|
|
9112
|
+
* instead of managing those internally. All other behavior is unchanged.
|
|
9113
|
+
*/
|
|
9114
|
+
fleetManager;
|
|
8557
9115
|
/**
|
|
8558
9116
|
* Director-side bridge endpoint. Subagents are wired to the same
|
|
8559
9117
|
* in-memory transport so the director can `ask()` them synchronously
|
|
@@ -8604,8 +9162,8 @@ var Director = class {
|
|
|
8604
9162
|
/** Debounce timer for periodic manifest writes. */
|
|
8605
9163
|
manifestTimer = null;
|
|
8606
9164
|
manifestDebounceMs;
|
|
8607
|
-
/** Fleet-wide cost cap. Infinity means no cap. */
|
|
8608
|
-
|
|
9165
|
+
/** Fleet-wide cost cap (entire fleet total, distinct from SubagentBudget limits). Infinity means no cap. */
|
|
9166
|
+
maxFleetCostUsd;
|
|
8609
9167
|
/** Max auto-extensions per subagent per budget kind before denying. */
|
|
8610
9168
|
maxBudgetExtensions;
|
|
8611
9169
|
/** Sessions root for direct subagent JSONL reads (fleet_session tool). */
|
|
@@ -8638,7 +9196,7 @@ var Director = class {
|
|
|
8638
9196
|
this.spawnDepth = opts.spawnDepth ?? 0;
|
|
8639
9197
|
this.sessionWriter = opts.sessionWriter ?? null;
|
|
8640
9198
|
this.manifestDebounceMs = opts.manifestDebounceMs ?? 2e3;
|
|
8641
|
-
this.
|
|
9199
|
+
this.maxFleetCostUsd = opts.directorBudget?.maxCostUsd ?? Number.POSITIVE_INFINITY;
|
|
8642
9200
|
this.maxBudgetExtensions = opts.maxBudgetExtensions ?? 2;
|
|
8643
9201
|
this.sessionsRoot = opts.sessionsRoot;
|
|
8644
9202
|
this.directorRunId = opts.directorRunId ?? this.id;
|
|
@@ -8649,20 +9207,28 @@ var Director = class {
|
|
|
8649
9207
|
maxSpawnDepth: this.maxSpawnDepth,
|
|
8650
9208
|
directorBudget: opts.directorBudget
|
|
8651
9209
|
}, opts.checkpointDebounceMs ?? 250) : null;
|
|
9210
|
+
this.fleetManager = opts.fleetManager;
|
|
8652
9211
|
if (this.sharedScratchpadPath) {
|
|
8653
|
-
void fsp2.mkdir(this.sharedScratchpadPath, { recursive: true }).catch(
|
|
9212
|
+
void fsp2.mkdir(this.sharedScratchpadPath, { recursive: true }).catch(
|
|
9213
|
+
(err) => this.logShutdownError("shared_scratchpad_mkdir", err)
|
|
9214
|
+
);
|
|
8654
9215
|
}
|
|
8655
9216
|
this.transport = new InMemoryBridgeTransport();
|
|
8656
9217
|
this.bridge = new InMemoryAgentBridge(
|
|
8657
9218
|
{ agentId: this.id, coordinatorId: this.id },
|
|
8658
9219
|
this.transport
|
|
8659
9220
|
);
|
|
8660
|
-
this.
|
|
8661
|
-
|
|
8662
|
-
this.
|
|
8663
|
-
|
|
8664
|
-
|
|
8665
|
-
|
|
9221
|
+
if (this.fleetManager) {
|
|
9222
|
+
this.fleet = this.fleetManager.fleet;
|
|
9223
|
+
this.usage = this.fleetManager.usage;
|
|
9224
|
+
} else {
|
|
9225
|
+
this.fleet = new FleetBus();
|
|
9226
|
+
this.usage = new FleetUsageAggregator(
|
|
9227
|
+
this.fleet,
|
|
9228
|
+
(id) => this.priceLookups.get(id),
|
|
9229
|
+
(id) => this.subagentMeta.get(id)
|
|
9230
|
+
);
|
|
9231
|
+
}
|
|
8666
9232
|
this.coordinator = new DefaultMultiAgentCoordinator(
|
|
8667
9233
|
{ ...opts.config, coordinatorId: this.id },
|
|
8668
9234
|
{ runner: opts.runner }
|
|
@@ -8751,7 +9317,9 @@ var Director = class {
|
|
|
8751
9317
|
if (this.manifestTimer) return;
|
|
8752
9318
|
this.manifestTimer = setTimeout(() => {
|
|
8753
9319
|
this.manifestTimer = null;
|
|
8754
|
-
void this.writeManifest().catch(
|
|
9320
|
+
void this.writeManifest().catch(
|
|
9321
|
+
(err) => this.logShutdownError("manifest_write_debounced", err)
|
|
9322
|
+
);
|
|
8755
9323
|
}, this.manifestDebounceMs);
|
|
8756
9324
|
}
|
|
8757
9325
|
/**
|
|
@@ -8764,58 +9332,73 @@ var Director = class {
|
|
|
8764
9332
|
* it the `cost` column in `usage.snapshot()` stays at 0.
|
|
8765
9333
|
*/
|
|
8766
9334
|
async spawn(config, priceLookup) {
|
|
8767
|
-
if (this.
|
|
8768
|
-
|
|
8769
|
-
|
|
8770
|
-
|
|
8771
|
-
|
|
8772
|
-
|
|
8773
|
-
|
|
8774
|
-
|
|
8775
|
-
if (
|
|
8776
|
-
throw new
|
|
9335
|
+
if (this.fleetManager) {
|
|
9336
|
+
const rejection = this.fleetManager.canSpawn(config);
|
|
9337
|
+
if (rejection) {
|
|
9338
|
+
if (rejection.kind === "max_spawn_depth") throw new FleetSpawnBudgetError("max_spawn_depth", rejection.limit, rejection.observed);
|
|
9339
|
+
if (rejection.kind === "max_spawns") throw new FleetSpawnBudgetError("max_spawns", rejection.limit, rejection.observed);
|
|
9340
|
+
if (rejection.kind === "max_cost_usd") throw new FleetCostCapError(rejection.limit, rejection.observed);
|
|
9341
|
+
}
|
|
9342
|
+
} else {
|
|
9343
|
+
if (this.spawnDepth >= this.maxSpawnDepth) {
|
|
9344
|
+
throw new FleetSpawnBudgetError("max_spawn_depth", this.maxSpawnDepth, this.spawnDepth);
|
|
9345
|
+
}
|
|
9346
|
+
if (this.spawnCount >= this.maxSpawns) {
|
|
9347
|
+
throw new FleetSpawnBudgetError("max_spawns", this.maxSpawns, this.spawnCount + 1);
|
|
9348
|
+
}
|
|
9349
|
+
if (this.maxFleetCostUsd < Number.POSITIVE_INFINITY) {
|
|
9350
|
+
const totalCost = this.usage.snapshot().total?.cost ?? 0;
|
|
9351
|
+
if (totalCost >= this.maxFleetCostUsd) {
|
|
9352
|
+
throw new FleetCostCapError(this.maxFleetCostUsd, totalCost);
|
|
9353
|
+
}
|
|
8777
9354
|
}
|
|
8778
9355
|
}
|
|
8779
9356
|
const result = await this.coordinator.spawn(config);
|
|
8780
|
-
this.
|
|
8781
|
-
|
|
8782
|
-
|
|
8783
|
-
|
|
8784
|
-
|
|
8785
|
-
|
|
9357
|
+
if (this.fleetManager) {
|
|
9358
|
+
this.fleetManager.recordSpawn(result.subagentId, config, priceLookup);
|
|
9359
|
+
} else {
|
|
9360
|
+
this.spawnCount += 1;
|
|
9361
|
+
this.subagentMeta.set(result.subagentId, {
|
|
9362
|
+
provider: config.provider,
|
|
9363
|
+
model: config.model
|
|
9364
|
+
});
|
|
9365
|
+
if (priceLookup) this.priceLookups.set(result.subagentId, priceLookup);
|
|
9366
|
+
}
|
|
8786
9367
|
const subagentBridge = new InMemoryAgentBridge(
|
|
8787
9368
|
{ agentId: result.subagentId, coordinatorId: this.id },
|
|
8788
9369
|
this.transport
|
|
8789
9370
|
);
|
|
8790
9371
|
this.coordinator.setSubagentBridge(result.subagentId, subagentBridge);
|
|
8791
9372
|
this.subagentBridges.set(result.subagentId, subagentBridge);
|
|
8792
|
-
this.
|
|
8793
|
-
|
|
8794
|
-
|
|
8795
|
-
role: config.role,
|
|
8796
|
-
provider: config.provider,
|
|
8797
|
-
model: config.model,
|
|
8798
|
-
taskIds: []
|
|
8799
|
-
});
|
|
8800
|
-
const spawnedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8801
|
-
this.stateCheckpoint?.recordSpawn(
|
|
8802
|
-
{
|
|
8803
|
-
id: result.subagentId,
|
|
9373
|
+
if (!this.fleetManager) {
|
|
9374
|
+
this.manifestEntries.set(result.subagentId, {
|
|
9375
|
+
subagentId: result.subagentId,
|
|
8804
9376
|
name: config.name,
|
|
8805
9377
|
role: config.role,
|
|
8806
9378
|
provider: config.provider,
|
|
8807
9379
|
model: config.model,
|
|
8808
|
-
|
|
8809
|
-
}
|
|
8810
|
-
|
|
8811
|
-
|
|
8812
|
-
|
|
8813
|
-
|
|
8814
|
-
|
|
8815
|
-
|
|
8816
|
-
|
|
8817
|
-
|
|
8818
|
-
|
|
9380
|
+
taskIds: []
|
|
9381
|
+
});
|
|
9382
|
+
const spawnedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
9383
|
+
this.stateCheckpoint?.recordSpawn(
|
|
9384
|
+
{
|
|
9385
|
+
id: result.subagentId,
|
|
9386
|
+
name: config.name,
|
|
9387
|
+
role: config.role,
|
|
9388
|
+
provider: config.provider,
|
|
9389
|
+
model: config.model,
|
|
9390
|
+
spawnedAt
|
|
9391
|
+
},
|
|
9392
|
+
this.spawnCount
|
|
9393
|
+
);
|
|
9394
|
+
void this.appendSessionEvent({
|
|
9395
|
+
type: "agent_spawned",
|
|
9396
|
+
ts: spawnedAt,
|
|
9397
|
+
agentId: result.subagentId,
|
|
9398
|
+
role: config.role ?? config.name
|
|
9399
|
+
});
|
|
9400
|
+
this.scheduleManifest();
|
|
9401
|
+
}
|
|
8819
9402
|
return result.subagentId;
|
|
8820
9403
|
}
|
|
8821
9404
|
/**
|
|
@@ -8927,7 +9510,7 @@ var Director = class {
|
|
|
8927
9510
|
usage: this.usage.snapshot()
|
|
8928
9511
|
};
|
|
8929
9512
|
await fsp2.mkdir(path6.dirname(this.manifestPath), { recursive: true });
|
|
8930
|
-
await
|
|
9513
|
+
await atomicWrite(this.manifestPath, JSON.stringify(manifest, null, 2), { mode: 384 });
|
|
8931
9514
|
return this.manifestPath;
|
|
8932
9515
|
}
|
|
8933
9516
|
/**
|
|
@@ -8982,8 +9565,12 @@ var Director = class {
|
|
|
8982
9565
|
async assign(task) {
|
|
8983
9566
|
const taskWithId = task.id ? task : { ...task, id: randomUUID() };
|
|
8984
9567
|
if (task.subagentId) {
|
|
8985
|
-
|
|
8986
|
-
|
|
9568
|
+
if (this.fleetManager) {
|
|
9569
|
+
this.fleetManager.addTaskToSubagent(task.subagentId, taskWithId.id);
|
|
9570
|
+
} else {
|
|
9571
|
+
const entry = this.manifestEntries.get(task.subagentId);
|
|
9572
|
+
if (entry) entry.taskIds.push(taskWithId.id);
|
|
9573
|
+
}
|
|
8987
9574
|
}
|
|
8988
9575
|
await this.coordinator.assign(taskWithId);
|
|
8989
9576
|
this.taskDescriptions.set(taskWithId.id, taskWithId.description);
|
|
@@ -9181,8 +9768,9 @@ var Director = class {
|
|
|
9181
9768
|
* still permission-checked normally.
|
|
9182
9769
|
*/
|
|
9183
9770
|
tools(roster) {
|
|
9771
|
+
const effectiveRoster = roster ?? this.roster;
|
|
9184
9772
|
const t2 = [
|
|
9185
|
-
makeSpawnTool(this,
|
|
9773
|
+
makeSpawnTool(this, effectiveRoster),
|
|
9186
9774
|
makeAssignTool(this),
|
|
9187
9775
|
makeAwaitTasksTool(this),
|
|
9188
9776
|
makeAskTool(this),
|
|
@@ -9451,9 +10039,11 @@ async function readSubagentPartial(opts, subagentId) {
|
|
|
9451
10039
|
candidates.push(path6.join(opts.sessionsRoot, opts.directorRunId, `${subagentId}.jsonl`));
|
|
9452
10040
|
} else {
|
|
9453
10041
|
try {
|
|
9454
|
-
const
|
|
9455
|
-
for (const
|
|
9456
|
-
|
|
10042
|
+
const entries = await fsp2.readdir(opts.sessionsRoot, { withFileTypes: true });
|
|
10043
|
+
for (const entry of entries) {
|
|
10044
|
+
if (entry.isDirectory()) {
|
|
10045
|
+
candidates.push(path6.join(opts.sessionsRoot, entry.name, `${subagentId}.jsonl`));
|
|
10046
|
+
}
|
|
9457
10047
|
}
|
|
9458
10048
|
} catch {
|
|
9459
10049
|
return void 0;
|
|
@@ -9648,6 +10238,12 @@ function makeDirectorSessionFactory(opts) {
|
|
|
9648
10238
|
}
|
|
9649
10239
|
};
|
|
9650
10240
|
}
|
|
10241
|
+
|
|
10242
|
+
// src/coordination/null-fleet-bus.ts
|
|
10243
|
+
var NULL_FLEET_BUS = new FleetBus();
|
|
10244
|
+
|
|
10245
|
+
// src/models/mode-store.ts
|
|
10246
|
+
init_atomic_write();
|
|
9651
10247
|
var DefaultModeStore = class {
|
|
9652
10248
|
activeModeId = null;
|
|
9653
10249
|
modes;
|
|
@@ -9706,10 +10302,9 @@ var DefaultModeStore = class {
|
|
|
9706
10302
|
try {
|
|
9707
10303
|
await fsp2.mkdir(this.configDir, { recursive: true });
|
|
9708
10304
|
const configPath = path6.join(this.configDir, "mode.json");
|
|
9709
|
-
await
|
|
10305
|
+
await atomicWrite(
|
|
9710
10306
|
configPath,
|
|
9711
|
-
JSON.stringify({ activeMode: this.activeModeId }, null, 2)
|
|
9712
|
-
"utf8"
|
|
10307
|
+
JSON.stringify({ activeMode: this.activeModeId }, null, 2)
|
|
9713
10308
|
);
|
|
9714
10309
|
} catch {
|
|
9715
10310
|
}
|
|
@@ -10563,6 +11158,9 @@ var SpecDrivenDev = class {
|
|
|
10563
11158
|
}));
|
|
10564
11159
|
}
|
|
10565
11160
|
};
|
|
11161
|
+
|
|
11162
|
+
// src/sdd/spec-store.ts
|
|
11163
|
+
init_atomic_write();
|
|
10566
11164
|
var SpecStore = class {
|
|
10567
11165
|
baseDir;
|
|
10568
11166
|
indexPath;
|
|
@@ -10672,6 +11270,9 @@ var SpecStore = class {
|
|
|
10672
11270
|
await atomicWrite(this.indexPath, JSON.stringify(index, null, 2), { mode: 384 });
|
|
10673
11271
|
}
|
|
10674
11272
|
};
|
|
11273
|
+
|
|
11274
|
+
// src/sdd/task-graph-store.ts
|
|
11275
|
+
init_atomic_write();
|
|
10675
11276
|
function graphToJSON(graph) {
|
|
10676
11277
|
const serialisable = {
|
|
10677
11278
|
...graph,
|
|
@@ -10973,10 +11574,11 @@ var AISpecBuilder = class {
|
|
|
10973
11574
|
async saveSession() {
|
|
10974
11575
|
if (!this.sessionPath) return;
|
|
10975
11576
|
try {
|
|
10976
|
-
const
|
|
10977
|
-
const
|
|
10978
|
-
await
|
|
10979
|
-
await
|
|
11577
|
+
const fsp16 = await import('fs/promises');
|
|
11578
|
+
const path26 = await import('path');
|
|
11579
|
+
const { atomicWrite: atomicWrite2 } = await Promise.resolve().then(() => (init_atomic_write(), atomic_write_exports));
|
|
11580
|
+
await fsp16.mkdir(path26.dirname(this.sessionPath), { recursive: true });
|
|
11581
|
+
await atomicWrite2(this.sessionPath, JSON.stringify(this.session, null, 2));
|
|
10980
11582
|
} catch {
|
|
10981
11583
|
}
|
|
10982
11584
|
}
|
|
@@ -10984,8 +11586,8 @@ var AISpecBuilder = class {
|
|
|
10984
11586
|
async loadSession() {
|
|
10985
11587
|
if (!this.sessionPath) return false;
|
|
10986
11588
|
try {
|
|
10987
|
-
const
|
|
10988
|
-
const raw = await
|
|
11589
|
+
const fsp16 = await import('fs/promises');
|
|
11590
|
+
const raw = await fsp16.readFile(this.sessionPath, "utf8");
|
|
10989
11591
|
const loaded = JSON.parse(raw);
|
|
10990
11592
|
if (loaded?.id && loaded?.phase && loaded?.title) {
|
|
10991
11593
|
this.session = loaded;
|
|
@@ -10999,14 +11601,21 @@ var AISpecBuilder = class {
|
|
|
10999
11601
|
async deleteSession() {
|
|
11000
11602
|
if (!this.sessionPath) return;
|
|
11001
11603
|
try {
|
|
11002
|
-
const
|
|
11003
|
-
await
|
|
11604
|
+
const fsp16 = await import('fs/promises');
|
|
11605
|
+
await fsp16.unlink(this.sessionPath);
|
|
11004
11606
|
} catch {
|
|
11005
11607
|
}
|
|
11006
11608
|
}
|
|
11007
|
-
/** Auto-save helper — calls saveSession() but never throws.
|
|
11609
|
+
/** Auto-save helper — calls saveSession() but never throws.
|
|
11610
|
+
* Failures are surfaced via process.emitWarning so a persistent
|
|
11611
|
+
* ENOSPC / EACCES doesn't silently strand session edits in memory. */
|
|
11008
11612
|
autoSave() {
|
|
11009
|
-
this.saveSession().catch(() => {
|
|
11613
|
+
this.saveSession().catch((err) => {
|
|
11614
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
11615
|
+
process.emitWarning(
|
|
11616
|
+
`SpecBuilder autoSave failed: ${detail}`,
|
|
11617
|
+
"SpecBuilderWarning"
|
|
11618
|
+
);
|
|
11010
11619
|
});
|
|
11011
11620
|
}
|
|
11012
11621
|
// ── Session Lifecycle ─────────────────────────────────────────────────────
|
|
@@ -11678,15 +12287,15 @@ function computeCriticalPath(graph, topoOrder, blockedByMap) {
|
|
|
11678
12287
|
maxId = id;
|
|
11679
12288
|
}
|
|
11680
12289
|
}
|
|
11681
|
-
const
|
|
12290
|
+
const path26 = [];
|
|
11682
12291
|
let current = maxId;
|
|
11683
12292
|
const visited = /* @__PURE__ */ new Set();
|
|
11684
12293
|
while (current && !visited.has(current)) {
|
|
11685
12294
|
visited.add(current);
|
|
11686
|
-
|
|
12295
|
+
path26.unshift(current);
|
|
11687
12296
|
current = prev.get(current) ?? null;
|
|
11688
12297
|
}
|
|
11689
|
-
return
|
|
12298
|
+
return path26;
|
|
11690
12299
|
}
|
|
11691
12300
|
function computeParallelGroups(graph, blockedByMap) {
|
|
11692
12301
|
const groups = [];
|
|
@@ -12396,7 +13005,7 @@ async function startMetricsServer(opts) {
|
|
|
12396
13005
|
const tls = opts.tls;
|
|
12397
13006
|
const useHttps = !!(tls?.cert && tls?.key);
|
|
12398
13007
|
const host = opts.host ?? "127.0.0.1";
|
|
12399
|
-
const
|
|
13008
|
+
const path26 = opts.path ?? "/metrics";
|
|
12400
13009
|
const healthPath = opts.healthPath ?? "/healthz";
|
|
12401
13010
|
const healthRegistry = opts.healthRegistry;
|
|
12402
13011
|
const listener = (req, res) => {
|
|
@@ -12406,7 +13015,7 @@ async function startMetricsServer(opts) {
|
|
|
12406
13015
|
return;
|
|
12407
13016
|
}
|
|
12408
13017
|
const url = req.url.split("?")[0];
|
|
12409
|
-
if (url ===
|
|
13018
|
+
if (url === path26) {
|
|
12410
13019
|
let body;
|
|
12411
13020
|
try {
|
|
12412
13021
|
body = renderPrometheus(opts.sink.snapshot());
|
|
@@ -12470,7 +13079,7 @@ async function startMetricsServer(opts) {
|
|
|
12470
13079
|
const protocol = useHttps ? "https" : "http";
|
|
12471
13080
|
return {
|
|
12472
13081
|
port: boundPort,
|
|
12473
|
-
url: `${protocol}://${host}:${boundPort}${
|
|
13082
|
+
url: `${protocol}://${host}:${boundPort}${path26}`,
|
|
12474
13083
|
close: () => new Promise((resolve5, reject) => {
|
|
12475
13084
|
server.close((err) => err ? reject(err) : resolve5());
|
|
12476
13085
|
})
|
|
@@ -13197,6 +13806,9 @@ function stripTopDir(p) {
|
|
|
13197
13806
|
if (idx === -1) return "";
|
|
13198
13807
|
return p.slice(idx + 1);
|
|
13199
13808
|
}
|
|
13809
|
+
|
|
13810
|
+
// src/skills/manifest-store.ts
|
|
13811
|
+
init_atomic_write();
|
|
13200
13812
|
var SkillManifestStore = class {
|
|
13201
13813
|
manifestPath;
|
|
13202
13814
|
cache;
|
|
@@ -13221,7 +13833,7 @@ var SkillManifestStore = class {
|
|
|
13221
13833
|
async write(data) {
|
|
13222
13834
|
const dir = path6.dirname(this.manifestPath);
|
|
13223
13835
|
await fsp2.mkdir(dir, { recursive: true });
|
|
13224
|
-
await
|
|
13836
|
+
await atomicWrite(this.manifestPath, JSON.stringify(data, null, 2) + "\n");
|
|
13225
13837
|
this.cache = data;
|
|
13226
13838
|
}
|
|
13227
13839
|
async addEntry(entry) {
|
|
@@ -13552,6 +14164,9 @@ async function collectFiles(dir, baseDir) {
|
|
|
13552
14164
|
}
|
|
13553
14165
|
return results;
|
|
13554
14166
|
}
|
|
14167
|
+
|
|
14168
|
+
// src/storage/session-rewinder.ts
|
|
14169
|
+
init_atomic_write();
|
|
13555
14170
|
var DefaultSessionRewinder = class {
|
|
13556
14171
|
constructor(sessionsDir) {
|
|
13557
14172
|
this.sessionsDir = sessionsDir;
|
|
@@ -13685,7 +14300,7 @@ async function revertSnapshots(snapshots) {
|
|
|
13685
14300
|
try {
|
|
13686
14301
|
if (file.action === "deleted") {
|
|
13687
14302
|
if (file.before !== null) {
|
|
13688
|
-
await
|
|
14303
|
+
await atomicWrite(file.path, file.before, { mode: 420 });
|
|
13689
14304
|
revertedFiles.push(file.path);
|
|
13690
14305
|
}
|
|
13691
14306
|
} else if (file.action === "created") {
|
|
@@ -13693,7 +14308,7 @@ async function revertSnapshots(snapshots) {
|
|
|
13693
14308
|
revertedFiles.push(file.path);
|
|
13694
14309
|
} else if (file.action === "modified") {
|
|
13695
14310
|
if (file.before !== null) {
|
|
13696
|
-
await
|
|
14311
|
+
await atomicWrite(file.path, file.before, { mode: 420 });
|
|
13697
14312
|
revertedFiles.push(file.path);
|
|
13698
14313
|
}
|
|
13699
14314
|
}
|
|
@@ -14479,10 +15094,10 @@ var SecurityScanner = class {
|
|
|
14479
15094
|
for (const pattern of patterns) {
|
|
14480
15095
|
if (!this.matchesCategory(pattern)) continue;
|
|
14481
15096
|
for (const regex of pattern.patterns) {
|
|
14482
|
-
regex.lastIndex = 0;
|
|
14483
15097
|
for (let lineNum = 0; lineNum < lines.length; lineNum++) {
|
|
14484
15098
|
const line = lines[lineNum];
|
|
14485
15099
|
if (!line) continue;
|
|
15100
|
+
regex.lastIndex = 0;
|
|
14486
15101
|
if (regex.test(line)) {
|
|
14487
15102
|
if (this.isFalsePositive(line, pattern.falsePositiveMarkers)) {
|
|
14488
15103
|
continue;
|
|
@@ -14544,6 +15159,9 @@ var SecurityScanner = class {
|
|
|
14544
15159
|
}
|
|
14545
15160
|
};
|
|
14546
15161
|
var defaultSecurityScanner = new SecurityScanner();
|
|
15162
|
+
|
|
15163
|
+
// src/security-scanner/report-generator.ts
|
|
15164
|
+
init_atomic_write();
|
|
14547
15165
|
var DEFAULT_REPORT_OPTIONS = {
|
|
14548
15166
|
outputDir: "security-reports",
|
|
14549
15167
|
format: "markdown",
|
|
@@ -14571,15 +15189,15 @@ var ReportGenerator = class {
|
|
|
14571
15189
|
default:
|
|
14572
15190
|
content = this.generateMarkdown(scanResult);
|
|
14573
15191
|
}
|
|
14574
|
-
await
|
|
15192
|
+
await atomicWrite(filepath, content);
|
|
14575
15193
|
return filepath;
|
|
14576
15194
|
}
|
|
14577
15195
|
async ensureOutputDir() {
|
|
14578
15196
|
try {
|
|
14579
15197
|
await stat(this.options.outputDir);
|
|
14580
15198
|
} catch {
|
|
14581
|
-
const { mkdir:
|
|
14582
|
-
await
|
|
15199
|
+
const { mkdir: mkdir11 } = await import('fs/promises');
|
|
15200
|
+
await mkdir11(this.options.outputDir, { recursive: true });
|
|
14583
15201
|
}
|
|
14584
15202
|
}
|
|
14585
15203
|
generateMarkdown(result) {
|
|
@@ -14763,6 +15381,9 @@ var ReportGenerator = class {
|
|
|
14763
15381
|
}
|
|
14764
15382
|
};
|
|
14765
15383
|
var defaultReportGenerator = new ReportGenerator();
|
|
15384
|
+
|
|
15385
|
+
// src/security-scanner/gitignore-updater.ts
|
|
15386
|
+
init_atomic_write();
|
|
14766
15387
|
var DEFAULT_OPTIONS2 = {
|
|
14767
15388
|
gitignorePath: ".gitignore",
|
|
14768
15389
|
entries: ["security-reports/", "security-reports/*"]
|
|
@@ -14789,12 +15410,12 @@ var GitignoreUpdater = class {
|
|
|
14789
15410
|
}
|
|
14790
15411
|
if (added.length > 0) {
|
|
14791
15412
|
const newContent = [...lines].filter(Boolean).join("\n") + "\n";
|
|
14792
|
-
await
|
|
15413
|
+
await atomicWrite(this.options.gitignorePath, newContent);
|
|
14793
15414
|
}
|
|
14794
15415
|
} catch (err) {
|
|
14795
15416
|
if (err.code === "ENOENT") {
|
|
14796
15417
|
const content = this.options.entries.join("\n") + "\n";
|
|
14797
|
-
await
|
|
15418
|
+
await atomicWrite(this.options.gitignorePath, content);
|
|
14798
15419
|
added.push(...this.options.entries);
|
|
14799
15420
|
} else {
|
|
14800
15421
|
errors.push(`Failed to update .gitignore: ${err}`);
|
|
@@ -14813,6 +15434,9 @@ var GitignoreUpdater = class {
|
|
|
14813
15434
|
}
|
|
14814
15435
|
};
|
|
14815
15436
|
var defaultGitignoreUpdater = new GitignoreUpdater();
|
|
15437
|
+
|
|
15438
|
+
// src/security-scanner/orchestrator.ts
|
|
15439
|
+
init_atomic_write();
|
|
14816
15440
|
var NETWORK_ERR_RE2 = /ECONN|ETIMEDOUT|ETIME|ENOTFOUND|EAI_AGAIN|fetch failed/i;
|
|
14817
15441
|
var SecurityScannerOrchestrator = class {
|
|
14818
15442
|
constructor(retryPolicy, errorHandler) {
|
|
@@ -14858,7 +15482,7 @@ var SecurityScannerOrchestrator = class {
|
|
|
14858
15482
|
*/
|
|
14859
15483
|
async run(ctx, options) {
|
|
14860
15484
|
const { projectRoot, reportOptions, skipGitignore, model: explicitModel } = options;
|
|
14861
|
-
const provider = "provider" in ctx ? ctx.provider : ctx;
|
|
15485
|
+
const provider = "provider" in ctx && ctx.provider ? ctx.provider : ctx;
|
|
14862
15486
|
const model = explicitModel ?? ("model" in ctx ? ctx.model : void 0);
|
|
14863
15487
|
const detectionResult = await this.detector.detect(projectRoot);
|
|
14864
15488
|
if (detectionResult.detectedStacks.length === 0) {
|
|
@@ -15197,7 +15821,7 @@ Be specific about the vulnerabilities found and how to fix them.`;
|
|
|
15197
15821
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
15198
15822
|
const filename = `security-report-${timestamp}.${format}`;
|
|
15199
15823
|
const filepath = join(outputDir, filename);
|
|
15200
|
-
await
|
|
15824
|
+
await atomicWrite(filepath, content);
|
|
15201
15825
|
return filepath;
|
|
15202
15826
|
}
|
|
15203
15827
|
/**
|
|
@@ -15371,11 +15995,21 @@ Show a previous security report.
|
|
|
15371
15995
|
}
|
|
15372
15996
|
};
|
|
15373
15997
|
}
|
|
15998
|
+
function getProviderFromContext(ctx) {
|
|
15999
|
+
if (ctx.provider && typeof ctx.provider.complete === "function") {
|
|
16000
|
+
return { provider: ctx.provider, model: ctx.model };
|
|
16001
|
+
}
|
|
16002
|
+
return null;
|
|
16003
|
+
}
|
|
15374
16004
|
async function handleScan(args, ctx) {
|
|
15375
16005
|
const options = parseArgs(args);
|
|
15376
16006
|
const projectRoot = ctx.projectRoot || ctx.cwd || process.cwd();
|
|
15377
16007
|
try {
|
|
15378
|
-
const
|
|
16008
|
+
const providerInfo = getProviderFromContext(ctx);
|
|
16009
|
+
if (!providerInfo) {
|
|
16010
|
+
return { message: "\u274C Security scan requires an active LLM provider. No provider configured." };
|
|
16011
|
+
}
|
|
16012
|
+
const result = await defaultOrchestrator.run(providerInfo, {
|
|
15379
16013
|
projectRoot,
|
|
15380
16014
|
scanOptions: {
|
|
15381
16015
|
depth: options.depth || "standard",
|
|
@@ -15424,7 +16058,11 @@ async function handleScan(args, ctx) {
|
|
|
15424
16058
|
async function handleAudit(ctx) {
|
|
15425
16059
|
const projectRoot = ctx.projectRoot || ctx.cwd || process.cwd();
|
|
15426
16060
|
try {
|
|
15427
|
-
const
|
|
16061
|
+
const providerInfo = getProviderFromContext(ctx);
|
|
16062
|
+
if (!providerInfo) {
|
|
16063
|
+
return { message: "\u274C Security audit requires an active LLM provider. No provider configured." };
|
|
16064
|
+
}
|
|
16065
|
+
const result = await defaultOrchestrator.run(providerInfo, {
|
|
15428
16066
|
projectRoot,
|
|
15429
16067
|
reportOptions: { format: "markdown" }
|
|
15430
16068
|
});
|
|
@@ -15480,16 +16118,16 @@ Use \`/security report <number>\` to view a specific report.` };
|
|
|
15480
16118
|
}
|
|
15481
16119
|
const index = parseInt(reportId, 10) - 1;
|
|
15482
16120
|
if (!isNaN(index) && reports[index]) {
|
|
15483
|
-
const { readFile:
|
|
15484
|
-
const content = await
|
|
16121
|
+
const { readFile: readFile29 } = await import('fs/promises');
|
|
16122
|
+
const content = await readFile29(join(reportsDir, reports[index]), "utf-8");
|
|
15485
16123
|
return { message: `# Security Report
|
|
15486
16124
|
|
|
15487
16125
|
${content}` };
|
|
15488
16126
|
}
|
|
15489
16127
|
const match = reports.find((r) => r.includes(reportId));
|
|
15490
16128
|
if (match) {
|
|
15491
|
-
const { readFile:
|
|
15492
|
-
const content = await
|
|
16129
|
+
const { readFile: readFile29 } = await import('fs/promises');
|
|
16130
|
+
const content = await readFile29(join(reportsDir, match), "utf-8");
|
|
15493
16131
|
return { message: `# Security Report
|
|
15494
16132
|
|
|
15495
16133
|
${content}` };
|
|
@@ -15540,6 +16178,211 @@ function getHelpMessage() {
|
|
|
15540
16178
|
}
|
|
15541
16179
|
var securitySlashCommand = createSecuritySlashCommand();
|
|
15542
16180
|
|
|
16181
|
+
// src/coordination/fleet-manager.ts
|
|
16182
|
+
init_atomic_write();
|
|
16183
|
+
var FleetManager = class {
|
|
16184
|
+
/** The fleet-wide event bus. */
|
|
16185
|
+
fleet;
|
|
16186
|
+
/** Usage rollup across all subagents. */
|
|
16187
|
+
usage;
|
|
16188
|
+
manifestPath;
|
|
16189
|
+
sessionsRoot;
|
|
16190
|
+
directorRunId;
|
|
16191
|
+
/** Spawn cap (lifetime total). Infinity means unlimited. */
|
|
16192
|
+
maxSpawns;
|
|
16193
|
+
/** Nesting cap. */
|
|
16194
|
+
maxSpawnDepth;
|
|
16195
|
+
/** This director's depth in a director chain. Root = 0. */
|
|
16196
|
+
spawnDepth;
|
|
16197
|
+
/** Live spawn counter. */
|
|
16198
|
+
spawnCount = 0;
|
|
16199
|
+
stateCheckpoint;
|
|
16200
|
+
sessionWriter;
|
|
16201
|
+
manifestTimer = null;
|
|
16202
|
+
manifestDebounceMs;
|
|
16203
|
+
/** Fleet-wide cost cap. Infinity = no cap. Distinct from SubagentBudget limits,
|
|
16204
|
+
* which track per-subagent spend — this field caps the entire fleet total. */
|
|
16205
|
+
maxFleetCostUsd;
|
|
16206
|
+
manifestEntries = /* @__PURE__ */ new Map();
|
|
16207
|
+
/** Pending tasks with their descriptions — populated by `addPendingTask`
|
|
16208
|
+
* and cleared by `removePendingTask`. Replaces the host-side `pending`
|
|
16209
|
+
* Map so task descriptions live in one place (FleetManager). */
|
|
16210
|
+
pendingTasks = /* @__PURE__ */ new Map();
|
|
16211
|
+
subagentMeta = /* @__PURE__ */ new Map();
|
|
16212
|
+
priceLookups = /* @__PURE__ */ new Map();
|
|
16213
|
+
constructor(opts = {}) {
|
|
16214
|
+
this.manifestPath = opts.manifestPath;
|
|
16215
|
+
this.sessionsRoot = opts.sessionsRoot;
|
|
16216
|
+
this.directorRunId = opts.directorRunId ?? randomUUID();
|
|
16217
|
+
this.maxSpawns = opts.maxSpawns ?? Number.POSITIVE_INFINITY;
|
|
16218
|
+
this.maxSpawnDepth = opts.maxSpawnDepth ?? 2;
|
|
16219
|
+
this.spawnDepth = opts.spawnDepth ?? 0;
|
|
16220
|
+
this.sessionWriter = opts.sessionWriter ?? null;
|
|
16221
|
+
this.manifestDebounceMs = opts.manifestDebounceMs ?? 2e3;
|
|
16222
|
+
this.maxFleetCostUsd = opts.directorBudget?.maxCostUsd ?? Number.POSITIVE_INFINITY;
|
|
16223
|
+
this.stateCheckpoint = opts.stateCheckpointPath ? new DirectorStateCheckpoint(
|
|
16224
|
+
opts.stateCheckpointPath,
|
|
16225
|
+
{
|
|
16226
|
+
directorRunId: this.directorRunId,
|
|
16227
|
+
maxSpawns: opts.maxSpawns,
|
|
16228
|
+
spawnDepth: this.spawnDepth,
|
|
16229
|
+
maxSpawnDepth: this.maxSpawnDepth,
|
|
16230
|
+
directorBudget: opts.directorBudget
|
|
16231
|
+
},
|
|
16232
|
+
opts.checkpointDebounceMs ?? 250
|
|
16233
|
+
) : null;
|
|
16234
|
+
this.fleet = new FleetBus();
|
|
16235
|
+
this.usage = new FleetUsageAggregator(
|
|
16236
|
+
this.fleet,
|
|
16237
|
+
(id) => this.priceLookups.get(id),
|
|
16238
|
+
(id) => this.subagentMeta.get(id)
|
|
16239
|
+
);
|
|
16240
|
+
}
|
|
16241
|
+
// -----------------------------------------------------------------------
|
|
16242
|
+
// IFleetManager surface
|
|
16243
|
+
// -----------------------------------------------------------------------
|
|
16244
|
+
get fleetBus() {
|
|
16245
|
+
return this.fleet;
|
|
16246
|
+
}
|
|
16247
|
+
snapshot() {
|
|
16248
|
+
return this.usage.snapshot();
|
|
16249
|
+
}
|
|
16250
|
+
getSubagentMeta(id) {
|
|
16251
|
+
return this.subagentMeta.get(id);
|
|
16252
|
+
}
|
|
16253
|
+
/**
|
|
16254
|
+
* Returns null if the spawn is allowed, or an object describing
|
|
16255
|
+
* which cap was exceeded. Does NOT throw — the caller decides
|
|
16256
|
+
* how to surface the rejection.
|
|
16257
|
+
*/
|
|
16258
|
+
canSpawn(config) {
|
|
16259
|
+
if (this.spawnDepth >= this.maxSpawnDepth) {
|
|
16260
|
+
return { kind: "max_spawn_depth", limit: this.maxSpawnDepth, observed: this.spawnDepth };
|
|
16261
|
+
}
|
|
16262
|
+
if (this.spawnCount >= this.maxSpawns) {
|
|
16263
|
+
return { kind: "max_spawns", limit: this.maxSpawns, observed: this.spawnCount + 1 };
|
|
16264
|
+
}
|
|
16265
|
+
if (this.maxFleetCostUsd < Number.POSITIVE_INFINITY) {
|
|
16266
|
+
const totalCost = this.usage.snapshot().total?.cost ?? 0;
|
|
16267
|
+
if (totalCost >= this.maxFleetCostUsd) {
|
|
16268
|
+
return { kind: "max_cost_usd", limit: this.maxFleetCostUsd, observed: totalCost };
|
|
16269
|
+
}
|
|
16270
|
+
}
|
|
16271
|
+
return null;
|
|
16272
|
+
}
|
|
16273
|
+
/**
|
|
16274
|
+
* Records a spawn: increments counter, stores metadata, updates state checkpoint,
|
|
16275
|
+
* and schedules a debounced manifest write. Call AFTER the coordinator
|
|
16276
|
+
* has successfully spawned the subagent.
|
|
16277
|
+
*
|
|
16278
|
+
* @param subagentId The subagent's id (from coordinator.spawn result)
|
|
16279
|
+
* @param config The SubagentConfig that was used
|
|
16280
|
+
* @param priceLookup Optional per-subagent pricing data
|
|
16281
|
+
*/
|
|
16282
|
+
recordSpawn(subagentId, config, priceLookup) {
|
|
16283
|
+
this.spawnCount += 1;
|
|
16284
|
+
this.subagentMeta.set(subagentId, {
|
|
16285
|
+
provider: config.provider,
|
|
16286
|
+
model: config.model
|
|
16287
|
+
});
|
|
16288
|
+
if (priceLookup) this.priceLookups.set(subagentId, priceLookup);
|
|
16289
|
+
this.manifestEntries.set(subagentId, {
|
|
16290
|
+
subagentId,
|
|
16291
|
+
name: config.name,
|
|
16292
|
+
role: config.role,
|
|
16293
|
+
provider: config.provider,
|
|
16294
|
+
model: config.model,
|
|
16295
|
+
taskIds: []
|
|
16296
|
+
});
|
|
16297
|
+
this.stateCheckpoint?.recordSpawn({
|
|
16298
|
+
id: subagentId,
|
|
16299
|
+
name: config.name,
|
|
16300
|
+
role: config.role,
|
|
16301
|
+
provider: config.provider,
|
|
16302
|
+
model: config.model,
|
|
16303
|
+
spawnedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
16304
|
+
}, this.spawnCount);
|
|
16305
|
+
void this.appendSessionEvent({
|
|
16306
|
+
type: "agent_spawned",
|
|
16307
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16308
|
+
agentId: subagentId,
|
|
16309
|
+
role: config.role ?? config.name
|
|
16310
|
+
});
|
|
16311
|
+
this.scheduleManifest();
|
|
16312
|
+
}
|
|
16313
|
+
async writeManifest() {
|
|
16314
|
+
if (!this.manifestPath) return null;
|
|
16315
|
+
const manifest = {
|
|
16316
|
+
version: 1,
|
|
16317
|
+
directorRunId: this.directorRunId,
|
|
16318
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16319
|
+
children: Array.from(this.manifestEntries.values()).map((entry) => ({
|
|
16320
|
+
id: entry.subagentId,
|
|
16321
|
+
name: entry.name,
|
|
16322
|
+
role: entry.role,
|
|
16323
|
+
provider: entry.provider,
|
|
16324
|
+
model: entry.model,
|
|
16325
|
+
taskIds: entry.taskIds
|
|
16326
|
+
})),
|
|
16327
|
+
usage: this.usage.snapshot()
|
|
16328
|
+
};
|
|
16329
|
+
await fsp2.mkdir(path6.dirname(this.manifestPath), { recursive: true });
|
|
16330
|
+
await atomicWrite(this.manifestPath, JSON.stringify(manifest, null, 2), { mode: 384 });
|
|
16331
|
+
return this.manifestPath;
|
|
16332
|
+
}
|
|
16333
|
+
/**
|
|
16334
|
+
* Attach task ids to an already-spawned subagent. Called by
|
|
16335
|
+
* `Director.assign()` after the coordinator assigns a task.
|
|
16336
|
+
*/
|
|
16337
|
+
addTaskToSubagent(subagentId, taskId) {
|
|
16338
|
+
const entry = this.manifestEntries.get(subagentId);
|
|
16339
|
+
if (entry) entry.taskIds.push(taskId);
|
|
16340
|
+
}
|
|
16341
|
+
/**
|
|
16342
|
+
* Debounced manifest write. Call after any state mutation
|
|
16343
|
+
* (spawn, assign, complete) so a burst collapses into one write.
|
|
16344
|
+
*/
|
|
16345
|
+
scheduleManifest() {
|
|
16346
|
+
if (!this.manifestPath || this.manifestDebounceMs <= 0) return;
|
|
16347
|
+
if (this.manifestTimer) return;
|
|
16348
|
+
this.manifestTimer = setTimeout(() => {
|
|
16349
|
+
this.manifestTimer = null;
|
|
16350
|
+
void this.writeManifest().catch((err) => {
|
|
16351
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
16352
|
+
process.emitWarning(
|
|
16353
|
+
`FleetManager manifest write failed: ${detail}`,
|
|
16354
|
+
"FleetManagerWarning"
|
|
16355
|
+
);
|
|
16356
|
+
});
|
|
16357
|
+
}, this.manifestDebounceMs);
|
|
16358
|
+
}
|
|
16359
|
+
/** Best-effort session event writer. Swallows failures. */
|
|
16360
|
+
async appendSessionEvent(event) {
|
|
16361
|
+
if (!this.sessionWriter) return;
|
|
16362
|
+
try {
|
|
16363
|
+
await this.sessionWriter.append(event);
|
|
16364
|
+
} catch {
|
|
16365
|
+
}
|
|
16366
|
+
}
|
|
16367
|
+
// -----------------------------------------------------------------------
|
|
16368
|
+
// Pending task management — eliminates host-side state duplication
|
|
16369
|
+
// -----------------------------------------------------------------------
|
|
16370
|
+
addPendingTask(taskId, subagentId, description) {
|
|
16371
|
+
this.pendingTasks.set(taskId, { subagentId, description });
|
|
16372
|
+
}
|
|
16373
|
+
removePendingTask(taskId) {
|
|
16374
|
+
this.pendingTasks.delete(taskId);
|
|
16375
|
+
}
|
|
16376
|
+
getFleetStatus() {
|
|
16377
|
+
const pending = Array.from(this.pendingTasks.entries()).map(([taskId, v]) => ({
|
|
16378
|
+
taskId,
|
|
16379
|
+
description: v.description,
|
|
16380
|
+
subagentId: v.subagentId
|
|
16381
|
+
}));
|
|
16382
|
+
return { pending, live: [] };
|
|
16383
|
+
}
|
|
16384
|
+
};
|
|
16385
|
+
|
|
15543
16386
|
// src/extension/registry.ts
|
|
15544
16387
|
var ExtensionRegistry = class {
|
|
15545
16388
|
extensions = [];
|
|
@@ -15568,7 +16411,8 @@ var ExtensionRegistry = class {
|
|
|
15568
16411
|
*/
|
|
15569
16412
|
async buildSystemPromptContributions(ctx) {
|
|
15570
16413
|
const blocks = [];
|
|
15571
|
-
|
|
16414
|
+
const snapshot = [...this.promptContributors];
|
|
16415
|
+
for (const c of snapshot) {
|
|
15572
16416
|
try {
|
|
15573
16417
|
const contributed = await c(ctx);
|
|
15574
16418
|
blocks.push(...contributed);
|
|
@@ -15748,6 +16592,50 @@ var ExtensionRegistry = class {
|
|
|
15748
16592
|
}
|
|
15749
16593
|
};
|
|
15750
16594
|
|
|
16595
|
+
// src/core/continue-to-next-iteration.ts
|
|
16596
|
+
function parseContinueDirective(text) {
|
|
16597
|
+
const LINE_MARKERS = /^\s*\[(continue|next step|proceed|done)\]\s*$/gim;
|
|
16598
|
+
let match;
|
|
16599
|
+
let lastDirective = "none";
|
|
16600
|
+
while ((match = LINE_MARKERS.exec(text)) !== null) {
|
|
16601
|
+
const value = (match[1] ?? "").toLowerCase();
|
|
16602
|
+
if (value === "continue" || value === "next step" || value === "proceed") {
|
|
16603
|
+
lastDirective = "continue";
|
|
16604
|
+
} else if (value === "done") {
|
|
16605
|
+
lastDirective = "stop";
|
|
16606
|
+
}
|
|
16607
|
+
}
|
|
16608
|
+
return lastDirective;
|
|
16609
|
+
}
|
|
16610
|
+
var META_KEY = "_autonomousContinue";
|
|
16611
|
+
function setAutonomousContinue(ctx) {
|
|
16612
|
+
ctx.meta[META_KEY] = true;
|
|
16613
|
+
}
|
|
16614
|
+
function consumeAutonomousContinue(ctx) {
|
|
16615
|
+
const val = ctx.meta[META_KEY] === true;
|
|
16616
|
+
delete ctx.meta[META_KEY];
|
|
16617
|
+
return val;
|
|
16618
|
+
}
|
|
16619
|
+
function makeContinueToNextIterationTool() {
|
|
16620
|
+
const inputSchema = {
|
|
16621
|
+
type: "object",
|
|
16622
|
+
properties: {},
|
|
16623
|
+
required: [],
|
|
16624
|
+
description: "Signal that the agent should continue to the next iteration immediately, without waiting for user input. Use this when you have completed a step and want to proceed automatically to the next step in your plan."
|
|
16625
|
+
};
|
|
16626
|
+
return {
|
|
16627
|
+
name: "continue_to_next_iteration",
|
|
16628
|
+
description: "Continue to the next iteration without returning to the user. Call this when you have finished a step and want to keep working autonomously.",
|
|
16629
|
+
permission: "auto",
|
|
16630
|
+
mutating: false,
|
|
16631
|
+
inputSchema,
|
|
16632
|
+
async execute(_input, ctx) {
|
|
16633
|
+
setAutonomousContinue(ctx);
|
|
16634
|
+
return { continue: true };
|
|
16635
|
+
}
|
|
16636
|
+
};
|
|
16637
|
+
}
|
|
16638
|
+
|
|
15751
16639
|
// src/core/iteration-limit.ts
|
|
15752
16640
|
function requestLimitExtension(opts) {
|
|
15753
16641
|
const { events, currentIterations, currentLimit, autoExtend, timeoutMs = 3e4 } = opts;
|
|
@@ -15826,6 +16714,8 @@ var Agent = class {
|
|
|
15826
16714
|
plugins = [];
|
|
15827
16715
|
toolExecutor;
|
|
15828
16716
|
autoExtendLimit;
|
|
16717
|
+
/** Enables autonomous continue: model can signal `[continue]` or call continue_to_next_iteration() to re-run. */
|
|
16718
|
+
autonomousContinue;
|
|
15829
16719
|
tracer;
|
|
15830
16720
|
extensions;
|
|
15831
16721
|
constructor(init) {
|
|
@@ -15840,19 +16730,11 @@ var Agent = class {
|
|
|
15840
16730
|
this.executionStrategy = init.executionStrategy ?? "smart";
|
|
15841
16731
|
this.perIterationOutputCapBytes = init.perIterationOutputCapBytes ?? 1e5;
|
|
15842
16732
|
this.autoExtendLimit = init.autoExtendLimit ?? true;
|
|
16733
|
+
this.autonomousContinue = init.autonomousContinue ?? false;
|
|
15843
16734
|
this.tracer = init.tracer;
|
|
15844
16735
|
this.extensions = init.extensions ?? new ExtensionRegistry();
|
|
15845
16736
|
this.extensions.setLogger(this.container.resolve(TOKENS.Logger));
|
|
15846
|
-
this.toolExecutor =
|
|
15847
|
-
permissionPolicy: init.permissionPolicy ?? this.permission,
|
|
15848
|
-
secretScrubber: this.scrubber,
|
|
15849
|
-
renderer: this.renderer,
|
|
15850
|
-
events: this.events,
|
|
15851
|
-
confirmAwaiter: init.confirmAwaiter,
|
|
15852
|
-
iterationTimeoutMs: this.iterationTimeoutMs,
|
|
15853
|
-
perIterationOutputCapBytes: this.perIterationOutputCapBytes,
|
|
15854
|
-
tracer: this.tracer
|
|
15855
|
-
});
|
|
16737
|
+
this.toolExecutor = init.toolExecutor;
|
|
15856
16738
|
}
|
|
15857
16739
|
get logger() {
|
|
15858
16740
|
return this.container.resolve(TOKENS.Logger);
|
|
@@ -15988,6 +16870,9 @@ var Agent = class {
|
|
|
15988
16870
|
if (controller.signal.aborted) {
|
|
15989
16871
|
return { status: "aborted", iterations };
|
|
15990
16872
|
}
|
|
16873
|
+
if (this.autonomousContinue) {
|
|
16874
|
+
consumeAutonomousContinue(this.ctx);
|
|
16875
|
+
}
|
|
15991
16876
|
const limitCheck = await this.checkIterationLimit(
|
|
15992
16877
|
i,
|
|
15993
16878
|
effectiveLimit,
|
|
@@ -16064,12 +16949,32 @@ var Agent = class {
|
|
|
16064
16949
|
const toolUses = res.content.filter(isToolUseBlock);
|
|
16065
16950
|
if (toolUses.length === 0) {
|
|
16066
16951
|
this.events.emit("iteration.completed", { ctx: this.ctx, index: i });
|
|
16952
|
+
if (this.autonomousContinue && responseResult.directive === "continue") {
|
|
16953
|
+
await this.compactContextIfNeeded();
|
|
16954
|
+
await this.extensions.runAfterIteration(this.ctx, i);
|
|
16955
|
+
continue;
|
|
16956
|
+
}
|
|
16957
|
+
if (this.autonomousContinue && responseResult.directive === "stop") {
|
|
16958
|
+
return { status: "done", iterations, finalText };
|
|
16959
|
+
}
|
|
16067
16960
|
return { status: "done", iterations, finalText };
|
|
16068
16961
|
}
|
|
16069
16962
|
await this.executeTools(toolUses);
|
|
16963
|
+
if (this.autonomousContinue && consumeAutonomousContinue(this.ctx)) {
|
|
16964
|
+
this.events.emit("iteration.completed", { ctx: this.ctx, index: i });
|
|
16965
|
+
await this.compactContextIfNeeded();
|
|
16966
|
+
await this.extensions.runAfterIteration(this.ctx, i);
|
|
16967
|
+
continue;
|
|
16968
|
+
}
|
|
16070
16969
|
this.events.emit("iteration.completed", { ctx: this.ctx, index: i });
|
|
16071
16970
|
await this.compactContextIfNeeded();
|
|
16072
16971
|
await this.extensions.runAfterIteration(this.ctx, i);
|
|
16972
|
+
if (this.autonomousContinue && responseResult.directive === "continue") {
|
|
16973
|
+
continue;
|
|
16974
|
+
}
|
|
16975
|
+
if (this.autonomousContinue && responseResult.directive === "stop") {
|
|
16976
|
+
return { status: "done", iterations, finalText };
|
|
16977
|
+
}
|
|
16073
16978
|
}
|
|
16074
16979
|
}
|
|
16075
16980
|
/**
|
|
@@ -16156,7 +17061,11 @@ var Agent = class {
|
|
|
16156
17061
|
if (!streamed) this.renderer?.write(rendered);
|
|
16157
17062
|
}
|
|
16158
17063
|
}
|
|
16159
|
-
|
|
17064
|
+
let directive = "none";
|
|
17065
|
+
if (this.autonomousContinue && finalText) {
|
|
17066
|
+
directive = parseContinueDirective(finalText);
|
|
17067
|
+
}
|
|
17068
|
+
return { finalText, aborted: false, done: false, directive };
|
|
16160
17069
|
}
|
|
16161
17070
|
/**
|
|
16162
17071
|
* Execute tools and append tool results to context.
|
|
@@ -17749,6 +18658,6 @@ function wrapApiForCapabilityCheck(plugin, api, log, enforce = false) {
|
|
|
17749
18658
|
});
|
|
17750
18659
|
}
|
|
17751
18660
|
|
|
17752
|
-
export { AISpecBuilder, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, Agent, AgentError, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, CONTEXT_WINDOW_MODES, ConfigError, ConfigMigrationError, Container, Context, ConversationState, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_MAX_ITERATIONS, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director,
|
|
18661
|
+
export { AISpecBuilder, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, Agent, AgentError, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, CONTEXT_WINDOW_MODES, ConfigError, ConfigMigrationError, Container, Context, ConversationState, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_MAX_ITERATIONS, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director, DirectorStateCheckpoint, DoneConditionChecker, EternalAutonomyEngine, EventBus, ExtensionRegistry, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FleetBus, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, GitignoreUpdater, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, LAYER_1_IDENTITY, LLMSelector, MAX_JOURNAL_ENTRIES, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, Pipeline, PluginError, ProviderError, ProviderRegistry, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, ReportGenerator, RunController, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, ScopedEventBus, SecurityScanner, SecurityScannerOrchestrator, SelectiveCompactor, SessionAnalyzer, SessionError, SkillGenerator, SkillInstaller, SkillManifestStore, SlashCommandRegistry, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TOKENS, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, TechStackDetector, ToolError, ToolExecutor, ToolRegistry, WrongStackError, addPlanItem, allServers, analyzeCriticalPath, appendJournal, applyRosterBudget, asBlocks, asText, atomicWrite, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildChildEnv, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildRecoveryStrategies, classifyFamily, clearPlan, color, compileGlob, compileUserRegex, composeDirectorPrompt, composeSubagentPrompt, computeTaskProgress, context7Server, contextManagerTool, createAutoExecutor, createContextManagerTool, createDefaultPipelines, createDelegateTool, createMessage, createSecuritySlashCommand, createToolOutputSerializer, decryptConfigSecrets, defaultGitignoreUpdater, defaultOrchestrator, defaultReportGenerator, defaultSecurityScanner, defaultSkillGenerator, defaultTechStackDetector, deriveTodosFromPlanItem, detectNewlineStyle, downloadGitHubTarball, emptyGoal, emptyPlan, encryptConfigSecrets, ensureDir, estimateRequestTokens, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, extractRunEnv, filesystemServer, findCriticalPath, formatContextWindowModeList, formatGoal, formatPlan, formatPlanTemplates, formatTodosList, getContextWindowMode, getPlanTemplate, getTemplate, githubServer, goalFilePath, googleMapsServer, isAgentError, isConfigError, isContextWindowModeId, isImageBlock, isPluginError, isSessionError, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isWrongStackError, listContextWindowModes, listPlanTemplates, listTemplates, loadDirectorState, loadGoal, loadPlan, loadPlugins, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeContinueToNextIterationTool, makeDirectorSessionFactory, matchAny, matchGlob, migratePlaintextSecrets, miniMaxVisionServer, normalizeToLf, parseContinueDirective, parseSkillRef, projectHash, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, repairToolUseAdjacency, resolveContextWindowPolicy, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, safeParse, safeStringify, sanitizeJsonString, saveGoal, savePlan, saveTodosCheckpoint, securitySlashCommand, sentinelServer, setPlanItemStatus, slackServer, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, stripAnsi, summarizeUsage, templateToMarkdown, toStyle, toWrongStackError, topologicalSort, unifiedDiff, unloadPlugins, validateAgainstSchema, wireMetricsToEvents, wrapAsState, zaiVisionServer };
|
|
17753
18662
|
//# sourceMappingURL=index.js.map
|
|
17754
18663
|
//# sourceMappingURL=index.js.map
|