svamp-cli 0.2.326 → 0.2.327
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/{adminCommands-D1NzA4Hb.mjs → adminCommands-BVXEj5XN.mjs} +1 -1
- package/dist/{agentCommands-DUHUBzrj.mjs → agentCommands-BiSVzb5D.mjs} +5 -5
- package/dist/{auth-DogM3PPm.mjs → auth-D0ji0kh3.mjs} +1 -1
- package/dist/{cli-PWoHWBkF.mjs → cli-CQlIsRxF.mjs} +77 -77
- package/dist/cli.mjs +2 -2
- package/dist/{commands-Dm9KNB-p.mjs → commands-BkzYDwHO.mjs} +3 -3
- package/dist/{commands-D-4ry2I6.mjs → commands-C4z-QQdO.mjs} +11 -11
- package/dist/{commands-CBk4dLUP.mjs → commands-CPZdcLvm.mjs} +3 -3
- package/dist/{commands-Dt_MecCB.mjs → commands-CXJmXwQo.mjs} +1 -1
- package/dist/{commands-Ck4pP86Q.mjs → commands-Cegv66fj.mjs} +2 -2
- package/dist/{commands-B5bC9eXz.mjs → commands-D5R0AmXt.mjs} +3 -3
- package/dist/{commands-CqqQYRzg.mjs → commands-D61SEgo0.mjs} +2 -2
- package/dist/{commands-CxfUr2Qr.mjs → commands-DexO3t-N.mjs} +1 -1
- package/dist/{fleet-Bzg3YxM4.mjs → fleet-CCVC-NiE.mjs} +2 -2
- package/dist/{headlessCli-DvJI9DRh.mjs → headlessCli-CFgDFTPd.mjs} +2 -2
- package/dist/{httpServer-1XjB2h3K.mjs → httpServer-Be1Hcddr.mjs} +59 -38
- package/dist/index.mjs +1 -1
- package/dist/{notifyCommands-BpMErHcv.mjs → notifyCommands-BfLeyHId.mjs} +1 -1
- package/dist/package-TcR2vlFS.mjs +64 -0
- package/dist/{rpc-Qcj4M_LQ.mjs → rpc-DXY3KqiA.mjs} +1 -1
- package/dist/{rpc-27xBajCe.mjs → rpc-hdw0ZlCj.mjs} +1 -1
- package/dist/{run-CocRShOR.mjs → run-CewXMfIf.mjs} +1 -1
- package/dist/{run-BBBr4jkI.mjs → run-DdTcDN3e.mjs} +190 -42
- package/dist/{scheduler-DjAVEyn3.mjs → scheduler-3LUL1jFD.mjs} +1 -1
- package/dist/{serveCommands-BK-wWc_i.mjs → serveCommands-DUJ4eojk.mjs} +10 -10
- package/dist/{sideband-5ck3CDWM.mjs → sideband-BRHu4M46.mjs} +1 -1
- package/package.json +3 -3
- package/dist/package-D_BgajkI.mjs +0 -64
|
@@ -3001,6 +3001,13 @@ function assertNonAdminMountDirSafe(directory, homeDir, resolveReal = defaultRes
|
|
|
3001
3001
|
}
|
|
3002
3002
|
}
|
|
3003
3003
|
}
|
|
3004
|
+
function hiddenSegmentOf(relPath) {
|
|
3005
|
+
for (const seg of relPath.split(/[\\/]+/)) {
|
|
3006
|
+
if (!seg || seg === ".") continue;
|
|
3007
|
+
if (seg.startsWith(".")) return seg;
|
|
3008
|
+
}
|
|
3009
|
+
return null;
|
|
3010
|
+
}
|
|
3004
3011
|
function defaultResolveReal(p) {
|
|
3005
3012
|
try {
|
|
3006
3013
|
return fs.realpathSync(p);
|
|
@@ -3203,8 +3210,19 @@ class ServeManager {
|
|
|
3203
3210
|
* Extract a channel capability token from the request, if one is present. A channel caller
|
|
3204
3211
|
* key is `ck_`-prefixed; we ONLY return a token that carries that prefix so a Hypha JWT
|
|
3205
3212
|
* bearer (used by the email-identity path) never collides with the capability authorizer.
|
|
3206
|
-
* Accepted carriers: `Authorization: Bearer ck_…` header, or `?key=ck_…` query
|
|
3207
|
-
* Returns null when no capability token is presented.
|
|
3213
|
+
* Accepted carriers: `Authorization: Bearer ck_…` header (any verb), or `?key=ck_…` query
|
|
3214
|
+
* param (READ verbs only). Returns null when no capability token is presented.
|
|
3215
|
+
* NEVER log the returned value.
|
|
3216
|
+
*
|
|
3217
|
+
* #1047: the query carrier is restricted to GET/HEAD. A query string is a poor place for a
|
|
3218
|
+
* credential — it lands in browser history, in the `Referer` of any HTML the gateway serves,
|
|
3219
|
+
* and in every tunnel/intermediary access log on the path (and the gateway is reached over an
|
|
3220
|
+
* frps tunnel whose logs the user does not control). This is the same exposure class #1006
|
|
3221
|
+
* closed for the Hypha JWT in download URLs. It is kept for reads because a browser-embeddable
|
|
3222
|
+
* URL genuinely needs it (an `<img src>` or a download link cannot set a header), but a leaked
|
|
3223
|
+
* URL must not also confer PUT/DELETE over the whole project. The generated skill already
|
|
3224
|
+
* documents only the Authorization form (see `fileSection` in channel/store.ts), so this
|
|
3225
|
+
* narrows an unused carrier rather than breaking the documented path.
|
|
3208
3226
|
*/
|
|
3209
3227
|
extractCapabilityToken(req, url) {
|
|
3210
3228
|
const auth = req.headers["authorization"];
|
|
@@ -3212,6 +3230,8 @@ class ServeManager {
|
|
|
3212
3230
|
const m = /^Bearer\s+(\S+)$/i.exec(auth.trim());
|
|
3213
3231
|
if (m && m[1].startsWith("ck_")) return m[1];
|
|
3214
3232
|
}
|
|
3233
|
+
const method = (req.method || "GET").toUpperCase();
|
|
3234
|
+
if (method !== "GET" && method !== "HEAD") return null;
|
|
3215
3235
|
const q = url.searchParams.get("key");
|
|
3216
3236
|
if (q && q.startsWith("ck_")) return q;
|
|
3217
3237
|
return null;
|
|
@@ -3857,6 +3877,14 @@ a{color:#0969da;text-decoration:none;font-weight:500}a:hover{text-decoration:und
|
|
|
3857
3877
|
}
|
|
3858
3878
|
const rootDir = target.cwd;
|
|
3859
3879
|
const normRel = this.toSessionRelative(rootDir, m[2] || "");
|
|
3880
|
+
if (capabilityGranted) {
|
|
3881
|
+
const badSeg = hiddenSegmentOf(normRel);
|
|
3882
|
+
if (badSeg) {
|
|
3883
|
+
this.log(`Files-gateway DENY '${sessionId}': channel capability may not reach a hidden path (${badSeg})`);
|
|
3884
|
+
deny(403, `Forbidden: channel file access may not reach hidden paths (${badSeg})`);
|
|
3885
|
+
return;
|
|
3886
|
+
}
|
|
3887
|
+
}
|
|
3860
3888
|
if (method === "GET" || method === "HEAD") {
|
|
3861
3889
|
if (url.searchParams.get("list") === "1") {
|
|
3862
3890
|
const lex = this.resolveContainedPath(rootDir, normRel);
|
|
@@ -3878,6 +3906,7 @@ a{color:#0969da;text-decoration:none;font-weight:500}a:hover{text-decoration:und
|
|
|
3878
3906
|
}
|
|
3879
3907
|
return { name: d.name, isDir, size, mtime };
|
|
3880
3908
|
});
|
|
3909
|
+
if (capabilityGranted) entries = entries.filter((e) => !e.name.startsWith("."));
|
|
3881
3910
|
} catch (err) {
|
|
3882
3911
|
deny(err?.code === "ENOENT" ? 404 : 500, `Listing failed: ${err?.message || err}`);
|
|
3883
3912
|
return;
|
|
@@ -4458,6 +4487,7 @@ var serveManager = /*#__PURE__*/Object.freeze({
|
|
|
4458
4487
|
ServeManager: ServeManager,
|
|
4459
4488
|
assertNonAdminMountDirSafe: assertNonAdminMountDirSafe,
|
|
4460
4489
|
buildLinkSubdomain: buildLinkSubdomain,
|
|
4490
|
+
hiddenSegmentOf: hiddenSegmentOf,
|
|
4461
4491
|
resolveMountOwnerEmail: resolveMountOwnerEmail,
|
|
4462
4492
|
sanitizeMountForRole: sanitizeMountForRole
|
|
4463
4493
|
});
|
|
@@ -4636,8 +4666,13 @@ function applyPromptCacheTtl(spawnEnv) {
|
|
|
4636
4666
|
const cacheOverride = spawnEnv.ENABLE_PROMPT_CACHING_1H ?? process.env.ENABLE_PROMPT_CACHING_1H ?? spawnEnv.FORCE_PROMPT_CACHING_5M ?? process.env.FORCE_PROMPT_CACHING_5M;
|
|
4637
4667
|
if (cacheOverride === void 0) spawnEnv.ENABLE_PROMPT_CACHING_1H = "1";
|
|
4638
4668
|
}
|
|
4669
|
+
function applyTodoTools(spawnEnv) {
|
|
4670
|
+
const override = spawnEnv.CLAUDE_CODE_ENABLE_TODO_TOOLS ?? process.env.CLAUDE_CODE_ENABLE_TODO_TOOLS;
|
|
4671
|
+
if (override === void 0) spawnEnv.CLAUDE_CODE_ENABLE_TODO_TOOLS = "1";
|
|
4672
|
+
}
|
|
4639
4673
|
function applyClaudeProxyEnv(spawnEnv) {
|
|
4640
4674
|
const mode = currentMode();
|
|
4675
|
+
applyTodoTools(spawnEnv);
|
|
4641
4676
|
if (mode === "hypha") {
|
|
4642
4677
|
const proxyUrl = resolveHyphaProxyUrl();
|
|
4643
4678
|
if (!proxyUrl) {
|
|
@@ -5692,20 +5727,22 @@ function channelKeyGrantsFileAccess(channels, token) {
|
|
|
5692
5727
|
}
|
|
5693
5728
|
};
|
|
5694
5729
|
for (const ch of channels) {
|
|
5730
|
+
if (ch.enabled === false) continue;
|
|
5695
5731
|
if (!ch.fileAccess?.enabled) continue;
|
|
5696
5732
|
if (eq(ch.identity?.shared_key)) return true;
|
|
5697
5733
|
for (const caller of ch.identity?.callers || []) if (eq(caller.key)) return true;
|
|
5698
5734
|
}
|
|
5699
5735
|
return false;
|
|
5700
5736
|
}
|
|
5737
|
+
const _channelListCache = /* @__PURE__ */ new Map();
|
|
5738
|
+
const _CHANNEL_CACHE_TTL_MS = 1e3;
|
|
5739
|
+
function invalidateChannelCache(dir) {
|
|
5740
|
+
_channelListCache.delete(dir);
|
|
5741
|
+
}
|
|
5701
5742
|
class ChannelStore {
|
|
5702
5743
|
dir;
|
|
5703
5744
|
constructor(projectDir) {
|
|
5704
5745
|
this.dir = join$1(projectDir, ".svamp", "channels");
|
|
5705
|
-
try {
|
|
5706
|
-
mkdirSync$1(this.dir, { recursive: true });
|
|
5707
|
-
} catch {
|
|
5708
|
-
}
|
|
5709
5746
|
}
|
|
5710
5747
|
// #0982: reject a traversing/oversized id at the path boundary itself, so no
|
|
5711
5748
|
// caller (RPC, HTTP, CLI) can reach outside this.dir regardless of its own checks.
|
|
@@ -5728,17 +5765,41 @@ class ChannelStore {
|
|
|
5728
5765
|
const tmp = `${this._path(c.id)}.tmp-${process.pid}-${randomBytes(4).toString("hex")}`;
|
|
5729
5766
|
writeFileSync$1(tmp, JSON.stringify(c, null, 2), { mode: 384 });
|
|
5730
5767
|
renameSync(tmp, this._path(c.id));
|
|
5768
|
+
invalidateChannelCache(this.dir);
|
|
5731
5769
|
return c;
|
|
5732
5770
|
}
|
|
5771
|
+
/**
|
|
5772
|
+
* All channels in this project, parsed. Cached per directory and revalidated with ONE statSync
|
|
5773
|
+
* — see the #1049/#1068 note above for why the uncached version was a daemon-wide stall.
|
|
5774
|
+
*/
|
|
5733
5775
|
list() {
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
}
|
|
5776
|
+
let st;
|
|
5777
|
+
try {
|
|
5778
|
+
const s = statSync(this.dir);
|
|
5779
|
+
st = { mtimeMs: s.mtimeMs, size: s.size };
|
|
5780
|
+
} catch {
|
|
5781
|
+
_channelListCache.delete(this.dir);
|
|
5782
|
+
return [];
|
|
5783
|
+
}
|
|
5784
|
+
const hit = _channelListCache.get(this.dir);
|
|
5785
|
+
if (hit && hit.mtimeMs === st.mtimeMs && hit.size === st.size && Date.now() - hit.at < _CHANNEL_CACHE_TTL_MS) {
|
|
5786
|
+
return hit.channels;
|
|
5787
|
+
}
|
|
5788
|
+
let channels;
|
|
5789
|
+
try {
|
|
5790
|
+
channels = readdirSync(this.dir).filter((f) => f.endsWith(".json")).map((f) => {
|
|
5791
|
+
try {
|
|
5792
|
+
return normalizeBind(JSON.parse(readFileSync(join$1(this.dir, f), "utf8")));
|
|
5793
|
+
} catch {
|
|
5794
|
+
return null;
|
|
5795
|
+
}
|
|
5796
|
+
}).filter((c) => !!c);
|
|
5797
|
+
} catch {
|
|
5798
|
+
_channelListCache.delete(this.dir);
|
|
5799
|
+
return [];
|
|
5800
|
+
}
|
|
5801
|
+
_channelListCache.set(this.dir, { mtimeMs: st.mtimeMs, size: st.size, at: Date.now(), channels });
|
|
5802
|
+
return channels;
|
|
5742
5803
|
}
|
|
5743
5804
|
get(id) {
|
|
5744
5805
|
try {
|
|
@@ -5756,6 +5817,7 @@ class ChannelStore {
|
|
|
5756
5817
|
const p = this._path(id);
|
|
5757
5818
|
if (existsSync(p)) {
|
|
5758
5819
|
rmSync$1(p);
|
|
5820
|
+
invalidateChannelCache(this.dir);
|
|
5759
5821
|
return true;
|
|
5760
5822
|
}
|
|
5761
5823
|
return false;
|
|
@@ -5796,6 +5858,28 @@ class ChannelStore {
|
|
|
5796
5858
|
return caller;
|
|
5797
5859
|
});
|
|
5798
5860
|
}
|
|
5861
|
+
/**
|
|
5862
|
+
* #1036: revoke ONE caller's key without destroying the channel.
|
|
5863
|
+
*
|
|
5864
|
+
* Before this, the only removal primitive was remove(id) — so withdrawing a single leaked key
|
|
5865
|
+
* meant deleting the whole channel and every other caller with it. That is a bad enough trade
|
|
5866
|
+
* that in practice a leaked key just stayed live, which matters much more now that a caller key
|
|
5867
|
+
* can carry whole-project file access (see channelKeyGrantsFileAccess).
|
|
5868
|
+
*
|
|
5869
|
+
* Same per-channel lock as the sibling mutators (#0679), so a concurrent addCaller cannot
|
|
5870
|
+
* resurrect the revoked entry by writing a stale base. Returns true when a caller was removed.
|
|
5871
|
+
*/
|
|
5872
|
+
removeCaller(id, name) {
|
|
5873
|
+
return withFileLock(this._lock(id), () => {
|
|
5874
|
+
const c = this.get(id);
|
|
5875
|
+
if (!c?.identity?.callers) return false;
|
|
5876
|
+
const before = c.identity.callers.length;
|
|
5877
|
+
c.identity.callers = c.identity.callers.filter((x) => x.name !== name);
|
|
5878
|
+
if (c.identity.callers.length === before) return false;
|
|
5879
|
+
this._writeChannel(c);
|
|
5880
|
+
return true;
|
|
5881
|
+
});
|
|
5882
|
+
}
|
|
5799
5883
|
}
|
|
5800
5884
|
function routingSession(channel, ctx) {
|
|
5801
5885
|
const mode = bindMode(channel);
|
|
@@ -6435,6 +6519,9 @@ function getParamNames(fn) {
|
|
|
6435
6519
|
const terminalSessions = /* @__PURE__ */ new Map();
|
|
6436
6520
|
let ptyModule = null;
|
|
6437
6521
|
const TERMINAL_IDLE_MS = 30 * 60 * 1e3;
|
|
6522
|
+
const TERMINAL_CLIENT_ABSENT_MS = 4 * 60 * 60 * 1e3;
|
|
6523
|
+
const TERMINAL_BUFFER_MAX_BYTES = 1024 * 1024;
|
|
6524
|
+
const TERMINAL_MAX_SESSIONS = 64;
|
|
6438
6525
|
let terminalReaper = null;
|
|
6439
6526
|
function ensureTerminalReaper() {
|
|
6440
6527
|
if (terminalReaper) return;
|
|
@@ -6442,7 +6529,8 @@ function ensureTerminalReaper() {
|
|
|
6442
6529
|
const now = Date.now();
|
|
6443
6530
|
for (const [id, s] of terminalSessions) {
|
|
6444
6531
|
if (s.exited) continue;
|
|
6445
|
-
|
|
6532
|
+
const abandoned = now - (s.lastClientActivity ?? s.lastActivity) > TERMINAL_CLIENT_ABSENT_MS;
|
|
6533
|
+
if (now - s.lastActivity > TERMINAL_IDLE_MS || abandoned) {
|
|
6446
6534
|
try {
|
|
6447
6535
|
s.pty.kill();
|
|
6448
6536
|
} catch {
|
|
@@ -7296,8 +7384,29 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
7296
7384
|
rows,
|
|
7297
7385
|
createdAt: Date.now(),
|
|
7298
7386
|
lastActivity: Date.now(),
|
|
7387
|
+
lastClientActivity: Date.now(),
|
|
7388
|
+
outputBytes: 0,
|
|
7299
7389
|
cwd
|
|
7300
7390
|
};
|
|
7391
|
+
if (terminalSessions.size >= TERMINAL_MAX_SESSIONS) {
|
|
7392
|
+
let victim = null, oldest = Infinity;
|
|
7393
|
+
for (const [id, s] of terminalSessions) {
|
|
7394
|
+
const seen = s.lastClientActivity ?? s.lastActivity;
|
|
7395
|
+
if (seen < oldest) {
|
|
7396
|
+
oldest = seen;
|
|
7397
|
+
victim = id;
|
|
7398
|
+
}
|
|
7399
|
+
}
|
|
7400
|
+
if (victim) {
|
|
7401
|
+
const v = terminalSessions.get(victim);
|
|
7402
|
+
try {
|
|
7403
|
+
v?.pty.kill();
|
|
7404
|
+
} catch {
|
|
7405
|
+
}
|
|
7406
|
+
terminalSessions.delete(victim);
|
|
7407
|
+
console.log(`[terminal] evicted idlest session ${victim} \u2014 at the ${TERMINAL_MAX_SESSIONS}-PTY ceiling`);
|
|
7408
|
+
}
|
|
7409
|
+
}
|
|
7301
7410
|
terminalSessions.set(sessionId, session);
|
|
7302
7411
|
ensureTerminalReaper();
|
|
7303
7412
|
let outputBatch = "";
|
|
@@ -7308,8 +7417,9 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
7308
7417
|
const batch = outputBatch;
|
|
7309
7418
|
outputBatch = "";
|
|
7310
7419
|
session.outputBuffer.push(batch);
|
|
7311
|
-
|
|
7312
|
-
|
|
7420
|
+
session.outputBytes += batch.length;
|
|
7421
|
+
while (session.outputBytes > TERMINAL_BUFFER_MAX_BYTES && session.outputBuffer.length > 1) {
|
|
7422
|
+
session.outputBytes -= (session.outputBuffer.shift() || "").length;
|
|
7313
7423
|
}
|
|
7314
7424
|
if (terminalClient) {
|
|
7315
7425
|
try {
|
|
@@ -7358,6 +7468,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
7358
7468
|
const filtered = filterTerminalResponses(params.data);
|
|
7359
7469
|
if (filtered) session.pty.write(filtered);
|
|
7360
7470
|
session.lastActivity = Date.now();
|
|
7471
|
+
session.lastClientActivity = Date.now();
|
|
7361
7472
|
return { success: true };
|
|
7362
7473
|
},
|
|
7363
7474
|
/** Resize a terminal session. */
|
|
@@ -7379,7 +7490,9 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
7379
7490
|
const session = terminalSessions.get(params.sessionId);
|
|
7380
7491
|
if (!session) throw new Error(`Terminal session ${params.sessionId} not found`);
|
|
7381
7492
|
const output = session.outputBuffer.splice(0).join("");
|
|
7493
|
+
session.outputBytes = 0;
|
|
7382
7494
|
session.lastActivity = Date.now();
|
|
7495
|
+
session.lastClientActivity = Date.now();
|
|
7383
7496
|
const result = {
|
|
7384
7497
|
output,
|
|
7385
7498
|
exited: session.exited
|
|
@@ -8189,7 +8302,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
8189
8302
|
}
|
|
8190
8303
|
const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
|
|
8191
8304
|
const sender = { name: context?.user?.email || context?.user?.id || "user", kind: "user", verified: true };
|
|
8192
|
-
const { toolsForRole } = await import('./sideband-
|
|
8305
|
+
const { toolsForRole } = await import('./sideband-BRHu4M46.mjs');
|
|
8193
8306
|
const r2 = await runWiseAgent({ message: params.message, sender, config: { tools: toolsForRole(role2) }, deps, transport, model: resolved.model });
|
|
8194
8307
|
return fmt(r2);
|
|
8195
8308
|
}
|
|
@@ -8294,7 +8407,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
8294
8407
|
return { ok: false, call_id: callId, status: "busy", error: "channel is busy (too many concurrent requests) \u2014 retry shortly" };
|
|
8295
8408
|
}
|
|
8296
8409
|
const rendered = renderMessage(c, { sender: r.sender, body: { message: kwargs.message }, callId });
|
|
8297
|
-
const { queryCore } = await import('./commands-
|
|
8410
|
+
const { queryCore } = await import('./commands-DexO3t-N.mjs');
|
|
8298
8411
|
const timeout = c.reply?.timeout_sec || 120;
|
|
8299
8412
|
let result;
|
|
8300
8413
|
try {
|
|
@@ -9442,6 +9555,7 @@ function loadMessagesFromDiskReverse(messagesDir, beforeSeq, limit) {
|
|
|
9442
9555
|
}
|
|
9443
9556
|
}
|
|
9444
9557
|
const messageCountCache = /* @__PURE__ */ new Map();
|
|
9558
|
+
const MESSAGE_COUNT_CACHE_MAX = 2e3;
|
|
9445
9559
|
function countMessagesOnDisk(messagesDir, fallbackInMemory) {
|
|
9446
9560
|
const filePath = join$1(messagesDir, "messages.jsonl");
|
|
9447
9561
|
if (!existsSync(filePath)) return fallbackInMemory;
|
|
@@ -9469,7 +9583,13 @@ function countMessagesOnDisk(messagesDir, fallbackInMemory) {
|
|
|
9469
9583
|
for (let i = 0; i < data.length; i++) if (data.charCodeAt(i) === 10) count++;
|
|
9470
9584
|
if (data.length > 0 && data[data.length - 1] !== "\n") count++;
|
|
9471
9585
|
}
|
|
9586
|
+
messageCountCache.delete(filePath);
|
|
9472
9587
|
messageCountCache.set(filePath, { size: st.size, mtimeMs: st.mtimeMs, count });
|
|
9588
|
+
while (messageCountCache.size > MESSAGE_COUNT_CACHE_MAX) {
|
|
9589
|
+
const oldest = messageCountCache.keys().next();
|
|
9590
|
+
if (oldest.done) break;
|
|
9591
|
+
messageCountCache.delete(oldest.value);
|
|
9592
|
+
}
|
|
9473
9593
|
return count;
|
|
9474
9594
|
} catch {
|
|
9475
9595
|
return fallbackInMemory;
|
|
@@ -10820,7 +10940,9 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
|
|
|
10820
10940
|
// ── Listener Registration ──
|
|
10821
10941
|
registerListener: async (callback, context) => {
|
|
10822
10942
|
authorizeRequest(context, metadata.sharing, "view");
|
|
10943
|
+
const MAX_LISTENERS = 64;
|
|
10823
10944
|
listeners.push(callback);
|
|
10945
|
+
while (listeners.length > MAX_LISTENERS) listeners.shift();
|
|
10824
10946
|
const replayMessages = messages.slice(-50);
|
|
10825
10947
|
const REPLAY_MESSAGE_TIMEOUT_MS = 1e4;
|
|
10826
10948
|
for (const msg of replayMessages) {
|
|
@@ -13698,6 +13820,25 @@ var acpAgentConfig = /*#__PURE__*/Object.freeze({
|
|
|
13698
13820
|
resolveAcpAgentConfig: resolveAcpAgentConfig
|
|
13699
13821
|
});
|
|
13700
13822
|
|
|
13823
|
+
const COMPLETED_REQUESTS_MAX = 500;
|
|
13824
|
+
function recordCompletedRequest(existing, id, entry, max = COMPLETED_REQUESTS_MAX) {
|
|
13825
|
+
const out = { ...existing || {} };
|
|
13826
|
+
delete out[id];
|
|
13827
|
+
const slim = {
|
|
13828
|
+
completedAt: typeof entry.completedAt === "number" ? entry.completedAt : Date.now(),
|
|
13829
|
+
status: String(entry.status ?? "")
|
|
13830
|
+
};
|
|
13831
|
+
if (typeof entry.tool === "string") slim.tool = entry.tool;
|
|
13832
|
+
if (entry.reason !== void 0) slim.reason = entry.reason;
|
|
13833
|
+
if (entry.mode !== void 0) slim.mode = entry.mode;
|
|
13834
|
+
if (entry.decision !== void 0) slim.decision = entry.decision;
|
|
13835
|
+
if (Array.isArray(entry.allowedTools)) slim.allowedTools = entry.allowedTools;
|
|
13836
|
+
out[id] = slim;
|
|
13837
|
+
const keys = Object.keys(out);
|
|
13838
|
+
for (let i = 0; i < keys.length - max; i++) delete out[keys[i]];
|
|
13839
|
+
return out;
|
|
13840
|
+
}
|
|
13841
|
+
|
|
13701
13842
|
function applyPermissionResolution(sessionService, requestId, approved) {
|
|
13702
13843
|
const reqs = { ...sessionService._agentState?.requests };
|
|
13703
13844
|
if (!(requestId in reqs)) return;
|
|
@@ -13707,14 +13848,12 @@ function applyPermissionResolution(sessionService, requestId, approved) {
|
|
|
13707
13848
|
sessionService.updateAgentState({
|
|
13708
13849
|
controlledByUser: false,
|
|
13709
13850
|
requests: reqs,
|
|
13710
|
-
|
|
13711
|
-
|
|
13712
|
-
|
|
13713
|
-
|
|
13714
|
-
|
|
13715
|
-
|
|
13716
|
-
}
|
|
13717
|
-
}
|
|
13851
|
+
// #1041: bounded + slimmed, same as the Claude permission path in daemon/run.ts.
|
|
13852
|
+
completedRequests: recordCompletedRequest(completedReqs, requestId, {
|
|
13853
|
+
...existingReq || {},
|
|
13854
|
+
completedAt: Date.now(),
|
|
13855
|
+
status: approved ? "approved" : "denied"
|
|
13856
|
+
})
|
|
13718
13857
|
});
|
|
13719
13858
|
}
|
|
13720
13859
|
function bridgeAcpToSession(backend, sessionService, getMetadata, setMetadata, log, onTurnEnd, getModelLabel) {
|
|
@@ -18035,6 +18174,9 @@ function shouldRunZombieProbe(args) {
|
|
|
18035
18174
|
function nextFailuresAfterZombieProbeFailure(consecutiveHeartbeatFailures) {
|
|
18036
18175
|
return consecutiveHeartbeatFailures + 1;
|
|
18037
18176
|
}
|
|
18177
|
+
function shouldRunServiceProbe(args) {
|
|
18178
|
+
return !args.inGrace && !args.zombieProbeRan;
|
|
18179
|
+
}
|
|
18038
18180
|
function shouldForceReconnect(consecutiveHeartbeatFailures) {
|
|
18039
18181
|
if (consecutiveHeartbeatFailures < 2) return false;
|
|
18040
18182
|
return consecutiveHeartbeatFailures === 2 || consecutiveHeartbeatFailures % 3 === 0;
|
|
@@ -20082,7 +20224,7 @@ async function startDaemon(options) {
|
|
|
20082
20224
|
try {
|
|
20083
20225
|
const dir = loadSessionIndex()[sessionId]?.directory;
|
|
20084
20226
|
if (!dir) return;
|
|
20085
|
-
const { reconcileServiceLinks } = await import('./agentCommands-
|
|
20227
|
+
const { reconcileServiceLinks } = await import('./agentCommands-BiSVzb5D.mjs');
|
|
20086
20228
|
const configPath = getSvampConfigPath(dir, sessionId);
|
|
20087
20229
|
const config = readSvampConfig(configPath);
|
|
20088
20230
|
const entries = Array.from(urls.entries());
|
|
@@ -20104,7 +20246,7 @@ async function startDaemon(options) {
|
|
|
20104
20246
|
try {
|
|
20105
20247
|
const dir = loadSessionIndex()[sessionId]?.directory;
|
|
20106
20248
|
if (!dir) return;
|
|
20107
|
-
const { reconcileServiceLinks } = await import('./agentCommands-
|
|
20249
|
+
const { reconcileServiceLinks } = await import('./agentCommands-BiSVzb5D.mjs');
|
|
20108
20250
|
const configPath = getSvampConfigPath(dir, sessionId);
|
|
20109
20251
|
const config = readSvampConfig(configPath);
|
|
20110
20252
|
const incoming = [{
|
|
@@ -20125,7 +20267,7 @@ async function startDaemon(options) {
|
|
|
20125
20267
|
try {
|
|
20126
20268
|
const dir = loadSessionIndex()[sessionId]?.directory;
|
|
20127
20269
|
if (!dir) return;
|
|
20128
|
-
const { dropServiceLinks } = await import('./agentCommands-
|
|
20270
|
+
const { dropServiceLinks } = await import('./agentCommands-BiSVzb5D.mjs');
|
|
20129
20271
|
const configPath = getSvampConfigPath(dir, sessionId);
|
|
20130
20272
|
const config = readSvampConfig(configPath);
|
|
20131
20273
|
if (dropServiceLinks(config, "serve", mountName)) {
|
|
@@ -21388,15 +21530,19 @@ ${parts.join("\n")}`);
|
|
|
21388
21530
|
sessionService.updateAgentState({
|
|
21389
21531
|
controlledByUser: false,
|
|
21390
21532
|
requests: reqs,
|
|
21391
|
-
|
|
21392
|
-
|
|
21393
|
-
|
|
21533
|
+
// #1041: bounded + slimmed. `arguments: toolInput` used to
|
|
21534
|
+
// be stored here — the whole tool input, e.g. a full Write
|
|
21535
|
+
// body — in a map that grew forever and is re-walked by the
|
|
21536
|
+
// reducer on every streamed batch.
|
|
21537
|
+
completedRequests: recordCompletedRequest(
|
|
21538
|
+
sessionService._agentState?.completedRequests,
|
|
21539
|
+
correlationId,
|
|
21540
|
+
{
|
|
21394
21541
|
tool: toolName,
|
|
21395
|
-
arguments: toolInput,
|
|
21396
21542
|
completedAt: Date.now(),
|
|
21397
21543
|
status: result.behavior === "allow" ? "approved" : "denied"
|
|
21398
21544
|
}
|
|
21399
|
-
|
|
21545
|
+
)
|
|
21400
21546
|
});
|
|
21401
21547
|
}).catch((err) => {
|
|
21402
21548
|
logger.log(`[Session ${sessionId}] Permission handler error (request ${requestId}): ${err}`);
|
|
@@ -22497,11 +22643,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
22497
22643
|
});
|
|
22498
22644
|
},
|
|
22499
22645
|
onIssue: async (params) => {
|
|
22500
|
-
const { issueRpc } = await import('./rpc-
|
|
22646
|
+
const { issueRpc } = await import('./rpc-DXY3KqiA.mjs');
|
|
22501
22647
|
return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
|
|
22502
22648
|
},
|
|
22503
22649
|
onWorkflow: async (params) => {
|
|
22504
|
-
const { workflowRpc } = await import('./rpc-
|
|
22650
|
+
const { workflowRpc } = await import('./rpc-hdw0ZlCj.mjs');
|
|
22505
22651
|
return workflowRpc(params?.cwd || directory, params || {});
|
|
22506
22652
|
},
|
|
22507
22653
|
onRipgrep: async (args, cwd) => {
|
|
@@ -23207,11 +23353,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
23207
23353
|
});
|
|
23208
23354
|
},
|
|
23209
23355
|
onIssue: async (params) => {
|
|
23210
|
-
const { issueRpc } = await import('./rpc-
|
|
23356
|
+
const { issueRpc } = await import('./rpc-DXY3KqiA.mjs');
|
|
23211
23357
|
return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
|
|
23212
23358
|
},
|
|
23213
23359
|
onWorkflow: async (params) => {
|
|
23214
|
-
const { workflowRpc } = await import('./rpc-
|
|
23360
|
+
const { workflowRpc } = await import('./rpc-hdw0ZlCj.mjs');
|
|
23215
23361
|
return workflowRpc(params?.cwd || directory, params || {});
|
|
23216
23362
|
},
|
|
23217
23363
|
onRipgrep: async (args, cwd) => {
|
|
@@ -24119,7 +24265,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
24119
24265
|
const channelHttpPort = Number(process.env.SVAMP_CHANNEL_HTTP_PORT) || 0;
|
|
24120
24266
|
if (channelHttpPort > 0) {
|
|
24121
24267
|
try {
|
|
24122
|
-
const { createChannelHttpServer } = await import('./httpServer-
|
|
24268
|
+
const { createChannelHttpServer } = await import('./httpServer-Be1Hcddr.mjs');
|
|
24123
24269
|
const channelHttpServer = createChannelHttpServer({
|
|
24124
24270
|
getSessionIds: () => {
|
|
24125
24271
|
const ids = [];
|
|
@@ -24548,7 +24694,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
24548
24694
|
const PING_TIMEOUT_MS = 15e3;
|
|
24549
24695
|
const POST_RECONNECT_GRACE_MS = 2e4;
|
|
24550
24696
|
const RECONNECT_JITTER_MS = 2500;
|
|
24551
|
-
const { WorkflowScheduler } = await import('./scheduler-
|
|
24697
|
+
const { WorkflowScheduler } = await import('./scheduler-3LUL1jFD.mjs');
|
|
24552
24698
|
const workflowProjectRoots = () => {
|
|
24553
24699
|
const dirs = /* @__PURE__ */ new Set();
|
|
24554
24700
|
for (const s of pidToTrackedSession.values()) {
|
|
@@ -24627,7 +24773,9 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
24627
24773
|
const INBOUND_SILENCE_THRESHOLD_MS = 12e4;
|
|
24628
24774
|
const inboundSilenceMs = Date.now() - machineService.getLastInboundRpcAt();
|
|
24629
24775
|
const hasActiveSessions = pidToTrackedSession.size > 0;
|
|
24776
|
+
let zombieProbeRan = false;
|
|
24630
24777
|
if (shouldRunZombieProbe({ hasActiveSessions, inboundSilenceMs, consecutiveHeartbeatFailures, inGrace, thresholdMs: INBOUND_SILENCE_THRESHOLD_MS })) {
|
|
24778
|
+
zombieProbeRan = true;
|
|
24631
24779
|
logger.log(`No inbound RPC for ${Math.round(inboundSilenceMs / 1e3)}s with ${pidToTrackedSession.size} active session(s) \u2014 zombie probe`);
|
|
24632
24780
|
try {
|
|
24633
24781
|
const machineServiceId = `${server.config.client_id}:default`;
|
|
@@ -24640,7 +24788,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
24640
24788
|
consecutiveHeartbeatFailures = nextFailuresAfterZombieProbeFailure(consecutiveHeartbeatFailures);
|
|
24641
24789
|
}
|
|
24642
24790
|
}
|
|
24643
|
-
if (
|
|
24791
|
+
if (shouldRunServiceProbe({ inGrace, zombieProbeRan })) {
|
|
24644
24792
|
try {
|
|
24645
24793
|
const pingStart = Date.now();
|
|
24646
24794
|
const machineServiceId = `${server.config.client_id}:default`;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { f as resolveProjectRoot, C as listWorkflows, D as isWorkflowEnabled, E as workflowSchedules, F as inZone, w as runWorkflow, G as cronMatches } from './run-
|
|
1
|
+
import { f as resolveProjectRoot, C as listWorkflows, D as isWorkflowEnabled, E as workflowSchedules, F as inZone, w as runWorkflow, G as cronMatches } from './run-DdTcDN3e.mjs';
|
|
2
2
|
import 'os';
|
|
3
3
|
import 'fs/promises';
|
|
4
4
|
import 'fs';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as path from 'path';
|
|
2
|
-
import { w as wantsHelp } from './cli-
|
|
3
|
-
import './run-
|
|
2
|
+
import { w as wantsHelp } from './cli-CQlIsRxF.mjs';
|
|
3
|
+
import './run-DdTcDN3e.mjs';
|
|
4
4
|
import 'os';
|
|
5
5
|
import 'fs/promises';
|
|
6
6
|
import 'fs';
|
|
@@ -77,7 +77,7 @@ async function handleServeCommand() {
|
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
async function serveAdd(args, machineId) {
|
|
80
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
80
|
+
const { connectAndGetMachine } = await import('./commands-DexO3t-N.mjs');
|
|
81
81
|
const pos = positionalArgs(args);
|
|
82
82
|
const name = pos[0];
|
|
83
83
|
if (!name) {
|
|
@@ -110,7 +110,7 @@ async function serveAdd(args, machineId) {
|
|
|
110
110
|
}
|
|
111
111
|
if (sessionId && result?.url) {
|
|
112
112
|
try {
|
|
113
|
-
const { autoAddSessionLink } = await import('./agentCommands-
|
|
113
|
+
const { autoAddSessionLink } = await import('./agentCommands-BiSVzb5D.mjs');
|
|
114
114
|
autoAddSessionLink(String(result.url), name, void 0, { kind: "serve", name });
|
|
115
115
|
} catch {
|
|
116
116
|
}
|
|
@@ -124,7 +124,7 @@ async function serveAdd(args, machineId) {
|
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
async function serveApply(args, machineId) {
|
|
127
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
127
|
+
const { connectAndGetMachine } = await import('./commands-DexO3t-N.mjs');
|
|
128
128
|
const fs = await import('fs');
|
|
129
129
|
const yaml = await import('yaml');
|
|
130
130
|
const file = positionalArgs(args)[0];
|
|
@@ -206,7 +206,7 @@ async function serveApply(args, machineId) {
|
|
|
206
206
|
console.log(`URL: ${result.url}`);
|
|
207
207
|
if (params.sessionId && result?.url) {
|
|
208
208
|
try {
|
|
209
|
-
const { autoAddSessionLink } = await import('./agentCommands-
|
|
209
|
+
const { autoAddSessionLink } = await import('./agentCommands-BiSVzb5D.mjs');
|
|
210
210
|
const prevSession = process.env.SVAMP_SESSION_ID;
|
|
211
211
|
process.env.SVAMP_SESSION_ID = String(params.sessionId);
|
|
212
212
|
try {
|
|
@@ -227,7 +227,7 @@ async function serveApply(args, machineId) {
|
|
|
227
227
|
}
|
|
228
228
|
}
|
|
229
229
|
async function serveRemove(args, machineId) {
|
|
230
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
230
|
+
const { connectAndGetMachine } = await import('./commands-DexO3t-N.mjs');
|
|
231
231
|
const pos = positionalArgs(args);
|
|
232
232
|
const name = pos[0];
|
|
233
233
|
if (!name) {
|
|
@@ -237,7 +237,7 @@ async function serveRemove(args, machineId) {
|
|
|
237
237
|
const { machine, server } = await connectAndGetMachine(machineId);
|
|
238
238
|
try {
|
|
239
239
|
await machine.serveRemove({ name });
|
|
240
|
-
const { removeSessionLinkByService } = await import('./agentCommands-
|
|
240
|
+
const { removeSessionLinkByService } = await import('./agentCommands-BiSVzb5D.mjs');
|
|
241
241
|
removeSessionLinkByService("serve", name);
|
|
242
242
|
console.log(`Mount '${name}' removed.`);
|
|
243
243
|
} catch (err) {
|
|
@@ -249,7 +249,7 @@ async function serveRemove(args, machineId) {
|
|
|
249
249
|
}
|
|
250
250
|
}
|
|
251
251
|
async function serveList(args, machineId) {
|
|
252
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
252
|
+
const { connectAndGetMachine } = await import('./commands-DexO3t-N.mjs');
|
|
253
253
|
const all = hasFlag(args, "--all", "-a");
|
|
254
254
|
const json = hasFlag(args, "--json");
|
|
255
255
|
const sessionId = getFlag(args, "--session");
|
|
@@ -283,7 +283,7 @@ async function serveList(args, machineId) {
|
|
|
283
283
|
}
|
|
284
284
|
}
|
|
285
285
|
async function serveInfo(machineId) {
|
|
286
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
286
|
+
const { connectAndGetMachine } = await import('./commands-DexO3t-N.mjs');
|
|
287
287
|
const { machine, server } = await connectAndGetMachine(machineId);
|
|
288
288
|
try {
|
|
289
289
|
const info = await machine.serveInfo();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { R as READ_ONLY_TOOLS, M as loadMachineContext, N as buildMachineInstructions, O as machineToolsForRole, P as buildMachineTools } from './run-
|
|
1
|
+
import { R as READ_ONLY_TOOLS, M as loadMachineContext, N as buildMachineInstructions, O as machineToolsForRole, P as buildMachineTools } from './run-DdTcDN3e.mjs';
|
|
2
2
|
import 'node:child_process';
|
|
3
3
|
import 'os';
|
|
4
4
|
import 'fs/promises';
|