@wrongstack/core 0.119.1 → 0.141.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-PRAbEX-9.d.ts → agent-bridge-r9y6gdn4.d.ts} +1 -1
- package/dist/{agent-subagent-runner-DXWImnNV.d.ts → agent-subagent-runner-1GeQE_L0.d.ts} +7 -7
- package/dist/{brain-NZZqL8sn.d.ts → brain-Cp_3GIS2.d.ts} +14 -3
- package/dist/{compactor-C3stu664.d.ts → compactor-BueGt7LG.d.ts} +9 -1
- package/dist/{config-DZV9VK9n.d.ts → config-BaVThgnT.d.ts} +1 -1
- package/dist/{context-hvXINa6a.d.ts → context-C7G_MtLV.d.ts} +7 -0
- package/dist/coordination/index.d.ts +12 -12
- package/dist/coordination/index.js +31 -17
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +24 -24
- package/dist/defaults/index.js +638 -681
- package/dist/defaults/index.js.map +1 -1
- package/dist/execution/index.d.ts +14 -14
- package/dist/execution/index.js +326 -317
- package/dist/execution/index.js.map +1 -1
- package/dist/extension/index.d.ts +6 -6
- package/dist/{goal-preamble-COpt55W8.d.ts → goal-preamble-iuIUTQwk.d.ts} +47 -17
- package/dist/{index-Cj3le7TI.d.ts → index-BZdezm3g.d.ts} +8 -8
- package/dist/{index-4VVHuTZF.d.ts → index-CPweVoFM.d.ts} +5 -5
- package/dist/index.d.ts +47 -47
- package/dist/index.js +455 -503
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +6 -6
- package/dist/infrastructure/index.js +64 -19
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/kernel/index.d.ts +9 -9
- package/dist/kernel/index.js.map +1 -1
- package/dist/{llm-selector-Bmx4PsVM.d.ts → llm-selector-CP72f1lC.d.ts} +2 -2
- package/dist/{mcp-servers-CofNHOqn.d.ts → mcp-servers-Bl5LTvQg.d.ts} +3 -3
- package/dist/models/index.d.ts +5 -5
- package/dist/models/index.js +53 -20
- package/dist/models/index.js.map +1 -1
- package/dist/{models-registry-BzlsjKcy.d.ts → models-registry-D90K9UnM.d.ts} +1 -1
- package/dist/{multi-agent-coordinator-CmDmUgq2.d.ts → multi-agent-coordinator-QWEzJDlm.d.ts} +1 -1
- package/dist/{null-fleet-bus-CyH2YqP4.d.ts → null-fleet-bus-BUyfqh23.d.ts} +13 -10
- package/dist/observability/index.d.ts +2 -2
- package/dist/{parallel-eternal-engine-uxVm4y4V.d.ts → parallel-eternal-engine-Dj2SYzha.d.ts} +24 -11
- package/dist/{path-resolver-gfoGxpdd.d.ts → path-resolver-DRjQBkoO.d.ts} +3 -3
- package/dist/{permission-DhLrgA30.d.ts → permission-B7nKnEvQ.d.ts} +1 -1
- package/dist/{permission-policy-DGOvne7i.d.ts → permission-policy-8-6zBmfA.d.ts} +2 -13
- package/dist/{pipeline-CqF7uju2.d.ts → pipeline-BG7UgbDc.d.ts} +2 -2
- package/dist/{plan-templates-DoKhzenv.d.ts → plan-templates-CkKNPU3I.d.ts} +5 -5
- package/dist/{provider-runner-BPlI5sPZ.d.ts → provider-runner-BNpuIyOL.d.ts} +3 -3
- package/dist/{retry-policy-B4Q-FZgE.d.ts → retry-policy-rutAfVeR.d.ts} +1 -1
- package/dist/sdd/index.d.ts +8 -8
- package/dist/sdd/index.js +10 -1
- package/dist/sdd/index.js.map +1 -1
- package/dist/{secret-vault-B7GnL6o9.d.ts → secret-vault-BTcC_T5v.d.ts} +3 -2
- package/dist/security/index.d.ts +5 -7
- package/dist/security/index.js +4 -35
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-BakeMnWR.d.ts → selector-4vDFZKt3.d.ts} +1 -1
- package/dist/{session-event-bridge-DE5Oekwx.d.ts → session-event-bridge-DWlvglC2.d.ts} +2 -2
- package/dist/{session-reader-DFC81-4p.d.ts → session-reader-BAtCxdaw.d.ts} +1 -1
- package/dist/storage/index.d.ts +13 -13
- package/dist/storage/index.js +98 -101
- package/dist/storage/index.js.map +1 -1
- package/dist/types/index.d.ts +18 -18
- package/dist/types/index.js +152 -102
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +37 -13
- package/dist/utils/index.js +60 -32
- package/dist/utils/index.js.map +1 -1
- package/package.json +2 -2
package/dist/defaults/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as crypto2 from 'crypto';
|
|
2
|
-
import { randomBytes,
|
|
2
|
+
import { randomBytes, createCipheriv, createDecipheriv, randomUUID, createHash } from 'crypto';
|
|
3
3
|
import * as fsp from 'fs/promises';
|
|
4
4
|
import * as path11 from 'path';
|
|
5
5
|
import { isAbsolute, resolve } from 'path';
|
|
6
|
-
import * as
|
|
6
|
+
import * as fs4 from 'fs';
|
|
7
7
|
import * as os from 'os';
|
|
8
8
|
import { hostname } from 'os';
|
|
9
9
|
import { execFile } from 'child_process';
|
|
@@ -218,7 +218,7 @@ var DefaultLogger = class _DefaultLogger {
|
|
|
218
218
|
this.stderr = opts.stderr !== false;
|
|
219
219
|
if (this.file) {
|
|
220
220
|
try {
|
|
221
|
-
|
|
221
|
+
fs4.mkdirSync(path11.dirname(this.file), { recursive: true });
|
|
222
222
|
} catch {
|
|
223
223
|
}
|
|
224
224
|
}
|
|
@@ -258,7 +258,7 @@ var DefaultLogger = class _DefaultLogger {
|
|
|
258
258
|
}
|
|
259
259
|
if (this.file) {
|
|
260
260
|
try {
|
|
261
|
-
|
|
261
|
+
fs4.appendFileSync(this.file, `${JSON.stringify(entry)}
|
|
262
262
|
`);
|
|
263
263
|
} catch {
|
|
264
264
|
}
|
|
@@ -452,8 +452,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
452
452
|
onClose: (s) => this.appendToIndex(s)
|
|
453
453
|
});
|
|
454
454
|
} catch (err) {
|
|
455
|
-
await handle.close().catch(() => {
|
|
456
|
-
});
|
|
455
|
+
await handle.close().catch((e) => console.warn(`[session-store] handle.close() failed: ${e}`));
|
|
457
456
|
throw err;
|
|
458
457
|
}
|
|
459
458
|
}
|
|
@@ -484,8 +483,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
484
483
|
);
|
|
485
484
|
return { writer, data };
|
|
486
485
|
} catch (err) {
|
|
487
|
-
await handle.close().catch(() => {
|
|
488
|
-
});
|
|
486
|
+
await handle.close().catch((e) => console.warn(`[session-store] handle.close() failed: ${e}`));
|
|
489
487
|
throw err;
|
|
490
488
|
}
|
|
491
489
|
}
|
|
@@ -674,15 +672,15 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
674
672
|
* sidecars, and the session directory (fleet.json, shared/, subagents/).
|
|
675
673
|
*/
|
|
676
674
|
async deleteSession(id) {
|
|
677
|
-
await fsp.unlink(this.sessionPath(id, ".jsonl")).catch(() =>
|
|
678
|
-
await fsp.unlink(this.sessionPath(id, ".summary.json")).catch(() =>
|
|
675
|
+
await fsp.unlink(this.sessionPath(id, ".jsonl")).catch((err) => console.warn(`[session-store] delete .jsonl failed: ${err}`));
|
|
676
|
+
await fsp.unlink(this.sessionPath(id, ".summary.json")).catch((err) => console.warn(`[session-store] delete .summary.json failed: ${err}`));
|
|
679
677
|
const shardDir = path11.dirname(path11.join(this.dir, id));
|
|
680
678
|
const base = path11.basename(id);
|
|
681
679
|
for (const ext of [".plan.json", ".todos.json"]) {
|
|
682
|
-
await fsp.unlink(path11.join(shardDir, `${base}${ext}`)).catch(() =>
|
|
680
|
+
await fsp.unlink(path11.join(shardDir, `${base}${ext}`)).catch((err) => console.warn(`[session-store] delete ${ext} failed: ${err}`));
|
|
683
681
|
}
|
|
684
682
|
const sessDir = path11.join(shardDir, base);
|
|
685
|
-
await fsp.rm(sessDir, { recursive: true, force: true }).catch(() =>
|
|
683
|
+
await fsp.rm(sessDir, { recursive: true, force: true }).catch((err) => console.warn(`[session-store] delete session dir failed: ${err}`));
|
|
686
684
|
await this.writeTombstone(id);
|
|
687
685
|
}
|
|
688
686
|
async delete(id) {
|
|
@@ -1985,20 +1983,101 @@ function deepFreeze(obj) {
|
|
|
1985
1983
|
return Object.freeze(obj);
|
|
1986
1984
|
}
|
|
1987
1985
|
|
|
1988
|
-
// src/
|
|
1989
|
-
|
|
1986
|
+
// src/types/secret-vault.ts
|
|
1987
|
+
var ENCRYPTED_PREFIX = "enc:v1:";
|
|
1988
|
+
|
|
1989
|
+
// src/security/secret-vault.ts
|
|
1990
|
+
init_atomic_write();
|
|
1991
|
+
var KEY_BYTES = 32;
|
|
1992
|
+
var IV_BYTES = 12;
|
|
1993
|
+
var TAG_BYTES = 16;
|
|
1994
|
+
var ALGO = "aes-256-gcm";
|
|
1995
|
+
var DefaultSecretVault = class {
|
|
1996
|
+
keyFile;
|
|
1997
|
+
key;
|
|
1998
|
+
constructor(opts) {
|
|
1999
|
+
this.keyFile = opts.keyFile;
|
|
2000
|
+
}
|
|
2001
|
+
isEncrypted(value) {
|
|
2002
|
+
return typeof value === "string" && value.startsWith(ENCRYPTED_PREFIX);
|
|
2003
|
+
}
|
|
2004
|
+
encrypt(plaintext) {
|
|
2005
|
+
if (this.isEncrypted(plaintext)) return plaintext;
|
|
2006
|
+
const key = this.loadOrCreateKey();
|
|
2007
|
+
const iv = randomBytes(IV_BYTES);
|
|
2008
|
+
const cipher = createCipheriv(ALGO, key, iv);
|
|
2009
|
+
const ct = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
|
|
2010
|
+
const tag = cipher.getAuthTag();
|
|
2011
|
+
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${tag.toString("base64")}:${ct.toString("base64")}`;
|
|
2012
|
+
}
|
|
2013
|
+
decrypt(value) {
|
|
2014
|
+
if (!this.isEncrypted(value)) return value;
|
|
2015
|
+
const rest = value.slice(ENCRYPTED_PREFIX.length);
|
|
2016
|
+
const parts = rest.split(":");
|
|
2017
|
+
if (parts.length !== 3) {
|
|
2018
|
+
throw new Error("SecretVault: malformed encrypted value");
|
|
2019
|
+
}
|
|
2020
|
+
const [ivB64, tagB64, ctB64] = parts;
|
|
2021
|
+
const iv = Buffer.from(ivB64, "base64");
|
|
2022
|
+
const tag = Buffer.from(tagB64, "base64");
|
|
2023
|
+
const ct = Buffer.from(ctB64, "base64");
|
|
2024
|
+
if (iv.length !== IV_BYTES) throw new Error("SecretVault: bad IV length");
|
|
2025
|
+
if (tag.length !== TAG_BYTES) throw new Error("SecretVault: bad tag length");
|
|
2026
|
+
const key = this.loadOrCreateKey();
|
|
2027
|
+
const decipher = createDecipheriv(ALGO, key, iv);
|
|
2028
|
+
decipher.setAuthTag(tag);
|
|
2029
|
+
const pt = Buffer.concat([decipher.update(ct), decipher.final()]);
|
|
2030
|
+
return pt.toString("utf8");
|
|
2031
|
+
}
|
|
2032
|
+
loadOrCreateKey() {
|
|
2033
|
+
if (this.key) return this.key;
|
|
2034
|
+
try {
|
|
2035
|
+
const buf = fs4.readFileSync(this.keyFile);
|
|
2036
|
+
if (buf.length !== KEY_BYTES) {
|
|
2037
|
+
throw new Error(
|
|
2038
|
+
`SecretVault: key file ${this.keyFile} is ${buf.length} bytes (expected ${KEY_BYTES}). Remove it manually to generate a new key.`
|
|
2039
|
+
);
|
|
2040
|
+
}
|
|
2041
|
+
this.key = buf;
|
|
2042
|
+
return this.key;
|
|
2043
|
+
} catch (err) {
|
|
2044
|
+
if (err.code !== "ENOENT") throw err;
|
|
2045
|
+
}
|
|
2046
|
+
fs4.mkdirSync(path11.dirname(this.keyFile), { recursive: true });
|
|
2047
|
+
const key = randomBytes(KEY_BYTES);
|
|
2048
|
+
try {
|
|
2049
|
+
fs4.writeFileSync(this.keyFile, key, { mode: 384, flag: "wx" });
|
|
2050
|
+
} catch (err) {
|
|
2051
|
+
if (err.code !== "EEXIST") throw err;
|
|
2052
|
+
const buf = fs4.readFileSync(this.keyFile);
|
|
2053
|
+
if (buf.length !== KEY_BYTES) {
|
|
2054
|
+
throw new Error(
|
|
2055
|
+
`SecretVault: key file ${this.keyFile} is ${buf.length} bytes (expected ${KEY_BYTES}). Remove it manually to generate a new key.`
|
|
2056
|
+
);
|
|
2057
|
+
}
|
|
2058
|
+
this.key = buf;
|
|
2059
|
+
return this.key;
|
|
2060
|
+
}
|
|
2061
|
+
this.key = key;
|
|
2062
|
+
return key;
|
|
2063
|
+
}
|
|
2064
|
+
};
|
|
2065
|
+
function decryptConfigSecrets(cfg, vault, opts) {
|
|
2066
|
+
const warn = opts?.warn ?? ((msg) => console.warn(msg));
|
|
1990
2067
|
return walk(cfg, vault, (v, key) => {
|
|
1991
2068
|
try {
|
|
1992
2069
|
return vault.decrypt(v);
|
|
1993
2070
|
} catch (err) {
|
|
1994
|
-
|
|
1995
|
-
`[secret-vault] Failed to decrypt "${key}"
|
|
1996
|
-
err instanceof Error ? err.message : err
|
|
2071
|
+
warn(
|
|
2072
|
+
`[secret-vault] Failed to decrypt "${key}": ${err instanceof Error ? err.message : err}`
|
|
1997
2073
|
);
|
|
1998
2074
|
return "";
|
|
1999
2075
|
}
|
|
2000
2076
|
});
|
|
2001
2077
|
}
|
|
2078
|
+
function encryptConfigSecrets(cfg, vault, _opts) {
|
|
2079
|
+
return walk(cfg, vault, (v) => vault.encrypt(v));
|
|
2080
|
+
}
|
|
2002
2081
|
function walk(node, vault, transform) {
|
|
2003
2082
|
if (node === null || node === void 0) return node;
|
|
2004
2083
|
if (typeof node !== "object") return node;
|
|
@@ -2024,6 +2103,114 @@ function isSecretField(name) {
|
|
|
2024
2103
|
if (NON_SECRET_OVERRIDES.has(lc)) return false;
|
|
2025
2104
|
return SECRET_KEY_PATTERN.test(lc);
|
|
2026
2105
|
}
|
|
2106
|
+
async function rewriteConfigEncrypted(configPath, vault, patch) {
|
|
2107
|
+
let current = {};
|
|
2108
|
+
try {
|
|
2109
|
+
const raw = await fsp.readFile(configPath, "utf8");
|
|
2110
|
+
current = JSON.parse(raw);
|
|
2111
|
+
} catch {
|
|
2112
|
+
}
|
|
2113
|
+
const merged = deepMerge(current, patch ?? {});
|
|
2114
|
+
const encrypted = encryptConfigSecrets(merged, vault);
|
|
2115
|
+
await fsp.mkdir(path11.dirname(configPath), { recursive: true });
|
|
2116
|
+
await atomicWrite(configPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
2117
|
+
await restrictFilePermissions(configPath);
|
|
2118
|
+
}
|
|
2119
|
+
async function migratePlaintextSecrets(configPath, vault) {
|
|
2120
|
+
let raw;
|
|
2121
|
+
try {
|
|
2122
|
+
raw = await fsp.readFile(configPath, "utf8");
|
|
2123
|
+
} catch {
|
|
2124
|
+
return { migrated: 0, file: configPath };
|
|
2125
|
+
}
|
|
2126
|
+
let parsed;
|
|
2127
|
+
try {
|
|
2128
|
+
parsed = JSON.parse(raw);
|
|
2129
|
+
} catch {
|
|
2130
|
+
return { migrated: 0, file: configPath };
|
|
2131
|
+
}
|
|
2132
|
+
const counter = { n: 0 };
|
|
2133
|
+
const migrated = walkCount(parsed, vault, counter);
|
|
2134
|
+
if (counter.n === 0) return { migrated: 0, file: configPath };
|
|
2135
|
+
await atomicWrite(configPath, JSON.stringify(migrated, null, 2), { mode: 384 });
|
|
2136
|
+
await restrictFilePermissions(configPath);
|
|
2137
|
+
return { migrated: counter.n, file: configPath };
|
|
2138
|
+
}
|
|
2139
|
+
async function restrictFilePermissions(filePath, opts) {
|
|
2140
|
+
const warn = ((msg) => console.warn(msg));
|
|
2141
|
+
if (process.platform === "win32") {
|
|
2142
|
+
try {
|
|
2143
|
+
const { execFile: execFile2 } = await import('child_process');
|
|
2144
|
+
const { promisify: promisify2 } = await import('util');
|
|
2145
|
+
const execFileAsync = promisify2(execFile2);
|
|
2146
|
+
const user = windowsAccountName();
|
|
2147
|
+
if (!user) {
|
|
2148
|
+
warn(
|
|
2149
|
+
`[secret-vault] Could not determine the current Windows user for ${filePath}; skipping icacls hardening.`
|
|
2150
|
+
);
|
|
2151
|
+
return;
|
|
2152
|
+
}
|
|
2153
|
+
await execFileAsync("icacls", [filePath, "/inheritance:r", "/grant:r", `${user}:(F)`]);
|
|
2154
|
+
} catch {
|
|
2155
|
+
warn(
|
|
2156
|
+
`[secret-vault] Could not restrict permissions on ${filePath} \u2014 config file may be readable by other users on this system.`
|
|
2157
|
+
);
|
|
2158
|
+
}
|
|
2159
|
+
} else {
|
|
2160
|
+
try {
|
|
2161
|
+
await fsp.chmod(filePath, 384);
|
|
2162
|
+
} catch {
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
function windowsAccountName() {
|
|
2167
|
+
const username = process.env.USERNAME || process.env.USER;
|
|
2168
|
+
if (!username || username.includes("\0")) return void 0;
|
|
2169
|
+
const domain = process.env.USERDOMAIN;
|
|
2170
|
+
if (domain && !domain.includes("\0")) return `${domain}\\${username}`;
|
|
2171
|
+
return username;
|
|
2172
|
+
}
|
|
2173
|
+
function walkCount(node, vault, counter) {
|
|
2174
|
+
if (node === null || node === void 0) return node;
|
|
2175
|
+
if (typeof node !== "object") return node;
|
|
2176
|
+
if (Array.isArray(node)) {
|
|
2177
|
+
return node.map((item) => walkCount(item, vault, counter));
|
|
2178
|
+
}
|
|
2179
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
2180
|
+
for (const [k, v] of Object.entries(node)) {
|
|
2181
|
+
if (typeof v === "string" && isSecretField(k) && !vault.isEncrypted(v) && v.length > 0) {
|
|
2182
|
+
out[k] = vault.encrypt(v);
|
|
2183
|
+
counter.n++;
|
|
2184
|
+
} else if (typeof v === "object" && v !== null) {
|
|
2185
|
+
out[k] = walkCount(v, vault, counter);
|
|
2186
|
+
} else {
|
|
2187
|
+
out[k] = v;
|
|
2188
|
+
}
|
|
2189
|
+
}
|
|
2190
|
+
return out;
|
|
2191
|
+
}
|
|
2192
|
+
var FORBIDDEN_PROTO_KEYS = /* @__PURE__ */ new Set([
|
|
2193
|
+
"__proto__",
|
|
2194
|
+
"constructor",
|
|
2195
|
+
"prototype",
|
|
2196
|
+
"__defineGetter__",
|
|
2197
|
+
"__defineSetter__",
|
|
2198
|
+
"__lookupGetter__",
|
|
2199
|
+
"__lookupSetter__"
|
|
2200
|
+
]);
|
|
2201
|
+
function deepMerge(a, b) {
|
|
2202
|
+
const out = { ...a };
|
|
2203
|
+
for (const [k, v] of Object.entries(b)) {
|
|
2204
|
+
if (FORBIDDEN_PROTO_KEYS.has(k)) continue;
|
|
2205
|
+
const existing = out[k];
|
|
2206
|
+
if (v !== null && typeof v === "object" && !Array.isArray(v) && existing !== null && typeof existing === "object" && !Array.isArray(existing)) {
|
|
2207
|
+
out[k] = deepMerge(existing, v);
|
|
2208
|
+
} else {
|
|
2209
|
+
out[k] = v;
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
return out;
|
|
2213
|
+
}
|
|
2027
2214
|
|
|
2028
2215
|
// src/storage/config-loader.ts
|
|
2029
2216
|
init_atomic_write();
|
|
@@ -2209,7 +2396,7 @@ var defaultIndexing = {
|
|
|
2209
2396
|
function isPrimitiveArray(a) {
|
|
2210
2397
|
return a.every((v) => v === null || typeof v !== "object");
|
|
2211
2398
|
}
|
|
2212
|
-
var
|
|
2399
|
+
var FORBIDDEN_PROTO_KEYS2 = /* @__PURE__ */ new Set([
|
|
2213
2400
|
"__proto__",
|
|
2214
2401
|
"constructor",
|
|
2215
2402
|
"prototype",
|
|
@@ -2218,12 +2405,12 @@ var FORBIDDEN_PROTO_KEYS = /* @__PURE__ */ new Set([
|
|
|
2218
2405
|
"__lookupGetter__",
|
|
2219
2406
|
"__lookupSetter__"
|
|
2220
2407
|
]);
|
|
2221
|
-
function
|
|
2408
|
+
function deepMerge2(base, patch) {
|
|
2222
2409
|
if (typeof base !== "object" || base === null) return patch ?? base;
|
|
2223
2410
|
if (typeof patch !== "object" || patch === null) return base;
|
|
2224
2411
|
const out = { ...base };
|
|
2225
2412
|
for (const [k, v] of Object.entries(patch)) {
|
|
2226
|
-
if (
|
|
2413
|
+
if (FORBIDDEN_PROTO_KEYS2.has(k)) continue;
|
|
2227
2414
|
const existing = out[k];
|
|
2228
2415
|
if (Array.isArray(v)) {
|
|
2229
2416
|
if (Array.isArray(existing) && isPrimitiveArray(v) && isPrimitiveArray(existing)) {
|
|
@@ -2237,7 +2424,7 @@ function deepMerge(base, patch) {
|
|
|
2237
2424
|
}
|
|
2238
2425
|
}
|
|
2239
2426
|
} else if (typeof v === "object" && v !== null && typeof existing === "object" && existing !== null) {
|
|
2240
|
-
out[k] =
|
|
2427
|
+
out[k] = deepMerge2(existing, v);
|
|
2241
2428
|
} else if (v !== void 0) {
|
|
2242
2429
|
out[k] = v;
|
|
2243
2430
|
}
|
|
@@ -2262,9 +2449,9 @@ var DefaultConfigLoader = class {
|
|
|
2262
2449
|
this.readJson(this.paths.projectLocalConfig),
|
|
2263
2450
|
this.readJson(this.paths.inProjectConfig)
|
|
2264
2451
|
]);
|
|
2265
|
-
cfg =
|
|
2266
|
-
cfg =
|
|
2267
|
-
cfg =
|
|
2452
|
+
cfg = deepMerge2(cfg, global);
|
|
2453
|
+
cfg = deepMerge2(cfg, local);
|
|
2454
|
+
cfg = deepMerge2(cfg, inProject);
|
|
2268
2455
|
for (const [key, fn] of Object.entries(ENV_MAP)) {
|
|
2269
2456
|
const v = process.env[key];
|
|
2270
2457
|
if (v) fn(cfg, v);
|
|
@@ -2278,14 +2465,14 @@ var DefaultConfigLoader = class {
|
|
|
2278
2465
|
try {
|
|
2279
2466
|
const patch = await src.read();
|
|
2280
2467
|
if (patch && Object.keys(patch).length > 0) {
|
|
2281
|
-
cfg =
|
|
2468
|
+
cfg = deepMerge2(cfg, patch);
|
|
2282
2469
|
}
|
|
2283
2470
|
} catch (err) {
|
|
2284
2471
|
console.warn(`Config source "${src.name}" failed`, err);
|
|
2285
2472
|
}
|
|
2286
2473
|
}
|
|
2287
2474
|
if (opts.cliFlags) {
|
|
2288
|
-
cfg =
|
|
2475
|
+
cfg = deepMerge2(cfg, opts.cliFlags);
|
|
2289
2476
|
}
|
|
2290
2477
|
if (this.vault) {
|
|
2291
2478
|
cfg = decryptConfigSecrets(cfg, this.vault);
|
|
@@ -3697,239 +3884,12 @@ var DefaultSecretScrubber = class {
|
|
|
3697
3884
|
}
|
|
3698
3885
|
};
|
|
3699
3886
|
|
|
3700
|
-
// src/types/secret-vault.ts
|
|
3701
|
-
var ENCRYPTED_PREFIX = "enc:v1:";
|
|
3702
|
-
|
|
3703
|
-
// src/security/secret-vault.ts
|
|
3704
|
-
init_atomic_write();
|
|
3705
|
-
var KEY_BYTES = 32;
|
|
3706
|
-
var IV_BYTES = 12;
|
|
3707
|
-
var TAG_BYTES = 16;
|
|
3708
|
-
var ALGO = "aes-256-gcm";
|
|
3709
|
-
var DefaultSecretVault = class {
|
|
3710
|
-
keyFile;
|
|
3711
|
-
key;
|
|
3712
|
-
constructor(opts) {
|
|
3713
|
-
this.keyFile = opts.keyFile;
|
|
3714
|
-
}
|
|
3715
|
-
isEncrypted(value) {
|
|
3716
|
-
return typeof value === "string" && value.startsWith(ENCRYPTED_PREFIX);
|
|
3717
|
-
}
|
|
3718
|
-
encrypt(plaintext) {
|
|
3719
|
-
if (this.isEncrypted(plaintext)) return plaintext;
|
|
3720
|
-
const key = this.loadOrCreateKey();
|
|
3721
|
-
const iv = randomBytes(IV_BYTES);
|
|
3722
|
-
const cipher = createCipheriv(ALGO, key, iv);
|
|
3723
|
-
const ct = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
|
|
3724
|
-
const tag = cipher.getAuthTag();
|
|
3725
|
-
return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${tag.toString("base64")}:${ct.toString("base64")}`;
|
|
3726
|
-
}
|
|
3727
|
-
decrypt(value) {
|
|
3728
|
-
if (!this.isEncrypted(value)) return value;
|
|
3729
|
-
const rest = value.slice(ENCRYPTED_PREFIX.length);
|
|
3730
|
-
const parts = rest.split(":");
|
|
3731
|
-
if (parts.length !== 3) {
|
|
3732
|
-
throw new Error("SecretVault: malformed encrypted value");
|
|
3733
|
-
}
|
|
3734
|
-
const [ivB64, tagB64, ctB64] = parts;
|
|
3735
|
-
const iv = Buffer.from(ivB64, "base64");
|
|
3736
|
-
const tag = Buffer.from(tagB64, "base64");
|
|
3737
|
-
const ct = Buffer.from(ctB64, "base64");
|
|
3738
|
-
if (iv.length !== IV_BYTES) throw new Error("SecretVault: bad IV length");
|
|
3739
|
-
if (tag.length !== TAG_BYTES) throw new Error("SecretVault: bad tag length");
|
|
3740
|
-
const key = this.loadOrCreateKey();
|
|
3741
|
-
const decipher = createDecipheriv(ALGO, key, iv);
|
|
3742
|
-
decipher.setAuthTag(tag);
|
|
3743
|
-
const pt = Buffer.concat([decipher.update(ct), decipher.final()]);
|
|
3744
|
-
return pt.toString("utf8");
|
|
3745
|
-
}
|
|
3746
|
-
loadOrCreateKey() {
|
|
3747
|
-
if (this.key) return this.key;
|
|
3748
|
-
try {
|
|
3749
|
-
const buf = fs5.readFileSync(this.keyFile);
|
|
3750
|
-
if (buf.length !== KEY_BYTES) {
|
|
3751
|
-
throw new Error(
|
|
3752
|
-
`SecretVault: key file ${this.keyFile} is ${buf.length} bytes (expected ${KEY_BYTES}). Remove it manually to generate a new key.`
|
|
3753
|
-
);
|
|
3754
|
-
}
|
|
3755
|
-
this.key = buf;
|
|
3756
|
-
return this.key;
|
|
3757
|
-
} catch (err) {
|
|
3758
|
-
if (err.code !== "ENOENT") throw err;
|
|
3759
|
-
}
|
|
3760
|
-
fs5.mkdirSync(path11.dirname(this.keyFile), { recursive: true });
|
|
3761
|
-
const key = randomBytes(KEY_BYTES);
|
|
3762
|
-
try {
|
|
3763
|
-
fs5.writeFileSync(this.keyFile, key, { mode: 384, flag: "wx" });
|
|
3764
|
-
} catch (err) {
|
|
3765
|
-
if (err.code !== "EEXIST") throw err;
|
|
3766
|
-
const buf = fs5.readFileSync(this.keyFile);
|
|
3767
|
-
if (buf.length !== KEY_BYTES) {
|
|
3768
|
-
throw new Error(
|
|
3769
|
-
`SecretVault: key file ${this.keyFile} is ${buf.length} bytes (expected ${KEY_BYTES}). Remove it manually to generate a new key.`
|
|
3770
|
-
);
|
|
3771
|
-
}
|
|
3772
|
-
this.key = buf;
|
|
3773
|
-
return this.key;
|
|
3774
|
-
}
|
|
3775
|
-
this.key = key;
|
|
3776
|
-
return key;
|
|
3777
|
-
}
|
|
3778
|
-
};
|
|
3779
|
-
function decryptConfigSecrets2(cfg, vault, opts) {
|
|
3780
|
-
const warn = opts?.warn ?? ((msg) => console.warn(msg));
|
|
3781
|
-
return walk2(cfg, vault, (v, key) => {
|
|
3782
|
-
try {
|
|
3783
|
-
return vault.decrypt(v);
|
|
3784
|
-
} catch (err) {
|
|
3785
|
-
warn(
|
|
3786
|
-
`[secret-vault] Failed to decrypt "${key}": ${err instanceof Error ? err.message : err}`
|
|
3787
|
-
);
|
|
3788
|
-
return "";
|
|
3789
|
-
}
|
|
3790
|
-
});
|
|
3791
|
-
}
|
|
3792
|
-
function encryptConfigSecrets(cfg, vault, _opts) {
|
|
3793
|
-
return walk2(cfg, vault, (v) => vault.encrypt(v));
|
|
3794
|
-
}
|
|
3795
|
-
function walk2(node, vault, transform) {
|
|
3796
|
-
if (node === null || node === void 0) return node;
|
|
3797
|
-
if (typeof node !== "object") return node;
|
|
3798
|
-
if (Array.isArray(node)) {
|
|
3799
|
-
return node.map((item) => walk2(item, vault, transform));
|
|
3800
|
-
}
|
|
3801
|
-
const out = /* @__PURE__ */ Object.create(null);
|
|
3802
|
-
for (const [k, v] of Object.entries(node)) {
|
|
3803
|
-
if (typeof v === "string" && isSecretField2(k)) {
|
|
3804
|
-
out[k] = transform(v, k);
|
|
3805
|
-
} else if (typeof v === "object" && v !== null) {
|
|
3806
|
-
out[k] = walk2(v, vault, transform);
|
|
3807
|
-
} else {
|
|
3808
|
-
out[k] = v;
|
|
3809
|
-
}
|
|
3810
|
-
}
|
|
3811
|
-
return out;
|
|
3812
|
-
}
|
|
3813
|
-
var SECRET_KEY_PATTERN2 = /(?:apikey|api_key|authtoken|auth_token|bearer|secret|password|passwd|pwd|refreshtoken|refresh_token|sessionkey|session_key|access[_-]?token|private[_-]?key)/i;
|
|
3814
|
-
var NON_SECRET_OVERRIDES2 = /* @__PURE__ */ new Set(["publickey", "public_key"]);
|
|
3815
|
-
function isSecretField2(name) {
|
|
3816
|
-
const lc = name.toLowerCase();
|
|
3817
|
-
if (NON_SECRET_OVERRIDES2.has(lc)) return false;
|
|
3818
|
-
return SECRET_KEY_PATTERN2.test(lc);
|
|
3819
|
-
}
|
|
3820
|
-
async function rewriteConfigEncrypted(configPath, vault, patch) {
|
|
3821
|
-
let current = {};
|
|
3822
|
-
try {
|
|
3823
|
-
const raw = await fsp.readFile(configPath, "utf8");
|
|
3824
|
-
current = JSON.parse(raw);
|
|
3825
|
-
} catch {
|
|
3826
|
-
}
|
|
3827
|
-
const merged = deepMerge2(current, patch ?? {});
|
|
3828
|
-
const encrypted = encryptConfigSecrets(merged, vault);
|
|
3829
|
-
await fsp.mkdir(path11.dirname(configPath), { recursive: true });
|
|
3830
|
-
await atomicWrite(configPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
3831
|
-
await restrictFilePermissions(configPath);
|
|
3832
|
-
}
|
|
3833
|
-
async function migratePlaintextSecrets(configPath, vault) {
|
|
3834
|
-
let raw;
|
|
3835
|
-
try {
|
|
3836
|
-
raw = await fsp.readFile(configPath, "utf8");
|
|
3837
|
-
} catch {
|
|
3838
|
-
return { migrated: 0, file: configPath };
|
|
3839
|
-
}
|
|
3840
|
-
let parsed;
|
|
3841
|
-
try {
|
|
3842
|
-
parsed = JSON.parse(raw);
|
|
3843
|
-
} catch {
|
|
3844
|
-
return { migrated: 0, file: configPath };
|
|
3845
|
-
}
|
|
3846
|
-
const counter = { n: 0 };
|
|
3847
|
-
const migrated = walkCount(parsed, vault, counter);
|
|
3848
|
-
if (counter.n === 0) return { migrated: 0, file: configPath };
|
|
3849
|
-
await atomicWrite(configPath, JSON.stringify(migrated, null, 2), { mode: 384 });
|
|
3850
|
-
await restrictFilePermissions(configPath);
|
|
3851
|
-
return { migrated: counter.n, file: configPath };
|
|
3852
|
-
}
|
|
3853
|
-
async function restrictFilePermissions(filePath, opts) {
|
|
3854
|
-
const warn = ((msg) => console.warn(msg));
|
|
3855
|
-
if (process.platform === "win32") {
|
|
3856
|
-
try {
|
|
3857
|
-
const { execFile: execFile2 } = await import('child_process');
|
|
3858
|
-
const { promisify: promisify2 } = await import('util');
|
|
3859
|
-
const execFileAsync = promisify2(execFile2);
|
|
3860
|
-
const user = windowsAccountName();
|
|
3861
|
-
if (!user) {
|
|
3862
|
-
warn(
|
|
3863
|
-
`[secret-vault] Could not determine the current Windows user for ${filePath}; skipping icacls hardening.`
|
|
3864
|
-
);
|
|
3865
|
-
return;
|
|
3866
|
-
}
|
|
3867
|
-
await execFileAsync("icacls", [filePath, "/inheritance:r", "/grant:r", `${user}:(F)`]);
|
|
3868
|
-
} catch {
|
|
3869
|
-
warn(
|
|
3870
|
-
`[secret-vault] Could not restrict permissions on ${filePath} \u2014 config file may be readable by other users on this system.`
|
|
3871
|
-
);
|
|
3872
|
-
}
|
|
3873
|
-
} else {
|
|
3874
|
-
try {
|
|
3875
|
-
await fsp.chmod(filePath, 384);
|
|
3876
|
-
} catch {
|
|
3877
|
-
}
|
|
3878
|
-
}
|
|
3879
|
-
}
|
|
3880
|
-
function windowsAccountName() {
|
|
3881
|
-
const username = process.env.USERNAME || process.env.USER;
|
|
3882
|
-
if (!username || username.includes("\0")) return void 0;
|
|
3883
|
-
const domain = process.env.USERDOMAIN;
|
|
3884
|
-
if (domain && !domain.includes("\0")) return `${domain}\\${username}`;
|
|
3885
|
-
return username;
|
|
3886
|
-
}
|
|
3887
|
-
function walkCount(node, vault, counter) {
|
|
3888
|
-
if (node === null || node === void 0) return node;
|
|
3889
|
-
if (typeof node !== "object") return node;
|
|
3890
|
-
if (Array.isArray(node)) {
|
|
3891
|
-
return node.map((item) => walkCount(item, vault, counter));
|
|
3892
|
-
}
|
|
3893
|
-
const out = /* @__PURE__ */ Object.create(null);
|
|
3894
|
-
for (const [k, v] of Object.entries(node)) {
|
|
3895
|
-
if (typeof v === "string" && isSecretField2(k) && !vault.isEncrypted(v) && v.length > 0) {
|
|
3896
|
-
out[k] = vault.encrypt(v);
|
|
3897
|
-
counter.n++;
|
|
3898
|
-
} else if (typeof v === "object" && v !== null) {
|
|
3899
|
-
out[k] = walkCount(v, vault, counter);
|
|
3900
|
-
} else {
|
|
3901
|
-
out[k] = v;
|
|
3902
|
-
}
|
|
3903
|
-
}
|
|
3904
|
-
return out;
|
|
3905
|
-
}
|
|
3906
|
-
var FORBIDDEN_PROTO_KEYS2 = /* @__PURE__ */ new Set([
|
|
3907
|
-
"__proto__",
|
|
3908
|
-
"constructor",
|
|
3909
|
-
"prototype",
|
|
3910
|
-
"__defineGetter__",
|
|
3911
|
-
"__defineSetter__",
|
|
3912
|
-
"__lookupGetter__",
|
|
3913
|
-
"__lookupSetter__"
|
|
3914
|
-
]);
|
|
3915
|
-
function deepMerge2(a, b) {
|
|
3916
|
-
const out = { ...a };
|
|
3917
|
-
for (const [k, v] of Object.entries(b)) {
|
|
3918
|
-
if (FORBIDDEN_PROTO_KEYS2.has(k)) continue;
|
|
3919
|
-
const existing = out[k];
|
|
3920
|
-
if (v !== null && typeof v === "object" && !Array.isArray(v) && existing !== null && typeof existing === "object" && !Array.isArray(existing)) {
|
|
3921
|
-
out[k] = deepMerge2(existing, v);
|
|
3922
|
-
} else {
|
|
3923
|
-
out[k] = v;
|
|
3924
|
-
}
|
|
3925
|
-
}
|
|
3926
|
-
return out;
|
|
3927
|
-
}
|
|
3928
|
-
|
|
3929
3887
|
// src/security/capabilities.ts
|
|
3930
3888
|
var ToolCapabilities = {
|
|
3931
3889
|
/** Can execute arbitrary commands in the user's shell (the `bash` tool). */
|
|
3932
3890
|
SHELL_ARBITRARY: "shell.arbitrary",
|
|
3891
|
+
/** Can execute a restricted set of commands (the `exec` tool). */
|
|
3892
|
+
SHELL_RESTRICTED: "shell.restricted",
|
|
3933
3893
|
/** Can write / modify / delete files inside the project. */
|
|
3934
3894
|
FS_WRITE: "fs.write",
|
|
3935
3895
|
/** Can write files outside the current project root (very high risk). */
|
|
@@ -3945,6 +3905,7 @@ var ToolCapabilities = {
|
|
|
3945
3905
|
};
|
|
3946
3906
|
var DANGEROUS_FOR_SUBAGENTS = [
|
|
3947
3907
|
ToolCapabilities.SHELL_ARBITRARY,
|
|
3908
|
+
ToolCapabilities.SHELL_RESTRICTED,
|
|
3948
3909
|
ToolCapabilities.FS_WRITE,
|
|
3949
3910
|
ToolCapabilities.FS_WRITE_OUTSIDE_PROJECT,
|
|
3950
3911
|
ToolCapabilities.MCP_PROXY,
|
|
@@ -4388,38 +4349,15 @@ var DefaultPermissionPolicy = class {
|
|
|
4388
4349
|
}
|
|
4389
4350
|
};
|
|
4390
4351
|
var AutoApprovePermissionPolicy = class _AutoApprovePermissionPolicy {
|
|
4391
|
-
/**
|
|
4392
|
-
* Legacy name-based denylist.
|
|
4393
|
-
* @deprecated Prefer declaring `capabilities` on the Tool and using capability-based checks.
|
|
4394
|
-
*/
|
|
4395
|
-
static LEGACY_NAME_DENY = /* @__PURE__ */ new Set([
|
|
4396
|
-
"bash",
|
|
4397
|
-
"write",
|
|
4398
|
-
"edit",
|
|
4399
|
-
"replace",
|
|
4400
|
-
"scaffold",
|
|
4401
|
-
"patch",
|
|
4402
|
-
"install",
|
|
4403
|
-
"exec"
|
|
4404
|
-
]);
|
|
4405
|
-
// Note: hasDangerousCapabilityForSubagents is now the shared helper from capabilities.ts
|
|
4406
|
-
// The old private method was removed in favor of the centralized utility.
|
|
4407
|
-
/**
|
|
4408
|
-
* Tools from MCP servers (`mcp__<server>__<tool>`) are external code of
|
|
4409
|
-
* unknown capability — they may wrap a shell or filesystem. They are
|
|
4410
|
-
* fail-closed here: not auto-approved for subagents by default, so the
|
|
4411
|
-
* leader must allow them explicitly per-spawn.
|
|
4412
|
-
*/
|
|
4413
4352
|
static isMcpTool(name) {
|
|
4414
4353
|
return name.startsWith("mcp__");
|
|
4415
4354
|
}
|
|
4416
4355
|
async evaluate(tool) {
|
|
4417
4356
|
const hasDangerousCap = hasDangerousCapabilityForSubagents(tool);
|
|
4418
|
-
const legacyNameBlock = _AutoApprovePermissionPolicy.LEGACY_NAME_DENY.has(tool.name);
|
|
4419
4357
|
const isMcp = _AutoApprovePermissionPolicy.isMcpTool(tool.name);
|
|
4420
|
-
const blocked = tool.permission === "deny" || hasDangerousCap ||
|
|
4358
|
+
const blocked = tool.permission === "deny" || hasDangerousCap || isMcp;
|
|
4421
4359
|
if (blocked) {
|
|
4422
|
-
const reason = hasDangerousCap ? `tool declares dangerous capability (${tool.capabilities?.join(", ")}) \u2014 not auto-approved for subagents` :
|
|
4360
|
+
const reason = hasDangerousCap ? `tool declares dangerous capability (${tool.capabilities?.join(", ")}) \u2014 not auto-approved for subagents` : isMcp ? `MCP tool ${tool.name} is not auto-approved for subagents \u2014 ask the leader to allow it explicitly` : "tool default deny";
|
|
4423
4361
|
return {
|
|
4424
4362
|
permission: "deny",
|
|
4425
4363
|
source: "subagent_guard",
|
|
@@ -5088,53 +5026,63 @@ async function streamProviderToResponse(provider, req, signal, ctx, events) {
|
|
|
5088
5026
|
const next = await iter.next();
|
|
5089
5027
|
if (next.done) break;
|
|
5090
5028
|
const ev = next.value;
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5029
|
+
try {
|
|
5030
|
+
switch (ev.type) {
|
|
5031
|
+
case "message_start":
|
|
5032
|
+
handleMessageStart(state, ev.model);
|
|
5033
|
+
break;
|
|
5034
|
+
case "content_block_start":
|
|
5035
|
+
handleContentBlockStart(state, ev);
|
|
5036
|
+
break;
|
|
5037
|
+
case "content_block_stop":
|
|
5038
|
+
handleContentBlockStop(state, ev);
|
|
5039
|
+
break;
|
|
5040
|
+
case "text_delta":
|
|
5041
|
+
handleTextDelta(state, ev.text);
|
|
5042
|
+
events.emit("provider.text_delta", { ctx, text: ev.text });
|
|
5043
|
+
break;
|
|
5044
|
+
case "tool_use_start": {
|
|
5045
|
+
const idVal = ev.id;
|
|
5046
|
+
const nameVal = ev.name;
|
|
5047
|
+
handleToolUseStart(state, { id: idVal, name: nameVal });
|
|
5048
|
+
const emittedPayload = { ctx, id: idVal ?? "unknown", name: nameVal ?? "unknown" };
|
|
5049
|
+
events.emit("provider.tool_use_start", emittedPayload);
|
|
5050
|
+
break;
|
|
5051
|
+
}
|
|
5052
|
+
case "tool_use_input_delta":
|
|
5053
|
+
handleToolUseInputDelta(state, ev);
|
|
5054
|
+
break;
|
|
5055
|
+
case "tool_use_stop": {
|
|
5056
|
+
const stoppedName = state.tools.get(ev.id)?.name ?? "unknown";
|
|
5057
|
+
handleToolUseStop(state, ev);
|
|
5058
|
+
events.emit("provider.tool_use_stop", { ctx, id: ev.id, name: stoppedName });
|
|
5059
|
+
break;
|
|
5060
|
+
}
|
|
5061
|
+
case "thinking_start":
|
|
5062
|
+
handleThinkingStart(state, ev);
|
|
5063
|
+
break;
|
|
5064
|
+
case "thinking_delta":
|
|
5065
|
+
handleThinkingDelta(state, ev.text);
|
|
5066
|
+
events.emit("provider.thinking_delta", { ctx, text: ev.text });
|
|
5067
|
+
break;
|
|
5068
|
+
case "thinking_signature":
|
|
5069
|
+
handleThinkingSignature(state, ev.signature);
|
|
5070
|
+
break;
|
|
5071
|
+
case "thinking_stop":
|
|
5072
|
+
handleThinkingStop(state);
|
|
5073
|
+
break;
|
|
5074
|
+
case "message_stop":
|
|
5075
|
+
handleMessageStop(state, ev);
|
|
5076
|
+
break;
|
|
5077
|
+
default:
|
|
5078
|
+
break;
|
|
5121
5079
|
}
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
break;
|
|
5129
|
-
case "thinking_signature":
|
|
5130
|
-
handleThinkingSignature(state, ev.signature);
|
|
5131
|
-
break;
|
|
5132
|
-
case "thinking_stop":
|
|
5133
|
-
handleThinkingStop(state);
|
|
5134
|
-
break;
|
|
5135
|
-
case "message_stop":
|
|
5136
|
-
handleMessageStop(state, ev);
|
|
5137
|
-
break;
|
|
5080
|
+
} catch (handlerErr) {
|
|
5081
|
+
events.emit("provider.stream_error", {
|
|
5082
|
+
ctx,
|
|
5083
|
+
eventType: ev.type,
|
|
5084
|
+
msg: handlerErr instanceof Error ? handlerErr.message : String(handlerErr)
|
|
5085
|
+
});
|
|
5138
5086
|
}
|
|
5139
5087
|
}
|
|
5140
5088
|
} catch (err) {
|
|
@@ -5147,8 +5095,10 @@ async function streamProviderToResponse(provider, req, signal, ctx, events) {
|
|
|
5147
5095
|
try {
|
|
5148
5096
|
let drainTimer = null;
|
|
5149
5097
|
try {
|
|
5098
|
+
const drainPromise = Promise.resolve(iter.return?.()).catch(() => {
|
|
5099
|
+
});
|
|
5150
5100
|
await Promise.race([
|
|
5151
|
-
|
|
5101
|
+
drainPromise,
|
|
5152
5102
|
new Promise((resolve5) => {
|
|
5153
5103
|
drainTimer = setTimeout(resolve5, STREAM_DRAIN_TIMEOUT_MS);
|
|
5154
5104
|
})
|
|
@@ -5246,6 +5196,17 @@ var DefaultProviderRunner = class {
|
|
|
5246
5196
|
|
|
5247
5197
|
// src/utils/token-estimate.ts
|
|
5248
5198
|
var RoughTokenEstimate = (text, charsPerToken = 3.5) => Math.max(1, Math.ceil(text.length / charsPerToken));
|
|
5199
|
+
var CALIBRATION_GLOBAL_KEY = "__global__";
|
|
5200
|
+
var _cals = /* @__PURE__ */ new Map();
|
|
5201
|
+
function calState(key) {
|
|
5202
|
+
let state = _cals.get(key);
|
|
5203
|
+
if (!state) {
|
|
5204
|
+
state = { ratio: 1, count: 0, prevEst: 0 };
|
|
5205
|
+
_cals.set(key, state);
|
|
5206
|
+
}
|
|
5207
|
+
return state;
|
|
5208
|
+
}
|
|
5209
|
+
var MIN_SAMPLES_FOR_CALIBRATION = 3;
|
|
5249
5210
|
var ESTIMATE_CACHE = /* @__PURE__ */ new Map();
|
|
5250
5211
|
var ESTIMATE_CACHE_MAX_SIZE = 1e4;
|
|
5251
5212
|
function getCachedEstimate(key, compute) {
|
|
@@ -5277,10 +5238,25 @@ function estimateToolResultTokens(content) {
|
|
|
5277
5238
|
function estimateTextTokens(text) {
|
|
5278
5239
|
return RoughTokenEstimate(text);
|
|
5279
5240
|
}
|
|
5241
|
+
function estimateMessageTokens(messages) {
|
|
5242
|
+
let total = 0;
|
|
5243
|
+
for (const m of messages) {
|
|
5244
|
+
if (typeof m.content === "string") {
|
|
5245
|
+
total += estimateTextTokens(m.content);
|
|
5246
|
+
} else {
|
|
5247
|
+
for (const b of m.content) {
|
|
5248
|
+
if (b.type === "text") total += estimateTextTokens(b.text);
|
|
5249
|
+
else if (b.type === "tool_use") total += estimateToolInputTokens(b.input);
|
|
5250
|
+
else if (b.type === "tool_result") total += estimateToolResultTokens(b.content);
|
|
5251
|
+
}
|
|
5252
|
+
}
|
|
5253
|
+
}
|
|
5254
|
+
return total;
|
|
5255
|
+
}
|
|
5280
5256
|
function estimateToolDefTokens(tool) {
|
|
5281
5257
|
return RoughTokenEstimate(tool.name) + RoughTokenEstimate(tool.description ?? "") + RoughTokenEstimate(JSON.stringify(tool.inputSchema));
|
|
5282
5258
|
}
|
|
5283
|
-
function estimateRequestTokens(messages, systemPrompt, tools) {
|
|
5259
|
+
function estimateRequestTokens(messages, systemPrompt, tools, calibrationKey = CALIBRATION_GLOBAL_KEY) {
|
|
5284
5260
|
let messagesTokens = 0;
|
|
5285
5261
|
if (typeof messages === "string") {
|
|
5286
5262
|
messagesTokens = RoughTokenEstimate(messages);
|
|
@@ -5319,6 +5295,7 @@ function estimateRequestTokens(messages, systemPrompt, tools) {
|
|
|
5319
5295
|
toolsTokens += estimateToolDefTokens(t);
|
|
5320
5296
|
}
|
|
5321
5297
|
const total = messagesTokens + systemTokens + toolsTokens;
|
|
5298
|
+
calState(calibrationKey).prevEst = total;
|
|
5322
5299
|
return {
|
|
5323
5300
|
messages: messagesTokens,
|
|
5324
5301
|
systemPrompt: systemTokens,
|
|
@@ -5326,39 +5303,164 @@ function estimateRequestTokens(messages, systemPrompt, tools) {
|
|
|
5326
5303
|
total
|
|
5327
5304
|
};
|
|
5328
5305
|
}
|
|
5329
|
-
function estimateRequestTokensCalibrated(messages, systemPrompt, tools) {
|
|
5330
|
-
const result = estimateRequestTokens(messages, systemPrompt, tools);
|
|
5306
|
+
function estimateRequestTokensCalibrated(messages, systemPrompt, tools, calibrationKey = CALIBRATION_GLOBAL_KEY) {
|
|
5307
|
+
const result = estimateRequestTokens(messages, systemPrompt, tools, calibrationKey);
|
|
5308
|
+
const cal = calState(calibrationKey);
|
|
5309
|
+
if (cal.count >= MIN_SAMPLES_FOR_CALIBRATION) {
|
|
5310
|
+
const safeRatio = Math.min(1.5, Math.max(0.5, cal.ratio));
|
|
5311
|
+
return {
|
|
5312
|
+
messages: Math.round(result.messages * safeRatio),
|
|
5313
|
+
systemPrompt: Math.round(result.systemPrompt * safeRatio),
|
|
5314
|
+
tools: Math.round(result.tools * safeRatio),
|
|
5315
|
+
total: Math.round(result.total * safeRatio)
|
|
5316
|
+
};
|
|
5317
|
+
}
|
|
5331
5318
|
return result;
|
|
5332
5319
|
}
|
|
5333
5320
|
|
|
5321
|
+
// src/types/blocks.ts
|
|
5322
|
+
function isTextBlock(b) {
|
|
5323
|
+
return b.type === "text";
|
|
5324
|
+
}
|
|
5325
|
+
|
|
5326
|
+
// src/execution/compaction-core.ts
|
|
5327
|
+
var estimateMessages = estimateMessageTokens;
|
|
5328
|
+
function hasTextContent(m) {
|
|
5329
|
+
if (typeof m.content === "string") return m.content.trim().length > 0;
|
|
5330
|
+
return m.content.some((b) => b.type === "text" && b.text.trim().length > 0);
|
|
5331
|
+
}
|
|
5332
|
+
function findPreserveStart(messages, preserveK) {
|
|
5333
|
+
let pairCount = 0;
|
|
5334
|
+
let preserveStart = messages.length;
|
|
5335
|
+
for (let i = messages.length - 1; i >= 0 && pairCount < preserveK; i--) {
|
|
5336
|
+
const m = messages[i];
|
|
5337
|
+
if (!m) continue;
|
|
5338
|
+
if (m.role === "user" || m.role === "assistant") {
|
|
5339
|
+
pairCount++;
|
|
5340
|
+
preserveStart = i;
|
|
5341
|
+
}
|
|
5342
|
+
}
|
|
5343
|
+
for (let i = preserveStart; i < messages.length; i++) {
|
|
5344
|
+
const m = messages[i];
|
|
5345
|
+
if (!m || typeof m.content === "string" || !Array.isArray(m.content)) continue;
|
|
5346
|
+
const hasToolUse2 = m.content.some((b) => b.type === "tool_use");
|
|
5347
|
+
if (hasToolUse2 && i + 1 < messages.length) {
|
|
5348
|
+
const next = messages[i + 1];
|
|
5349
|
+
if (next && next.role === "user" && typeof next.content !== "string" && Array.isArray(next.content) && next.content.some((b) => b.type === "tool_result")) {
|
|
5350
|
+
preserveStart = i + 1;
|
|
5351
|
+
}
|
|
5352
|
+
}
|
|
5353
|
+
}
|
|
5354
|
+
return preserveStart;
|
|
5355
|
+
}
|
|
5356
|
+
function eliseOldToolResults(messages, opts) {
|
|
5357
|
+
const preserveStart = findPreserveStart(messages, opts.preserveK);
|
|
5358
|
+
let saved = 0;
|
|
5359
|
+
let changed = false;
|
|
5360
|
+
const next = new Array(messages.length);
|
|
5361
|
+
for (let i = 0; i < messages.length; i++) {
|
|
5362
|
+
const msg = messages[i];
|
|
5363
|
+
if (i >= preserveStart || !msg || !Array.isArray(msg.content)) {
|
|
5364
|
+
next[i] = msg;
|
|
5365
|
+
continue;
|
|
5366
|
+
}
|
|
5367
|
+
const original = msg.content;
|
|
5368
|
+
const newContent = original.map((b) => {
|
|
5369
|
+
if (b.type !== "tool_result") return b;
|
|
5370
|
+
const tokens = estimateToolResultTokens(b.content);
|
|
5371
|
+
if (tokens < opts.eliseThreshold) return b;
|
|
5372
|
+
saved += tokens;
|
|
5373
|
+
const elided = {
|
|
5374
|
+
type: "tool_result",
|
|
5375
|
+
tool_use_id: b.tool_use_id,
|
|
5376
|
+
content: `[elided: ~${tokens} tokens]`,
|
|
5377
|
+
is_error: b.is_error
|
|
5378
|
+
};
|
|
5379
|
+
return elided;
|
|
5380
|
+
});
|
|
5381
|
+
if (newContent.every((b, idx) => b === original[idx])) {
|
|
5382
|
+
next[i] = msg;
|
|
5383
|
+
} else {
|
|
5384
|
+
next[i] = { ...msg, content: newContent };
|
|
5385
|
+
changed = true;
|
|
5386
|
+
}
|
|
5387
|
+
}
|
|
5388
|
+
return { messages: changed ? next : messages, saved, changed };
|
|
5389
|
+
}
|
|
5390
|
+
function buildLosslessDigest(messages) {
|
|
5391
|
+
const lines = [];
|
|
5392
|
+
for (const m of messages) {
|
|
5393
|
+
let text;
|
|
5394
|
+
let omitted = 0;
|
|
5395
|
+
if (typeof m.content === "string") {
|
|
5396
|
+
text = m.content;
|
|
5397
|
+
} else {
|
|
5398
|
+
const parts = [];
|
|
5399
|
+
for (const b of m.content) {
|
|
5400
|
+
if (isTextBlock(b)) parts.push(b.text);
|
|
5401
|
+
else if (b.type === "tool_use" || b.type === "tool_result") omitted++;
|
|
5402
|
+
}
|
|
5403
|
+
text = parts.join(" ");
|
|
5404
|
+
}
|
|
5405
|
+
if (text.trim().length === 0 && omitted === 0) continue;
|
|
5406
|
+
const marker = omitted > 0 ? ` [${omitted} tool call(s) omitted \u2014 see session log]` : "";
|
|
5407
|
+
lines.push(`[${m.role}]: ${text}${marker}`);
|
|
5408
|
+
}
|
|
5409
|
+
return lines.join("\n");
|
|
5410
|
+
}
|
|
5411
|
+
function findSafeBoundary(messages, from, to) {
|
|
5412
|
+
for (let i = to; i >= from; i--) {
|
|
5413
|
+
const m = messages[i];
|
|
5414
|
+
if (!m) continue;
|
|
5415
|
+
if (m.role === "user" && hasTextContent(m)) {
|
|
5416
|
+
return findExchangeStart(messages, i);
|
|
5417
|
+
}
|
|
5418
|
+
}
|
|
5419
|
+
return -1;
|
|
5420
|
+
}
|
|
5421
|
+
function findExchangeStart(messages, userIndex) {
|
|
5422
|
+
for (let i = userIndex - 1; i >= 0; i--) {
|
|
5423
|
+
const m = messages[i];
|
|
5424
|
+
if (!m) continue;
|
|
5425
|
+
if (m.role === "assistant") {
|
|
5426
|
+
const hasToolUse2 = Array.isArray(m.content) ? m.content.some((b) => b.type === "tool_use") : false;
|
|
5427
|
+
if (!hasToolUse2) return i + 1;
|
|
5428
|
+
} else if (m.role === "user") {
|
|
5429
|
+
return i;
|
|
5430
|
+
}
|
|
5431
|
+
}
|
|
5432
|
+
return 0;
|
|
5433
|
+
}
|
|
5434
|
+
|
|
5334
5435
|
// src/execution/compactor.ts
|
|
5335
5436
|
var HybridCompactor = class {
|
|
5336
5437
|
preserveK;
|
|
5337
5438
|
eliseThreshold;
|
|
5338
|
-
estimator;
|
|
5339
5439
|
constructor(opts = {}) {
|
|
5340
5440
|
this.preserveK = opts.preserveK ?? 5;
|
|
5341
5441
|
this.eliseThreshold = opts.eliseThreshold ?? 2e3;
|
|
5342
|
-
this.estimator = opts.estimator ?? estimateTextTokens;
|
|
5343
5442
|
}
|
|
5344
5443
|
async compact(ctx, opts = {}) {
|
|
5345
|
-
const beforeTokens =
|
|
5444
|
+
const beforeTokens = estimateMessages(ctx.messages);
|
|
5346
5445
|
const beforeFull = this.estimateFullRequest(ctx);
|
|
5347
5446
|
const reductions = [];
|
|
5348
5447
|
const policy = readContextWindowPolicy(ctx);
|
|
5349
5448
|
const preserveK = policy?.preserveK ?? this.preserveK;
|
|
5350
5449
|
const eliseThreshold = policy?.eliseThreshold ?? this.eliseThreshold;
|
|
5351
|
-
const
|
|
5352
|
-
if (
|
|
5450
|
+
const elide = eliseOldToolResults(ctx.messages, { preserveK, eliseThreshold });
|
|
5451
|
+
if (elide.changed) ctx.state.replaceMessages(elide.messages);
|
|
5452
|
+
if (elide.saved > 0) reductions.push({ phase: "elision", saved: elide.saved });
|
|
5453
|
+
let collapsedDigest;
|
|
5353
5454
|
if (opts.aggressive) {
|
|
5354
|
-
const
|
|
5355
|
-
if (
|
|
5455
|
+
const phase2 = this.collapseAncientTurns(ctx, preserveK);
|
|
5456
|
+
if (phase2.saved > 0) reductions.push({ phase: "summary", saved: phase2.saved });
|
|
5457
|
+
collapsedDigest = phase2.digest;
|
|
5356
5458
|
}
|
|
5357
5459
|
const repaired = repairToolUseAdjacency(ctx.messages);
|
|
5358
5460
|
if (repaired.report.changed) {
|
|
5359
5461
|
ctx.state.replaceMessages(repaired.messages);
|
|
5360
5462
|
}
|
|
5361
|
-
const afterTokens =
|
|
5463
|
+
const afterTokens = estimateMessages(ctx.messages);
|
|
5362
5464
|
const afterFull = this.estimateFullRequest(ctx);
|
|
5363
5465
|
return {
|
|
5364
5466
|
before: beforeTokens,
|
|
@@ -5366,6 +5468,7 @@ var HybridCompactor = class {
|
|
|
5366
5468
|
fullRequestTokensBefore: beforeFull,
|
|
5367
5469
|
fullRequestTokensAfter: afterFull,
|
|
5368
5470
|
reductions,
|
|
5471
|
+
collapsedDigest,
|
|
5369
5472
|
repaired: repaired.report.changed ? {
|
|
5370
5473
|
removedToolUses: repaired.report.removedToolUses,
|
|
5371
5474
|
removedToolResults: repaired.report.removedToolResults,
|
|
@@ -5378,72 +5481,23 @@ var HybridCompactor = class {
|
|
|
5378
5481
|
* This is the accurate figure for context-window pressure monitoring.
|
|
5379
5482
|
*/
|
|
5380
5483
|
estimateFullRequest(ctx) {
|
|
5381
|
-
|
|
5382
|
-
return breakdown.total;
|
|
5383
|
-
}
|
|
5384
|
-
eliseOldToolResults(ctx, preserveK = this.preserveK, eliseThreshold = this.eliseThreshold) {
|
|
5385
|
-
const messages = ctx.messages;
|
|
5386
|
-
let pairCount = 0;
|
|
5387
|
-
let preserveStart = messages.length;
|
|
5388
|
-
for (let i = messages.length - 1; i >= 0 && pairCount < preserveK; i--) {
|
|
5389
|
-
const m = messages[i];
|
|
5390
|
-
if (!m) continue;
|
|
5391
|
-
if (m.role === "user" || m.role === "assistant") {
|
|
5392
|
-
pairCount++;
|
|
5393
|
-
preserveStart = i;
|
|
5394
|
-
}
|
|
5395
|
-
}
|
|
5396
|
-
for (let i = preserveStart; i < messages.length; i++) {
|
|
5397
|
-
const m = messages[i];
|
|
5398
|
-
if (!m || typeof m.content === "string" || !Array.isArray(m.content)) continue;
|
|
5399
|
-
const hasToolUse2 = m.content.some((b) => b.type === "tool_use");
|
|
5400
|
-
if (hasToolUse2 && i + 1 < messages.length) {
|
|
5401
|
-
const next = messages[i + 1];
|
|
5402
|
-
if (next && next.role === "user" && typeof next.content !== "string" && Array.isArray(next.content) && next.content.some((b) => b.type === "tool_result")) {
|
|
5403
|
-
preserveStart = i + 1;
|
|
5404
|
-
}
|
|
5405
|
-
}
|
|
5406
|
-
}
|
|
5407
|
-
let saved = 0;
|
|
5408
|
-
let changed = false;
|
|
5409
|
-
const nextMessages = new Array(messages.length);
|
|
5410
|
-
for (let i = 0; i < messages.length; i++) {
|
|
5411
|
-
const msg = messages[i];
|
|
5412
|
-
if (i >= preserveStart) {
|
|
5413
|
-
nextMessages[i] = msg;
|
|
5414
|
-
continue;
|
|
5415
|
-
}
|
|
5416
|
-
if (!msg || !Array.isArray(msg.content)) {
|
|
5417
|
-
nextMessages[i] = msg;
|
|
5418
|
-
continue;
|
|
5419
|
-
}
|
|
5420
|
-
const newContent = msg.content.map((b) => {
|
|
5421
|
-
if (b.type !== "tool_result") return b;
|
|
5422
|
-
const tokens = estimateToolResultTokens(b.content);
|
|
5423
|
-
if (tokens < eliseThreshold) return b;
|
|
5424
|
-
saved += tokens;
|
|
5425
|
-
const elided = {
|
|
5426
|
-
type: "tool_result",
|
|
5427
|
-
tool_use_id: b.tool_use_id,
|
|
5428
|
-
content: `[elided: ~${tokens} tokens]`,
|
|
5429
|
-
is_error: b.is_error
|
|
5430
|
-
};
|
|
5431
|
-
return elided;
|
|
5432
|
-
});
|
|
5433
|
-
if (newContent.length === msg.content.length && newContent.every((b, idx) => b === msg.content[idx])) {
|
|
5434
|
-
nextMessages[i] = msg;
|
|
5435
|
-
} else {
|
|
5436
|
-
nextMessages[i] = { ...msg, content: newContent };
|
|
5437
|
-
changed = true;
|
|
5438
|
-
}
|
|
5439
|
-
}
|
|
5440
|
-
if (changed) ctx.state.replaceMessages(nextMessages);
|
|
5441
|
-
return saved;
|
|
5484
|
+
return estimateRequestTokens(ctx.messages, ctx.systemPrompt, ctx.tools ?? []).total;
|
|
5442
5485
|
}
|
|
5486
|
+
/**
|
|
5487
|
+
* Lossless rule-based collapse of ancient turns into a single digest message.
|
|
5488
|
+
*
|
|
5489
|
+
* Preserves ALL textual content of the collapsed range — user instructions,
|
|
5490
|
+
* assistant decisions/conclusions, and any prior digests (chained forward so
|
|
5491
|
+
* the digest stays lossless across repeated compactions). Only `tool_use` /
|
|
5492
|
+
* `tool_result` protocol blocks are dropped and replaced with a count marker;
|
|
5493
|
+
* their full payload already lives in the session log. No sub-LLM call.
|
|
5494
|
+
*
|
|
5495
|
+
* Returns the token savings and the digest text (for audit logging).
|
|
5496
|
+
*/
|
|
5443
5497
|
collapseAncientTurns(ctx, preserveK = this.preserveK) {
|
|
5444
5498
|
const messages = ctx.messages;
|
|
5445
5499
|
const cutTarget = Math.max(0, messages.length - preserveK * 2);
|
|
5446
|
-
if (cutTarget <= 0) return 0;
|
|
5500
|
+
if (cutTarget <= 0) return { saved: 0 };
|
|
5447
5501
|
let boundary = -1;
|
|
5448
5502
|
for (let i = cutTarget; i < messages.length; i++) {
|
|
5449
5503
|
const m = messages[i];
|
|
@@ -5453,34 +5507,20 @@ var HybridCompactor = class {
|
|
|
5453
5507
|
break;
|
|
5454
5508
|
}
|
|
5455
5509
|
}
|
|
5456
|
-
if (boundary <= 0) return 0;
|
|
5510
|
+
if (boundary <= 0) return { saved: 0 };
|
|
5457
5511
|
const removed = messages.slice(0, boundary);
|
|
5458
|
-
const removedTokens =
|
|
5459
|
-
const
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
{ role: "assistant", content: "Continuing from compacted context." }
|
|
5465
|
-
];
|
|
5512
|
+
const removedTokens = estimateMessages(removed);
|
|
5513
|
+
const digest = buildLosslessDigest(removed) || `${removed.length} earlier turns (no textual content; tool I/O omitted \u2014 see session log)`;
|
|
5514
|
+
const summaryMsg = {
|
|
5515
|
+
role: "system",
|
|
5516
|
+
content: `[prior_turns_digest: ${digest}]`
|
|
5517
|
+
};
|
|
5466
5518
|
const tail = ctx.messages.slice(boundary);
|
|
5467
|
-
ctx.state.replaceMessages([
|
|
5468
|
-
return
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
for (const m of messages) {
|
|
5473
|
-
if (typeof m.content === "string") {
|
|
5474
|
-
total += this.estimator(m.content);
|
|
5475
|
-
} else {
|
|
5476
|
-
for (const b of m.content) {
|
|
5477
|
-
if (b.type === "text") total += this.estimator(b.text);
|
|
5478
|
-
else if (b.type === "tool_use") total += estimateToolInputTokens(b.input);
|
|
5479
|
-
else if (b.type === "tool_result") total += estimateToolResultTokens(b.content);
|
|
5480
|
-
}
|
|
5481
|
-
}
|
|
5482
|
-
}
|
|
5483
|
-
return total;
|
|
5519
|
+
ctx.state.replaceMessages([summaryMsg, ...tail]);
|
|
5520
|
+
return {
|
|
5521
|
+
saved: Math.max(0, removedTokens - estimateMessages([summaryMsg])),
|
|
5522
|
+
digest
|
|
5523
|
+
};
|
|
5484
5524
|
}
|
|
5485
5525
|
};
|
|
5486
5526
|
function readContextWindowPolicy(ctx) {
|
|
@@ -5492,15 +5532,6 @@ function readContextWindowPolicy(ctx) {
|
|
|
5492
5532
|
}
|
|
5493
5533
|
return candidate;
|
|
5494
5534
|
}
|
|
5495
|
-
function hasTextContent(m) {
|
|
5496
|
-
if (typeof m.content === "string") return m.content.trim().length > 0;
|
|
5497
|
-
return m.content.some((b) => b.type === "text" && b.text.trim().length > 0);
|
|
5498
|
-
}
|
|
5499
|
-
|
|
5500
|
-
// src/types/blocks.ts
|
|
5501
|
-
function isTextBlock(b) {
|
|
5502
|
-
return b.type === "text";
|
|
5503
|
-
}
|
|
5504
5535
|
|
|
5505
5536
|
// src/execution/intelligent-compactor.ts
|
|
5506
5537
|
var IntelligentCompactor = class {
|
|
@@ -5525,23 +5556,25 @@ var IntelligentCompactor = class {
|
|
|
5525
5556
|
this.summarizerModel = opts.summarizerModel;
|
|
5526
5557
|
}
|
|
5527
5558
|
async compact(ctx, opts = {}) {
|
|
5528
|
-
const beforeTokens =
|
|
5559
|
+
const beforeTokens = estimateMessages(ctx.messages);
|
|
5529
5560
|
const beforeFull = this.estimateFullRequest(ctx);
|
|
5530
5561
|
const reductions = [];
|
|
5531
5562
|
const load = beforeFull / this.maxContext;
|
|
5532
5563
|
const aggressive = load >= this.hardThreshold ? true : opts.aggressive ?? load >= this.softThreshold;
|
|
5533
|
-
const saved1 = this.
|
|
5564
|
+
const saved1 = this.elide(ctx);
|
|
5534
5565
|
if (saved1 > 0) reductions.push({ phase: "elision", saved: saved1 });
|
|
5566
|
+
let collapsedDigest;
|
|
5535
5567
|
if (aggressive) {
|
|
5536
|
-
const
|
|
5537
|
-
if (
|
|
5568
|
+
const phase2 = await this.summarizeAncientTurns(ctx);
|
|
5569
|
+
if (phase2.saved > 0) reductions.push({ phase: "summary", saved: phase2.saved });
|
|
5570
|
+
collapsedDigest = phase2.digest;
|
|
5538
5571
|
} else if (load >= this.warnThreshold) {
|
|
5539
|
-
const saved2 = this.
|
|
5572
|
+
const saved2 = this.elide(ctx);
|
|
5540
5573
|
if (saved2 > 0) reductions.push({ phase: "elision", saved: saved2 });
|
|
5541
5574
|
}
|
|
5542
5575
|
const repaired = repairToolUseAdjacency(ctx.messages);
|
|
5543
5576
|
if (repaired.report.changed) ctx.state.replaceMessages(repaired.messages);
|
|
5544
|
-
const afterTokens =
|
|
5577
|
+
const afterTokens = estimateMessages(ctx.messages);
|
|
5545
5578
|
const afterFull = this.estimateFullRequest(ctx);
|
|
5546
5579
|
return {
|
|
5547
5580
|
before: beforeTokens,
|
|
@@ -5549,6 +5582,7 @@ var IntelligentCompactor = class {
|
|
|
5549
5582
|
fullRequestTokensBefore: beforeFull,
|
|
5550
5583
|
fullRequestTokensAfter: afterFull,
|
|
5551
5584
|
reductions,
|
|
5585
|
+
collapsedDigest,
|
|
5552
5586
|
repaired: repaired.report.changed ? {
|
|
5553
5587
|
removedToolUses: repaired.report.removedToolUses,
|
|
5554
5588
|
removedToolResults: repaired.report.removedToolResults,
|
|
@@ -5561,75 +5595,39 @@ var IntelligentCompactor = class {
|
|
|
5561
5595
|
* This is the accurate figure for context-window pressure monitoring.
|
|
5562
5596
|
*/
|
|
5563
5597
|
estimateFullRequest(ctx) {
|
|
5564
|
-
|
|
5565
|
-
|
|
5598
|
+
return estimateRequestTokens(ctx.messages, ctx.systemPrompt, ctx.tools ?? []).total;
|
|
5599
|
+
}
|
|
5600
|
+
/** Run shared tool-result elision and commit through ConversationState. */
|
|
5601
|
+
elide(ctx) {
|
|
5602
|
+
const result = eliseOldToolResults(ctx.messages, {
|
|
5603
|
+
preserveK: this.preserveK,
|
|
5604
|
+
eliseThreshold: this.eliseThreshold
|
|
5605
|
+
});
|
|
5606
|
+
if (result.changed) ctx.state.replaceMessages(result.messages);
|
|
5607
|
+
return result.saved;
|
|
5566
5608
|
}
|
|
5567
5609
|
async summarizeAncientTurns(ctx) {
|
|
5568
5610
|
const messages = ctx.messages;
|
|
5569
5611
|
const cutoff = Math.max(0, messages.length - this.preserveK * 2);
|
|
5570
|
-
if (cutoff <= 2) return 0;
|
|
5571
|
-
const boundary =
|
|
5572
|
-
if (boundary <= 1) return 0;
|
|
5612
|
+
if (cutoff <= 2) return { saved: 0 };
|
|
5613
|
+
const boundary = findSafeBoundary(messages, 0, cutoff);
|
|
5614
|
+
if (boundary <= 1) return { saved: 0 };
|
|
5573
5615
|
const toSummarize = messages.slice(0, boundary);
|
|
5574
|
-
const removedTokens =
|
|
5616
|
+
const removedTokens = estimateMessages(toSummarize);
|
|
5575
5617
|
let summaryText;
|
|
5576
5618
|
try {
|
|
5577
5619
|
summaryText = await this.callSummarizer(toSummarize, ctx);
|
|
5578
5620
|
} catch {
|
|
5579
|
-
|
|
5580
|
-
for (const m of toSummarize) {
|
|
5581
|
-
if (m.role === "system") continue;
|
|
5582
|
-
if (typeof m.content === "string") {
|
|
5583
|
-
preservedMessages.push(m);
|
|
5584
|
-
} else if (Array.isArray(m.content)) {
|
|
5585
|
-
const textParts = m.content.filter(isTextBlock);
|
|
5586
|
-
const toolParts = m.content.filter((b) => b.type === "tool_use" || b.type === "tool_result");
|
|
5587
|
-
const next = {
|
|
5588
|
-
role: m.role,
|
|
5589
|
-
content: [
|
|
5590
|
-
...textParts,
|
|
5591
|
-
...toolParts.length > 0 ? [{ type: "text", text: `[${toolParts.length} tool call(s) omitted]` }] : []
|
|
5592
|
-
]
|
|
5593
|
-
};
|
|
5594
|
-
preservedMessages.push(next);
|
|
5595
|
-
}
|
|
5596
|
-
}
|
|
5597
|
-
summaryText = preservedMessages.map((m) => `[${m.role}]: ${typeof m.content === "string" ? m.content : m.content.filter(isTextBlock).map((b) => b.text).join(" ")}`).join("\n");
|
|
5598
|
-
if (!summaryText) summaryText = `${toSummarize.length} earlier turns (semantic content preserved)`;
|
|
5621
|
+
summaryText = buildLosslessDigest(toSummarize) || `${toSummarize.length} earlier turns (semantic content preserved)`;
|
|
5599
5622
|
}
|
|
5600
5623
|
const summaryMsg = {
|
|
5601
5624
|
role: "system",
|
|
5602
5625
|
content: `[prior_turns_summary: ${summaryText}]`
|
|
5603
5626
|
};
|
|
5604
|
-
const summaryTokens =
|
|
5627
|
+
const summaryTokens = estimateMessages([summaryMsg]);
|
|
5605
5628
|
const tail = ctx.messages.slice(boundary);
|
|
5606
5629
|
ctx.state.replaceMessages([summaryMsg, ...tail]);
|
|
5607
|
-
return Math.max(0, removedTokens - summaryTokens);
|
|
5608
|
-
}
|
|
5609
|
-
findSafeBoundary(messages, from, to) {
|
|
5610
|
-
for (let i = to; i >= from; i--) {
|
|
5611
|
-
const m = messages[i];
|
|
5612
|
-
if (!m) continue;
|
|
5613
|
-
if (m.role === "user" && this.hasTextContent(m)) {
|
|
5614
|
-
return this.findExchangeStart(messages, i);
|
|
5615
|
-
}
|
|
5616
|
-
}
|
|
5617
|
-
return -1;
|
|
5618
|
-
}
|
|
5619
|
-
findExchangeStart(messages, userIndex) {
|
|
5620
|
-
for (let i = userIndex - 1; i >= 0; i--) {
|
|
5621
|
-
const m = messages[i];
|
|
5622
|
-
if (!m) continue;
|
|
5623
|
-
if (m.role === "assistant") {
|
|
5624
|
-
const hasToolUse2 = Array.isArray(m.content) ? m.content.some((b) => b.type === "tool_use") : false;
|
|
5625
|
-
if (!hasToolUse2) {
|
|
5626
|
-
return i + 1;
|
|
5627
|
-
}
|
|
5628
|
-
} else if (m.role !== "user") ; else {
|
|
5629
|
-
return i;
|
|
5630
|
-
}
|
|
5631
|
-
}
|
|
5632
|
-
return 0;
|
|
5630
|
+
return { saved: Math.max(0, removedTokens - summaryTokens), digest: summaryText };
|
|
5633
5631
|
}
|
|
5634
5632
|
async callSummarizer(messages, ctx) {
|
|
5635
5633
|
const prompt = [
|
|
@@ -5664,86 +5662,6 @@ var IntelligentCompactor = class {
|
|
|
5664
5662
|
}
|
|
5665
5663
|
return [{ type: "text", text: lines.join("\n") }];
|
|
5666
5664
|
}
|
|
5667
|
-
lightweightCompact(ctx) {
|
|
5668
|
-
return this.eliseOldToolResults(ctx);
|
|
5669
|
-
}
|
|
5670
|
-
eliseOldToolResults(ctx) {
|
|
5671
|
-
const messages = ctx.messages;
|
|
5672
|
-
let pairCount = 0;
|
|
5673
|
-
let preserveStart = messages.length;
|
|
5674
|
-
for (let i = messages.length - 1; i >= 0 && pairCount < this.preserveK; i--) {
|
|
5675
|
-
const m = messages[i];
|
|
5676
|
-
if (!m) continue;
|
|
5677
|
-
if (m.role === "user" || m.role === "assistant") {
|
|
5678
|
-
pairCount++;
|
|
5679
|
-
preserveStart = i;
|
|
5680
|
-
}
|
|
5681
|
-
}
|
|
5682
|
-
for (let i = preserveStart; i < messages.length; i++) {
|
|
5683
|
-
const m = messages[i];
|
|
5684
|
-
if (!m || typeof m.content === "string" || !Array.isArray(m.content)) continue;
|
|
5685
|
-
const hasToolUse2 = m.content.some((b) => b.type === "tool_use");
|
|
5686
|
-
if (hasToolUse2 && i + 1 < messages.length) {
|
|
5687
|
-
const next = messages[i + 1];
|
|
5688
|
-
if (next && next.role === "user" && typeof next.content !== "string" && Array.isArray(next.content) && next.content.some((b) => b.type === "tool_result")) {
|
|
5689
|
-
preserveStart = i + 1;
|
|
5690
|
-
}
|
|
5691
|
-
}
|
|
5692
|
-
}
|
|
5693
|
-
let saved = 0;
|
|
5694
|
-
let changed = false;
|
|
5695
|
-
const nextMessages = new Array(messages.length);
|
|
5696
|
-
for (let i = 0; i < messages.length; i++) {
|
|
5697
|
-
const msg = messages[i];
|
|
5698
|
-
if (i >= preserveStart) {
|
|
5699
|
-
nextMessages[i] = msg;
|
|
5700
|
-
continue;
|
|
5701
|
-
}
|
|
5702
|
-
if (!msg || !Array.isArray(msg.content)) {
|
|
5703
|
-
nextMessages[i] = msg;
|
|
5704
|
-
continue;
|
|
5705
|
-
}
|
|
5706
|
-
const newContent = msg.content.map((b) => {
|
|
5707
|
-
if (b.type !== "tool_result") return b;
|
|
5708
|
-
const tokens = estimateToolResultTokens(b.content);
|
|
5709
|
-
if (tokens < this.eliseThreshold) return b;
|
|
5710
|
-
saved += tokens;
|
|
5711
|
-
return {
|
|
5712
|
-
type: "tool_result",
|
|
5713
|
-
tool_use_id: b.tool_use_id,
|
|
5714
|
-
content: `[elided: ~${tokens} tokens]`,
|
|
5715
|
-
is_error: b.is_error
|
|
5716
|
-
};
|
|
5717
|
-
});
|
|
5718
|
-
if (newContent.length === msg.content.length && newContent.every((b, idx) => b === msg.content[idx])) {
|
|
5719
|
-
nextMessages[i] = msg;
|
|
5720
|
-
} else {
|
|
5721
|
-
nextMessages[i] = { ...msg, content: newContent };
|
|
5722
|
-
changed = true;
|
|
5723
|
-
}
|
|
5724
|
-
}
|
|
5725
|
-
if (changed) ctx.state.replaceMessages(nextMessages);
|
|
5726
|
-
return saved;
|
|
5727
|
-
}
|
|
5728
|
-
hasTextContent(m) {
|
|
5729
|
-
if (typeof m.content === "string") return m.content.trim().length > 0;
|
|
5730
|
-
return m.content.some((b) => b.type === "text" && b.text.trim().length > 0);
|
|
5731
|
-
}
|
|
5732
|
-
estimateTokens(messages) {
|
|
5733
|
-
let total = 0;
|
|
5734
|
-
for (const m of messages) {
|
|
5735
|
-
if (typeof m.content === "string") {
|
|
5736
|
-
total += estimateTextTokens(m.content);
|
|
5737
|
-
} else {
|
|
5738
|
-
for (const b of m.content) {
|
|
5739
|
-
if (b.type === "text") total += estimateTextTokens(b.text);
|
|
5740
|
-
else if (b.type === "tool_use") total += estimateToolInputTokens(b.input);
|
|
5741
|
-
else if (b.type === "tool_result") total += estimateToolResultTokens(b.content);
|
|
5742
|
-
}
|
|
5743
|
-
}
|
|
5744
|
-
}
|
|
5745
|
-
return total;
|
|
5746
|
-
}
|
|
5747
5665
|
};
|
|
5748
5666
|
|
|
5749
5667
|
// src/models/llm-selector.ts
|
|
@@ -5769,20 +5687,6 @@ Rules:
|
|
|
5769
5687
|
- If unsure, keep rather than collapse (errors are more costly than waste)
|
|
5770
5688
|
|
|
5771
5689
|
Return ONLY the JSON object, no markdown, no explanation outside the JSON.`;
|
|
5772
|
-
function estimateTokens(messages) {
|
|
5773
|
-
let total = 0;
|
|
5774
|
-
for (const m of messages) {
|
|
5775
|
-
if (typeof m.content === "string") {
|
|
5776
|
-
total += Math.ceil(m.content.length / 4);
|
|
5777
|
-
} else if (Array.isArray(m.content)) {
|
|
5778
|
-
for (const b of m.content) {
|
|
5779
|
-
if (b.type === "text") total += Math.ceil(b.text.length / 4);
|
|
5780
|
-
else total += Math.ceil(JSON.stringify(b).length / 4);
|
|
5781
|
-
}
|
|
5782
|
-
}
|
|
5783
|
-
}
|
|
5784
|
-
return total;
|
|
5785
|
-
}
|
|
5786
5690
|
function formatMessages(messages, maxChars = 8e3) {
|
|
5787
5691
|
const lines = [];
|
|
5788
5692
|
let used = 0;
|
|
@@ -5821,7 +5725,7 @@ var LLMSelector = class {
|
|
|
5821
5725
|
async select(messages, maxToKeep) {
|
|
5822
5726
|
const effectiveBudget = Math.min(maxToKeep, this.maxContextTokens);
|
|
5823
5727
|
const historyText = formatMessages(messages);
|
|
5824
|
-
const totalTokens =
|
|
5728
|
+
const totalTokens = estimateMessageTokens(messages);
|
|
5825
5729
|
const systemText = `${this.systemPrompt}
|
|
5826
5730
|
|
|
5827
5731
|
Conversation (${messages.length} messages, ~${totalTokens} tokens, budget: ${effectiveBudget}):
|
|
@@ -5853,10 +5757,7 @@ IMPORTANT: Total conversation (${totalTokens} tokens) exceeds budget (${effectiv
|
|
|
5853
5757
|
let startIdx = 0;
|
|
5854
5758
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
5855
5759
|
const m = expectDefined(messages[i]);
|
|
5856
|
-
const cost =
|
|
5857
|
-
(acc, b) => acc + (b.type === "text" ? Math.ceil(b.text.length / 4) : Math.ceil(JSON.stringify(b).length / 4)),
|
|
5858
|
-
0
|
|
5859
|
-
);
|
|
5760
|
+
const cost = estimateMessageTokens([m]);
|
|
5860
5761
|
if (tokenCount + cost <= budget) {
|
|
5861
5762
|
tokenCount += cost;
|
|
5862
5763
|
} else {
|
|
@@ -6089,52 +5990,12 @@ Summarize the following message range:`;
|
|
|
6089
5990
|
return Math.floor(this.maxContext * 0.75);
|
|
6090
5991
|
}
|
|
6091
5992
|
eliseOldToolResults(ctx) {
|
|
6092
|
-
const
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
if (m.role === "user" || m.role === "assistant") {
|
|
6099
|
-
pairCount++;
|
|
6100
|
-
preserveStart = i;
|
|
6101
|
-
}
|
|
6102
|
-
}
|
|
6103
|
-
let saved = 0;
|
|
6104
|
-
let changed = false;
|
|
6105
|
-
const nextMessages = new Array(messages.length);
|
|
6106
|
-
for (let i = 0; i < messages.length; i++) {
|
|
6107
|
-
const msg = messages[i];
|
|
6108
|
-
if (i >= preserveStart) {
|
|
6109
|
-
nextMessages[i] = msg;
|
|
6110
|
-
continue;
|
|
6111
|
-
}
|
|
6112
|
-
if (!msg || !Array.isArray(msg.content)) {
|
|
6113
|
-
nextMessages[i] = msg;
|
|
6114
|
-
continue;
|
|
6115
|
-
}
|
|
6116
|
-
const newContent = msg.content.map((b) => {
|
|
6117
|
-
if (b.type !== "tool_result") return b;
|
|
6118
|
-
const text = typeof b.content === "string" ? b.content : JSON.stringify(b.content);
|
|
6119
|
-
const tokens = this.roughTokenEstimate(text);
|
|
6120
|
-
if (tokens < this.eliseThreshold) return b;
|
|
6121
|
-
saved += tokens;
|
|
6122
|
-
return {
|
|
6123
|
-
type: "tool_result",
|
|
6124
|
-
tool_use_id: b.tool_use_id,
|
|
6125
|
-
content: `[elided: ~${tokens} tokens]`,
|
|
6126
|
-
is_error: b.is_error
|
|
6127
|
-
};
|
|
6128
|
-
});
|
|
6129
|
-
if (newContent.every((b, idx) => b === msg.content[idx])) {
|
|
6130
|
-
nextMessages[i] = msg;
|
|
6131
|
-
} else {
|
|
6132
|
-
nextMessages[i] = { ...msg, content: newContent };
|
|
6133
|
-
changed = true;
|
|
6134
|
-
}
|
|
6135
|
-
}
|
|
6136
|
-
if (changed) ctx.state.replaceMessages(nextMessages);
|
|
6137
|
-
return saved;
|
|
5993
|
+
const result = eliseOldToolResults(ctx.messages, {
|
|
5994
|
+
preserveK: this.preserveK,
|
|
5995
|
+
eliseThreshold: this.eliseThreshold
|
|
5996
|
+
});
|
|
5997
|
+
if (result.changed) ctx.state.replaceMessages(result.messages);
|
|
5998
|
+
return result.saved;
|
|
6138
5999
|
}
|
|
6139
6000
|
hasTextContent(m) {
|
|
6140
6001
|
if (typeof m.content === "string") return m.content.trim().length > 0;
|
|
@@ -6164,8 +6025,80 @@ Summarize the following message range:`;
|
|
|
6164
6025
|
}
|
|
6165
6026
|
};
|
|
6166
6027
|
|
|
6028
|
+
// src/execution/strategy-compactor.ts
|
|
6029
|
+
function createStrategyCompactor(opts = {}) {
|
|
6030
|
+
const requested = opts.strategy ?? (opts.llmSelector ? "selective" : "hybrid");
|
|
6031
|
+
const strategy = requested;
|
|
6032
|
+
if (strategy === "intelligent" || strategy === "selective") {
|
|
6033
|
+
return new ProviderBackedCompactor(strategy, opts);
|
|
6034
|
+
}
|
|
6035
|
+
return new HybridCompactor({
|
|
6036
|
+
preserveK: opts.preserveK,
|
|
6037
|
+
eliseThreshold: opts.eliseThreshold
|
|
6038
|
+
});
|
|
6039
|
+
}
|
|
6040
|
+
var ProviderBackedCompactor = class {
|
|
6041
|
+
constructor(strategy, opts) {
|
|
6042
|
+
this.strategy = strategy;
|
|
6043
|
+
this.opts = opts;
|
|
6044
|
+
}
|
|
6045
|
+
strategy;
|
|
6046
|
+
opts;
|
|
6047
|
+
async compact(ctx, compactOpts = {}) {
|
|
6048
|
+
return this.resolveInner(ctx).compact(ctx, compactOpts);
|
|
6049
|
+
}
|
|
6050
|
+
/**
|
|
6051
|
+
* Construct the concrete compactor for this run. Rebuilt per call (cheap, no
|
|
6052
|
+
* I/O) so a model switch — which changes `ctx.provider.capabilities.maxContext`
|
|
6053
|
+
* — is always reflected. Reads the active ContextWindowPolicy from `ctx.meta`
|
|
6054
|
+
* so the LLM compactors honor the same thresholds/preserveK as the policy.
|
|
6055
|
+
*/
|
|
6056
|
+
resolveInner(ctx) {
|
|
6057
|
+
const provider = ctx.provider;
|
|
6058
|
+
if (!provider) {
|
|
6059
|
+
return new HybridCompactor({
|
|
6060
|
+
preserveK: this.opts.preserveK,
|
|
6061
|
+
eliseThreshold: this.opts.eliseThreshold
|
|
6062
|
+
});
|
|
6063
|
+
}
|
|
6064
|
+
const policy = readPolicy(ctx);
|
|
6065
|
+
const maxContext = provider.capabilities?.maxContext || void 0;
|
|
6066
|
+
const thresholds = policy?.thresholds;
|
|
6067
|
+
const common = {
|
|
6068
|
+
provider,
|
|
6069
|
+
maxContext,
|
|
6070
|
+
preserveK: this.opts.preserveK ?? policy?.preserveK,
|
|
6071
|
+
eliseThreshold: this.opts.eliseThreshold ?? policy?.eliseThreshold,
|
|
6072
|
+
...thresholds ? { warnThreshold: thresholds.warn, softThreshold: thresholds.soft, hardThreshold: thresholds.hard } : {}
|
|
6073
|
+
};
|
|
6074
|
+
if (this.strategy === "selective") {
|
|
6075
|
+
return new SelectiveCompactor({
|
|
6076
|
+
...common,
|
|
6077
|
+
selectorModel: this.opts.summarizerModel,
|
|
6078
|
+
summarizerModel: this.opts.summarizerModel
|
|
6079
|
+
});
|
|
6080
|
+
}
|
|
6081
|
+
return new IntelligentCompactor({
|
|
6082
|
+
...common,
|
|
6083
|
+
summarizerModel: this.opts.summarizerModel
|
|
6084
|
+
});
|
|
6085
|
+
}
|
|
6086
|
+
};
|
|
6087
|
+
function readPolicy(ctx) {
|
|
6088
|
+
const policy = ctx.meta?.["contextWindowPolicy"];
|
|
6089
|
+
if (!policy || typeof policy !== "object") return null;
|
|
6090
|
+
const candidate = policy;
|
|
6091
|
+
if (typeof candidate.preserveK !== "number" || !candidate.thresholds) return null;
|
|
6092
|
+
return candidate;
|
|
6093
|
+
}
|
|
6094
|
+
|
|
6167
6095
|
// src/execution/auto-compaction-middleware.ts
|
|
6168
6096
|
var LEVEL_RANK2 = { warn: 0, soft: 1, hard: 2 };
|
|
6097
|
+
var MAX_DIGEST_LOG_CHARS = 4e3;
|
|
6098
|
+
function truncateDigest(digest) {
|
|
6099
|
+
if (digest.length <= MAX_DIGEST_LOG_CHARS) return digest;
|
|
6100
|
+
return `${digest.slice(0, MAX_DIGEST_LOG_CHARS)}\u2026 [+${digest.length - MAX_DIGEST_LOG_CHARS} chars; full turns in session log]`;
|
|
6101
|
+
}
|
|
6169
6102
|
var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
|
|
6170
6103
|
name = "AutoCompaction";
|
|
6171
6104
|
compactor;
|
|
@@ -6226,7 +6159,12 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
|
|
|
6226
6159
|
}
|
|
6227
6160
|
handler() {
|
|
6228
6161
|
return async (ctx, next) => {
|
|
6229
|
-
const tokens = this._estimator ? this._estimator(ctx) : estimateRequestTokensCalibrated(
|
|
6162
|
+
const tokens = this._estimator ? this._estimator(ctx) : estimateRequestTokensCalibrated(
|
|
6163
|
+
ctx.messages,
|
|
6164
|
+
ctx.systemPrompt,
|
|
6165
|
+
ctx.tools ?? [],
|
|
6166
|
+
`${ctx.provider?.id ?? "unknown"}/${ctx.model}`
|
|
6167
|
+
).total;
|
|
6230
6168
|
const load = tokens / this._maxContext;
|
|
6231
6169
|
const policy = this.policyProvider?.(ctx);
|
|
6232
6170
|
const thresholds = policy?.thresholds ?? {
|
|
@@ -6288,7 +6226,11 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
|
|
|
6288
6226
|
after: report.after,
|
|
6289
6227
|
level: pressure.level,
|
|
6290
6228
|
aggressive,
|
|
6291
|
-
reductions: report.reductions?.map((r) => ({ phase: r.phase, saved: r.saved }))
|
|
6229
|
+
reductions: report.reductions?.map((r) => ({ phase: r.phase, saved: r.saved })),
|
|
6230
|
+
// Record what was collapsed so the audit trail shows the preserved
|
|
6231
|
+
// content, not just token counts. Bounded to keep the log line small;
|
|
6232
|
+
// the full original turns are already in the session JSONL.
|
|
6233
|
+
...report.collapsedDigest ? { digest: truncateDigest(report.collapsedDigest) } : {}
|
|
6292
6234
|
});
|
|
6293
6235
|
ctx.clearFileTracking();
|
|
6294
6236
|
} catch (err) {
|
|
@@ -6323,10 +6265,10 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
|
|
|
6323
6265
|
// src/utils/json-schema-validate.ts
|
|
6324
6266
|
function validateAgainstSchema(value, schema) {
|
|
6325
6267
|
const errors = [];
|
|
6326
|
-
|
|
6268
|
+
walk2(value, schema, "", errors);
|
|
6327
6269
|
return { ok: errors.length === 0, errors };
|
|
6328
6270
|
}
|
|
6329
|
-
function
|
|
6271
|
+
function walk2(value, schema, path19, errors) {
|
|
6330
6272
|
if (schema.enum !== void 0) {
|
|
6331
6273
|
if (!schema.enum.some((e) => deepEqual(e, value))) {
|
|
6332
6274
|
errors.push({
|
|
@@ -6355,13 +6297,13 @@ function walk3(value, schema, path19, errors) {
|
|
|
6355
6297
|
if (schema.properties) {
|
|
6356
6298
|
for (const [key, subSchema] of Object.entries(schema.properties)) {
|
|
6357
6299
|
if (key in obj) {
|
|
6358
|
-
|
|
6300
|
+
walk2(obj[key], subSchema, joinPath(path19, key), errors);
|
|
6359
6301
|
}
|
|
6360
6302
|
}
|
|
6361
6303
|
}
|
|
6362
6304
|
}
|
|
6363
6305
|
if (schema.type === "array" && Array.isArray(value) && schema.items) {
|
|
6364
|
-
value.forEach((item, i) =>
|
|
6306
|
+
value.forEach((item, i) => walk2(item, schema.items, `${path19}[${i}]`, errors));
|
|
6365
6307
|
}
|
|
6366
6308
|
}
|
|
6367
6309
|
function checkType(value, type) {
|
|
@@ -6875,6 +6817,13 @@ function extractMalformedRaw(input) {
|
|
|
6875
6817
|
}
|
|
6876
6818
|
}
|
|
6877
6819
|
|
|
6820
|
+
// src/utils/assert-never.ts
|
|
6821
|
+
function assertNever(x, message) {
|
|
6822
|
+
throw new Error(
|
|
6823
|
+
`Unhandled case: ${JSON.stringify(x)}`
|
|
6824
|
+
);
|
|
6825
|
+
}
|
|
6826
|
+
|
|
6878
6827
|
// src/execution/autonomous-runner.ts
|
|
6879
6828
|
var DoneConditionChecker = class {
|
|
6880
6829
|
constructor(condition) {
|
|
@@ -6928,6 +6877,12 @@ var DoneConditionChecker = class {
|
|
|
6928
6877
|
return { done: true, reason: `max tool calls (${this.condition.maxToolCalls}) reached`, ...state };
|
|
6929
6878
|
}
|
|
6930
6879
|
break;
|
|
6880
|
+
case "all_tasks_done":
|
|
6881
|
+
break;
|
|
6882
|
+
case "custom":
|
|
6883
|
+
break;
|
|
6884
|
+
default:
|
|
6885
|
+
return assertNever(this.condition.type);
|
|
6931
6886
|
}
|
|
6932
6887
|
return { done: false, iterations: state.iterations, toolCalls: state.toolCalls };
|
|
6933
6888
|
}
|
|
@@ -7150,9 +7105,9 @@ function parseProgressFromText(text) {
|
|
|
7150
7105
|
const re = /\[progress:\s*(\d{1,3})%\]\s*(?:[—\-]\s*(.+))?/i;
|
|
7151
7106
|
const m = text.match(re);
|
|
7152
7107
|
if (!m) return null;
|
|
7153
|
-
const progress = Math.min(100, Math.max(0, Number.parseInt(m[1], 10)));
|
|
7108
|
+
const progress = Math.min(100, Math.max(0, Number.parseInt(m[1] ?? "0", 10)));
|
|
7154
7109
|
const note = m[2]?.trim() || void 0;
|
|
7155
|
-
return { progress, note };
|
|
7110
|
+
return note === void 0 ? { progress } : { progress, note };
|
|
7156
7111
|
}
|
|
7157
7112
|
function recordProgress(goal, progress, note) {
|
|
7158
7113
|
const clamped = Math.min(100, Math.max(0, progress));
|
|
@@ -7750,6 +7705,7 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
7750
7705
|
note: note.slice(0, 240)
|
|
7751
7706
|
});
|
|
7752
7707
|
await saveGoal(this.goalPath, withEntry);
|
|
7708
|
+
this.opts.onEternalStop?.();
|
|
7753
7709
|
}
|
|
7754
7710
|
/**
|
|
7755
7711
|
* Manually clear the goal — equivalent to `/goal clear` typed by the user.
|
|
@@ -12886,7 +12842,7 @@ async function expandGlob(pattern) {
|
|
|
12886
12842
|
const abs = isAbsolute(pattern);
|
|
12887
12843
|
const base = abs ? baseDir(pattern) : baseDir(pattern);
|
|
12888
12844
|
const relPat = base === "." ? pattern : pattern.slice(base.length + 1);
|
|
12889
|
-
async function
|
|
12845
|
+
async function walk3(dir, pat) {
|
|
12890
12846
|
let entries;
|
|
12891
12847
|
try {
|
|
12892
12848
|
entries = await fsp.readdir(dir);
|
|
@@ -12907,12 +12863,12 @@ async function expandGlob(pattern) {
|
|
|
12907
12863
|
const before = pat.slice(0, firstGlob);
|
|
12908
12864
|
const rest = pat.slice(firstGlob);
|
|
12909
12865
|
if (before.endsWith("**")) {
|
|
12910
|
-
await
|
|
12866
|
+
await walk3(dir, rest);
|
|
12911
12867
|
for (const e of entries) {
|
|
12912
12868
|
const full = `${dir}${SEP}${e}`;
|
|
12913
12869
|
try {
|
|
12914
12870
|
const stat6 = await fsp.stat(full);
|
|
12915
|
-
if (stat6.isDirectory()) await
|
|
12871
|
+
if (stat6.isDirectory()) await walk3(full, rest);
|
|
12916
12872
|
} catch {
|
|
12917
12873
|
}
|
|
12918
12874
|
}
|
|
@@ -12930,13 +12886,13 @@ async function expandGlob(pattern) {
|
|
|
12930
12886
|
const full = `${dir}${SEP}${seg}`;
|
|
12931
12887
|
try {
|
|
12932
12888
|
const stat6 = await fsp.stat(full);
|
|
12933
|
-
if (stat6.isDirectory()) await
|
|
12889
|
+
if (stat6.isDirectory()) await walk3(full, rest);
|
|
12934
12890
|
} catch {
|
|
12935
12891
|
}
|
|
12936
12892
|
}
|
|
12937
12893
|
}
|
|
12938
12894
|
}
|
|
12939
|
-
await
|
|
12895
|
+
await walk3(base === "." ? "." : base, relPat);
|
|
12940
12896
|
return [...results];
|
|
12941
12897
|
}
|
|
12942
12898
|
|
|
@@ -14532,7 +14488,10 @@ var Director = class _Director {
|
|
|
14532
14488
|
workCompleteFlag = false;
|
|
14533
14489
|
/** Pending /btw notes stashed by the leader agent (see setLeaderBtwNote). */
|
|
14534
14490
|
_leaderBtwNotes = [];
|
|
14535
|
-
/** Active collab sessions tracked by sessionId (see spawnCollab).
|
|
14491
|
+
/** Active collab sessions tracked by sessionId (see spawnCollab).
|
|
14492
|
+
* The tuple holds the session and its Director-registered listener unsubs.
|
|
14493
|
+
* Calling the unsubs on cancel/premature-cleanup prevents listener accumulation
|
|
14494
|
+
* on CollabSession (EventEmitter) across many spawnCollab() calls. */
|
|
14536
14495
|
_activeCollabSessions = /* @__PURE__ */ new Map();
|
|
14537
14496
|
/** Prevents large `ask_subagent` answers from bloating the leader's context window. */
|
|
14538
14497
|
largeAnswerStore;
|
|
@@ -14862,11 +14821,13 @@ var Director = class _Director {
|
|
|
14862
14821
|
* The CollabDebugReport will reflect 'cancelled' disposition when awaited.
|
|
14863
14822
|
*/
|
|
14864
14823
|
cancelCollabSession(sessionId, reason = "Director cancelled") {
|
|
14865
|
-
const
|
|
14866
|
-
if (!
|
|
14867
|
-
|
|
14868
|
-
|
|
14869
|
-
|
|
14824
|
+
const entry = this._activeCollabSessions.get(sessionId);
|
|
14825
|
+
if (!entry || entry.session.isCancelled()) return;
|
|
14826
|
+
for (const unsub of entry.unsubs) unsub();
|
|
14827
|
+
entry.session.cancel(reason);
|
|
14828
|
+
for (const [_role, subagentId] of entry.session.getSubagentIds()) {
|
|
14829
|
+
this.coordinator.stop(subagentId).catch((err) => {
|
|
14830
|
+
console.debug(`[director] stop subagent ${subagentId} failed (may have already completed): ${err}`);
|
|
14870
14831
|
});
|
|
14871
14832
|
}
|
|
14872
14833
|
}
|
|
@@ -15514,9 +15475,15 @@ var Director = class _Director {
|
|
|
15514
15475
|
return options.onBudgetWarning?.(alert) ?? "ignore";
|
|
15515
15476
|
}
|
|
15516
15477
|
});
|
|
15517
|
-
this._activeCollabSessions.
|
|
15518
|
-
|
|
15519
|
-
session.on("session.
|
|
15478
|
+
const doneHandler = () => this._activeCollabSessions.delete(session.sessionId);
|
|
15479
|
+
const errorHandler = () => this._activeCollabSessions.delete(session.sessionId);
|
|
15480
|
+
session.on("session.done", doneHandler);
|
|
15481
|
+
session.on("session.error", errorHandler);
|
|
15482
|
+
const unsubs = [
|
|
15483
|
+
() => session.off("session.done", doneHandler),
|
|
15484
|
+
() => session.off("session.error", errorHandler)
|
|
15485
|
+
];
|
|
15486
|
+
this._activeCollabSessions.set(session.sessionId, { session, unsubs });
|
|
15520
15487
|
return session.start();
|
|
15521
15488
|
}
|
|
15522
15489
|
/**
|
|
@@ -18938,6 +18905,8 @@ var SpecVersioning = class {
|
|
|
18938
18905
|
switch (type) {
|
|
18939
18906
|
case "functional":
|
|
18940
18907
|
return "feature";
|
|
18908
|
+
case "non-functional":
|
|
18909
|
+
return "feature";
|
|
18941
18910
|
case "security":
|
|
18942
18911
|
return "feature";
|
|
18943
18912
|
case "performance":
|
|
@@ -18945,7 +18914,7 @@ var SpecVersioning = class {
|
|
|
18945
18914
|
case "ux":
|
|
18946
18915
|
return "feature";
|
|
18947
18916
|
default:
|
|
18948
|
-
return
|
|
18917
|
+
return assertNever(type);
|
|
18949
18918
|
}
|
|
18950
18919
|
}
|
|
18951
18920
|
};
|
|
@@ -20096,27 +20065,13 @@ function startOtlpTraceExporter(opts) {
|
|
|
20096
20065
|
// src/infrastructure/context-manager.ts
|
|
20097
20066
|
var CONTEXT_MANAGER_TOOL_NAME = "context_manager";
|
|
20098
20067
|
function roughEstimate(messages) {
|
|
20099
|
-
|
|
20100
|
-
for (const m of messages) {
|
|
20101
|
-
if (typeof m.content === "string") {
|
|
20102
|
-
total += Math.ceil(m.content.length / 4);
|
|
20103
|
-
} else if (Array.isArray(m.content)) {
|
|
20104
|
-
for (const b of m.content) {
|
|
20105
|
-
if (b.type === "text") total += Math.ceil(b.text.length / 4);
|
|
20106
|
-
else if (b.type === "tool_use" || b.type === "tool_result") {
|
|
20107
|
-
total += Math.ceil(JSON.stringify(b).length / 4);
|
|
20108
|
-
}
|
|
20109
|
-
}
|
|
20110
|
-
}
|
|
20111
|
-
}
|
|
20112
|
-
return total;
|
|
20068
|
+
return estimateMessageTokens(messages);
|
|
20113
20069
|
}
|
|
20114
20070
|
function createContextManagerTool(opts = {}) {
|
|
20115
20071
|
const minCompactThreshold = opts.minCompactThreshold ?? 0;
|
|
20116
20072
|
const noopRetryDeltaTokens = opts.noopRetryDeltaTokens ?? 2e3;
|
|
20117
|
-
const
|
|
20073
|
+
const configuredMaxContext = opts.maxContext;
|
|
20118
20074
|
const compactThresholdFraction = opts.compactThresholdFraction ?? 0.5;
|
|
20119
|
-
const effectiveThreshold = minCompactThreshold > 0 ? minCompactThreshold : Math.floor(maxContext * compactThresholdFraction);
|
|
20120
20075
|
let lastNoopTokens = 0;
|
|
20121
20076
|
return {
|
|
20122
20077
|
name: CONTEXT_MANAGER_TOOL_NAME,
|
|
@@ -20210,6 +20165,8 @@ function createContextManagerTool(opts = {}) {
|
|
|
20210
20165
|
}
|
|
20211
20166
|
const fullEstimate = input.systemPrompt != null && Array.isArray(input.tools) ? estimateRequestTokens(messages, input.systemPrompt, input.tools) : { total: beforeTokens};
|
|
20212
20167
|
const currentTokens = fullEstimate.total;
|
|
20168
|
+
const runtimeMaxContext = configuredMaxContext ?? ctx.provider?.capabilities?.maxContext ?? 128e3;
|
|
20169
|
+
const runtimeThreshold = minCompactThreshold > 0 ? minCompactThreshold : Math.floor(runtimeMaxContext * compactThresholdFraction);
|
|
20213
20170
|
if (lastNoopTokens > 0) {
|
|
20214
20171
|
const delta = currentTokens - lastNoopTokens;
|
|
20215
20172
|
if (delta < noopRetryDeltaTokens) {
|
|
@@ -20222,13 +20179,13 @@ function createContextManagerTool(opts = {}) {
|
|
|
20222
20179
|
};
|
|
20223
20180
|
}
|
|
20224
20181
|
}
|
|
20225
|
-
if (currentTokens <
|
|
20182
|
+
if (runtimeThreshold > 0 && currentTokens < runtimeThreshold) {
|
|
20226
20183
|
return {
|
|
20227
20184
|
action: "compact",
|
|
20228
20185
|
beforeTokens,
|
|
20229
20186
|
afterTokens: beforeTokens,
|
|
20230
20187
|
messageCount: messages.length,
|
|
20231
|
-
notes: `Context tokens (${currentTokens}) below compact threshold (${
|
|
20188
|
+
notes: `Context tokens (${currentTokens}) below compact threshold (${runtimeThreshold}, based on provider maxContext ${runtimeMaxContext}). Skipping.`
|
|
20232
20189
|
};
|
|
20233
20190
|
}
|
|
20234
20191
|
const report = await opts.compactor.compact(ctx);
|
|
@@ -20481,6 +20438,6 @@ var allServers = () => ({
|
|
|
20481
20438
|
"minimax-vision": { ...miniMaxVisionServer(), enabled: false }
|
|
20482
20439
|
});
|
|
20483
20440
|
|
|
20484
|
-
export { AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, ConfigMigrationError, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_CONFIG, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPermissionPolicy, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionStore, DefaultSkillLoader, DefaultTaskStore, Director, DirectorStateCheckpoint, DoneConditionChecker, EternalAutonomyEngine, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FleetBus, FleetSpawnBudgetError, FleetUsageAggregator, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, IntelligentCompactor, LLMSelector, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, ParallelEternalEngine, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, SddParallelRun, SddTaskDecomposer, SelectiveCompactor, SessionAnalyzer, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, ToolExecutor, addPlanItem, allServers, analyzeCriticalPath, applyRosterBudget, attachAutoExtend, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, classifyFamily, clearPlan, composeDirectorPrompt, composeSubagentPrompt, context7Server, contextManagerTool, createAutoExecutor, createContextManagerTool, createDelegateTool, createMessage, createSessionEventBridge,
|
|
20441
|
+
export { AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, ConfigMigrationError, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_CONFIG, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPermissionPolicy, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionStore, DefaultSkillLoader, DefaultTaskStore, Director, DirectorStateCheckpoint, DoneConditionChecker, EternalAutonomyEngine, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FleetBus, FleetSpawnBudgetError, FleetUsageAggregator, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, IntelligentCompactor, LLMSelector, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, ParallelEternalEngine, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, SddParallelRun, SddTaskDecomposer, SelectiveCompactor, SessionAnalyzer, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, ToolExecutor, addPlanItem, allServers, analyzeCriticalPath, applyRosterBudget, attachAutoExtend, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, classifyFamily, clearPlan, composeDirectorPrompt, composeSubagentPrompt, context7Server, contextManagerTool, createAutoExecutor, createContextManagerTool, createDelegateTool, createMessage, createSessionEventBridge, createStrategyCompactor, decryptConfigSecrets, deriveTodosFromPlanItem, dispatchAgent, emptyPlan, encryptConfigSecrets, everArtServer, filesystemServer, formatPlan, formatPlanTemplates, getAgentDefinition, getPlanTemplate, getTemplate, githubServer, googleMapsServer, listPlanTemplates, listTemplates, loadDirectorState, loadPlan, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeAskTool, makeAssignTool, makeAutonomyPromptContributor, makeAwaitTasksTool, makeCollabDebugTool, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeRollUpTool, makeSpawnTool, makeTerminateTool, migratePlaintextSecrets, miniMaxVisionServer, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, resolveAuditLevel, resolveSessionLoggingConfig, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, savePlan, saveTodosCheckpoint, scoreAgents, sentinelServer, setPlanItemStatus, slackServer, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, templateToMarkdown, wireMetricsToEvents, zaiVisionServer };
|
|
20485
20442
|
//# sourceMappingURL=index.js.map
|
|
20486
20443
|
//# sourceMappingURL=index.js.map
|