svamp-cli 0.2.335 → 0.2.337
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-BQzdevf-.mjs → adminCommands-i7zkVIFU.mjs} +1 -1
- package/dist/{agentCommands-Ckfp_hB-.mjs → agentCommands-DmRCmbeU.mjs} +14 -9
- package/dist/{auth-Cs929l-I.mjs → auth-CXJGid9o.mjs} +1 -1
- package/dist/{cli-CEGWJ4kA.mjs → cli-v0NMiesq.mjs} +77 -77
- package/dist/cli.mjs +2 -2
- package/dist/{commands-DpO2Dpso.mjs → commands-B8KlOrBS.mjs} +3 -3
- package/dist/{commands-BUkvmpSe.mjs → commands-BRI0wxnE.mjs} +2 -2
- package/dist/{commands-DeG4CgpF.mjs → commands-BeSB75OM.mjs} +3 -3
- package/dist/{commands-5Zi72dU_.mjs → commands-Bn-JAlUf.mjs} +3 -3
- package/dist/{commands-CxTExdOM.mjs → commands-DWNYEYRO.mjs} +1 -1
- package/dist/{commands-DsMMKbBf.mjs → commands-DZF_q6a0.mjs} +2 -2
- package/dist/{commands-hGgwLjkk.mjs → commands-Dw_oiiqX.mjs} +1 -1
- package/dist/{commands-g0JC8Jsl.mjs → commands-dYyv0P82.mjs} +11 -11
- package/dist/{fleet-CBxcXUJf.mjs → fleet-Dion32w1.mjs} +1 -1
- package/dist/{headlessCli-CuD4z0w7.mjs → headlessCli-bB0pjxSC.mjs} +2 -2
- package/dist/index.mjs +1 -1
- package/dist/{notifyCommands-CwHruAja.mjs → notifyCommands-BVESEkQL.mjs} +1 -1
- package/dist/{package-CzPxgXcc.mjs → package-DBcAF326.mjs} +3 -3
- package/dist/{rpc-P1bWcW8c.mjs → rpc-BSWkkPHd.mjs} +1 -1
- package/dist/{rpc-D-NLQR0s.mjs → rpc-Cj31v2sm.mjs} +1 -1
- package/dist/{run-Bvkd9xLx.mjs → run-C6gfyMsM.mjs} +1 -1
- package/dist/{run-DUzNQjSO.mjs → run-bo0i02Dy.mjs} +317 -57
- package/dist/{scheduler-DSloKtUY.mjs → scheduler-DkfqQjih.mjs} +1 -1
- package/dist/{serveCommands-BH411VAs.mjs → serveCommands-D2sKo0xC.mjs} +10 -10
- package/dist/{sideband-_vhbrouu.mjs → sideband-D5Kan4J1.mjs} +1 -1
- package/package.json +3 -3
|
@@ -5521,27 +5521,63 @@ function sleepSync$1(ms) {
|
|
|
5521
5521
|
} catch {
|
|
5522
5522
|
}
|
|
5523
5523
|
}
|
|
5524
|
+
function isProcessDead(pid) {
|
|
5525
|
+
try {
|
|
5526
|
+
process.kill(pid, 0);
|
|
5527
|
+
return false;
|
|
5528
|
+
} catch (e) {
|
|
5529
|
+
return e?.code === "ESRCH";
|
|
5530
|
+
}
|
|
5531
|
+
}
|
|
5524
5532
|
function withFileLock(lockPath, fn, opts) {
|
|
5525
5533
|
const deadlineMs = opts?.deadlineMs ?? 50;
|
|
5526
5534
|
const staleMs = opts?.staleMs ?? 5e3;
|
|
5535
|
+
const trackOwner = opts?.breakOnDeadOwner ?? false;
|
|
5536
|
+
const pidPath = join$1(lockPath, "owner.pid");
|
|
5527
5537
|
const deadline = Date.now() + deadlineMs;
|
|
5528
5538
|
let held = false;
|
|
5529
5539
|
while (Date.now() < deadline) {
|
|
5530
5540
|
try {
|
|
5531
5541
|
mkdirSync$1(lockPath);
|
|
5532
5542
|
held = true;
|
|
5543
|
+
if (trackOwner) {
|
|
5544
|
+
try {
|
|
5545
|
+
writeFileSync$1(pidPath, String(process.pid));
|
|
5546
|
+
} catch {
|
|
5547
|
+
}
|
|
5548
|
+
}
|
|
5533
5549
|
break;
|
|
5534
5550
|
} catch {
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5551
|
+
let broke = false;
|
|
5552
|
+
if (trackOwner) {
|
|
5553
|
+
try {
|
|
5554
|
+
const owner = Number(readFileSync(pidPath, "utf8"));
|
|
5555
|
+
if (Number.isInteger(owner) && owner > 0 && owner !== process.pid && isProcessDead(owner)) {
|
|
5556
|
+
rmSync$1(lockPath, { recursive: true, force: true });
|
|
5557
|
+
broke = true;
|
|
5558
|
+
}
|
|
5559
|
+
} catch {
|
|
5560
|
+
}
|
|
5561
|
+
}
|
|
5562
|
+
if (!broke) {
|
|
5563
|
+
try {
|
|
5564
|
+
if (Date.now() - statSync(lockPath).mtimeMs > staleMs) {
|
|
5565
|
+
rmSync$1(lockPath, { recursive: true, force: true });
|
|
5566
|
+
broke = true;
|
|
5567
|
+
}
|
|
5568
|
+
} catch {
|
|
5539
5569
|
}
|
|
5540
|
-
} catch {
|
|
5541
5570
|
}
|
|
5571
|
+
if (broke) continue;
|
|
5542
5572
|
sleepSync$1(5);
|
|
5543
5573
|
}
|
|
5544
5574
|
}
|
|
5575
|
+
if (!held) {
|
|
5576
|
+
try {
|
|
5577
|
+
opts?.onFailOpen?.(lockPath);
|
|
5578
|
+
} catch {
|
|
5579
|
+
}
|
|
5580
|
+
}
|
|
5545
5581
|
try {
|
|
5546
5582
|
return fn();
|
|
5547
5583
|
} finally {
|
|
@@ -5554,6 +5590,33 @@ function withFileLock(lockPath, fn, opts) {
|
|
|
5554
5590
|
}
|
|
5555
5591
|
}
|
|
5556
5592
|
|
|
5593
|
+
function revGuardedMutate(lockPath, io, mutate, opts) {
|
|
5594
|
+
const deadlineMs = opts?.deadlineMs ?? 2e3;
|
|
5595
|
+
const maxAttempts = opts?.maxAttempts ?? 50;
|
|
5596
|
+
let last;
|
|
5597
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
5598
|
+
const settled = withFileLock(lockPath, () => {
|
|
5599
|
+
const base = io.read();
|
|
5600
|
+
const m = mutate(base.state);
|
|
5601
|
+
last = m.result;
|
|
5602
|
+
if (m.next === null) return { committed: true, result: m.result };
|
|
5603
|
+
if (opts?._seam) opts._seam(attempt);
|
|
5604
|
+
const again = io.read();
|
|
5605
|
+
if (again.rev !== base.rev) return { committed: false, result: m.result };
|
|
5606
|
+
io.write(m.next, base.rev + 1);
|
|
5607
|
+
return { committed: true, result: m.result };
|
|
5608
|
+
}, {
|
|
5609
|
+
deadlineMs,
|
|
5610
|
+
breakOnDeadOwner: true,
|
|
5611
|
+
onFailOpen: opts?.onFailOpen
|
|
5612
|
+
});
|
|
5613
|
+
last = settled.result;
|
|
5614
|
+
if (settled.committed) return settled.result;
|
|
5615
|
+
sleepSync$1(3 + attempt % 5);
|
|
5616
|
+
}
|
|
5617
|
+
return last;
|
|
5618
|
+
}
|
|
5619
|
+
|
|
5557
5620
|
const HARD_MAX_BYTES = 25 * 1024 * 1024;
|
|
5558
5621
|
const DEFAULT_MAX_BYTES = 5 * 1024 * 1024;
|
|
5559
5622
|
const DEFAULT_MAX_COUNT = 100;
|
|
@@ -5805,6 +5868,23 @@ const _CHANNEL_CACHE_TTL_MS = 1e3;
|
|
|
5805
5868
|
function invalidateChannelCache(dir) {
|
|
5806
5869
|
_channelListCache.delete(dir);
|
|
5807
5870
|
}
|
|
5871
|
+
function makeRmwSeam() {
|
|
5872
|
+
const delayMs = Number(process.env.SVAMP_TEST_RMW_DELAY_MS || 0);
|
|
5873
|
+
if (!delayMs || !Number.isFinite(delayMs)) return void 0;
|
|
5874
|
+
const readyFile = process.env.SVAMP_TEST_RMW_READY_FILE;
|
|
5875
|
+
let fired = false;
|
|
5876
|
+
return (attempt) => {
|
|
5877
|
+
if (attempt !== 0 || fired) return;
|
|
5878
|
+
fired = true;
|
|
5879
|
+
if (readyFile) {
|
|
5880
|
+
try {
|
|
5881
|
+
writeFileSync$1(readyFile, "1");
|
|
5882
|
+
} catch {
|
|
5883
|
+
}
|
|
5884
|
+
}
|
|
5885
|
+
sleepSync$1(delayMs);
|
|
5886
|
+
};
|
|
5887
|
+
}
|
|
5808
5888
|
class ChannelStore {
|
|
5809
5889
|
dir;
|
|
5810
5890
|
constructor(projectDir) {
|
|
@@ -5818,12 +5898,18 @@ class ChannelStore {
|
|
|
5818
5898
|
}
|
|
5819
5899
|
_lock(id) {
|
|
5820
5900
|
if (!isValidChannelId(id)) throw new Error(`invalid channel id: ${String(id).slice(0, 64)}`);
|
|
5901
|
+
mkdirSync$1(this.dir, { recursive: true });
|
|
5821
5902
|
return join$1(this.dir, `${id}.json.lock`);
|
|
5822
5903
|
}
|
|
5904
|
+
/** The default-filled canonical form of a channel (the minimal fields the store guarantees).
|
|
5905
|
+
* Extracted so `save()` can return the SAME shape it persists without re-running the write. */
|
|
5906
|
+
_applyDefaults(channel) {
|
|
5907
|
+
return { enabled: true, bind: "dynamic", template: DEFAULT_TEMPLATE, last_calls: [], ...channel };
|
|
5908
|
+
}
|
|
5823
5909
|
// #0679: the actual validate + atomic write, WITHOUT the lock, so a locked RMW mutator can
|
|
5824
5910
|
// reuse it inside its own lock section (no re-entrant re-lock / deadlock).
|
|
5825
5911
|
_writeChannel(channel) {
|
|
5826
|
-
const c =
|
|
5912
|
+
const c = this._applyDefaults(channel);
|
|
5827
5913
|
if (!c.id) c.id = genId();
|
|
5828
5914
|
const errs = validateChannel(c);
|
|
5829
5915
|
if (errs.length) throw new Error("invalid channel: " + errs.join("; "));
|
|
@@ -5875,8 +5961,11 @@ class ChannelStore {
|
|
|
5875
5961
|
}
|
|
5876
5962
|
}
|
|
5877
5963
|
save(channel) {
|
|
5878
|
-
if (channel.id) return
|
|
5879
|
-
return this.
|
|
5964
|
+
if (!channel.id) return this._writeChannel(channel);
|
|
5965
|
+
return this._rmw(channel.id, "save", () => {
|
|
5966
|
+
const merged = this._applyDefaults(channel);
|
|
5967
|
+
return { next: merged, result: merged };
|
|
5968
|
+
});
|
|
5880
5969
|
}
|
|
5881
5970
|
remove(id) {
|
|
5882
5971
|
return withFileLock(this._lock(id), () => {
|
|
@@ -5887,41 +5976,85 @@ class ChannelStore {
|
|
|
5887
5976
|
return true;
|
|
5888
5977
|
}
|
|
5889
5978
|
return false;
|
|
5979
|
+
}, {
|
|
5980
|
+
deadlineMs: 2e3,
|
|
5981
|
+
breakOnDeadOwner: true,
|
|
5982
|
+
onFailOpen: () => console.warn(`[channels] remove(${id}): lock contended for 2000ms \u2014 proceeding WITHOUT it`)
|
|
5890
5983
|
});
|
|
5891
5984
|
}
|
|
5892
|
-
// #0679: setEnabled/recordCall/addCaller are read-modify-write mutators — multiple
|
|
5893
|
-
// instances (one per session) point at the same
|
|
5894
|
-
// serialization two concurrent RMWs both read the same base
|
|
5895
|
-
// first (addCaller silently DROPS a freshly-generated caller
|
|
5896
|
-
//
|
|
5897
|
-
|
|
5985
|
+
// #0679: setEnabled/recordCall/addCaller/removeCaller are read-modify-write mutators — multiple
|
|
5986
|
+
// ChannelStore instances (one per session, plus the CLI) point at the same
|
|
5987
|
+
// .svamp/channels/<id>.json, so without serialization two concurrent RMWs both read the same base
|
|
5988
|
+
// and the second write clobbers the first (addCaller silently DROPS a freshly-generated caller
|
|
5989
|
+
// key; removeCaller silently RESURRECTS a revoked one — a SECURITY outcome).
|
|
5990
|
+
/**
|
|
5991
|
+
* #1226: the STRUCTURAL fix for those lost updates — a rev-guarded read-modify-write.
|
|
5992
|
+
*
|
|
5993
|
+
* withFileLock is bounded and FAIL-OPEN by design: on timeout it runs the mutation WITHOUT the
|
|
5994
|
+
* lock rather than hanging the daemon's single thread. That fail-open is a genuine lost-update
|
|
5995
|
+
* window, and merely widening the deadline (the earlier 50→2000 change) only shrinks the
|
|
5996
|
+
* PROBABILITY proportional to N×holdTime — the child's barrier harness reproduced 36-of-48
|
|
5997
|
+
* mutations lost at deadline=2000/hold=50/N=48. `revGuardedMutate` instead embeds a monotonic
|
|
5998
|
+
* `_rev`: it reads the base at rev R, applies the mutation, RE-READS immediately before writing,
|
|
5999
|
+
* and if the rev has advanced (a concurrent commit landed in its window) it retries against the
|
|
6000
|
+
* fresh base rather than clobbering. When the lock genuinely holds this is airtight; when it
|
|
6001
|
+
* fails open it collapses the loss window to the sub-millisecond re-read→rename gap (see
|
|
6002
|
+
* util/revLock.ts for the honest residual). `breakOnDeadOwner` (enabled inside revLock) also
|
|
6003
|
+
* closes the #1228 gap where a crashed holder's lock was never broken because deadlineMs(2000)
|
|
6004
|
+
* < staleMs(5000) — safe precisely because the rev guard tolerates the transient double-writer.
|
|
6005
|
+
*
|
|
6006
|
+
* recordCall uses a SHORT deadline + few attempts: it runs on every channel send (a hot path)
|
|
6007
|
+
* and all it can lose is one entry of a 20-deep rolling log, so it must not block the loop — but
|
|
6008
|
+
* it is still rev-guarded, so it no longer silently drops a log entry to a concurrent mutator
|
|
6009
|
+
* when it does acquire.
|
|
6010
|
+
*/
|
|
6011
|
+
_rmw(id, op, mutate, opts) {
|
|
6012
|
+
return revGuardedMutate(
|
|
6013
|
+
this._lock(id),
|
|
6014
|
+
{
|
|
6015
|
+
read: () => {
|
|
6016
|
+
const c = this.get(id);
|
|
6017
|
+
return { state: c, rev: c?._rev ?? 0 };
|
|
6018
|
+
},
|
|
6019
|
+
write: (state, rev) => {
|
|
6020
|
+
state._rev = rev;
|
|
6021
|
+
this._writeChannel(state);
|
|
6022
|
+
}
|
|
6023
|
+
},
|
|
6024
|
+
mutate,
|
|
6025
|
+
{
|
|
6026
|
+
deadlineMs: opts?.deadlineMs ?? 2e3,
|
|
6027
|
+
maxAttempts: opts?.maxAttempts,
|
|
6028
|
+
onFailOpen: () => {
|
|
6029
|
+
console.warn(`[channels] ${op}(${id}): lock contended \u2014 proceeding WITHOUT it (rev-guarded); a concurrent update is still highly unlikely to be lost`);
|
|
6030
|
+
},
|
|
6031
|
+
_seam: makeRmwSeam()
|
|
6032
|
+
}
|
|
6033
|
+
);
|
|
6034
|
+
}
|
|
5898
6035
|
setEnabled(id, enabled) {
|
|
5899
|
-
return
|
|
5900
|
-
|
|
5901
|
-
if (!c) return null;
|
|
6036
|
+
return this._rmw(id, "setEnabled", (c) => {
|
|
6037
|
+
if (!c) return { next: null, result: null };
|
|
5902
6038
|
c.enabled = enabled;
|
|
5903
|
-
return
|
|
6039
|
+
return { next: c, result: c };
|
|
5904
6040
|
});
|
|
5905
6041
|
}
|
|
5906
6042
|
recordCall(id, entry) {
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
if (!c) return;
|
|
6043
|
+
this._rmw(id, "recordCall", (c) => {
|
|
6044
|
+
if (!c) return { next: null, result: void 0 };
|
|
5910
6045
|
c.last_calls = c.last_calls || [];
|
|
5911
6046
|
c.last_calls.unshift({ at: (/* @__PURE__ */ new Date()).toISOString(), ...entry });
|
|
5912
6047
|
c.last_calls = c.last_calls.slice(0, 20);
|
|
5913
|
-
|
|
5914
|
-
});
|
|
6048
|
+
return { next: c, result: void 0 };
|
|
6049
|
+
}, { deadlineMs: 200, maxAttempts: 3 });
|
|
5915
6050
|
}
|
|
5916
6051
|
addCaller(id, name, kind = "agent") {
|
|
5917
|
-
return
|
|
5918
|
-
|
|
5919
|
-
if (!c) return null;
|
|
6052
|
+
return this._rmw(id, "addCaller", (c) => {
|
|
6053
|
+
if (!c) return { next: null, result: null };
|
|
5920
6054
|
c.identity.callers = c.identity.callers || [];
|
|
5921
6055
|
const caller = { name, kind, key: genKey() };
|
|
5922
6056
|
c.identity.callers.push(caller);
|
|
5923
|
-
|
|
5924
|
-
return caller;
|
|
6057
|
+
return { next: c, result: caller };
|
|
5925
6058
|
});
|
|
5926
6059
|
}
|
|
5927
6060
|
/**
|
|
@@ -5936,14 +6069,12 @@ class ChannelStore {
|
|
|
5936
6069
|
* resurrect the revoked entry by writing a stale base. Returns true when a caller was removed.
|
|
5937
6070
|
*/
|
|
5938
6071
|
removeCaller(id, name) {
|
|
5939
|
-
return
|
|
5940
|
-
|
|
5941
|
-
if (!c?.identity?.callers) return false;
|
|
6072
|
+
return this._rmw(id, "removeCaller", (c) => {
|
|
6073
|
+
if (!c?.identity?.callers) return { next: null, result: false };
|
|
5942
6074
|
const before = c.identity.callers.length;
|
|
5943
6075
|
c.identity.callers = c.identity.callers.filter((x) => x.name !== name);
|
|
5944
|
-
if (c.identity.callers.length === before) return false;
|
|
5945
|
-
|
|
5946
|
-
return true;
|
|
6076
|
+
if (c.identity.callers.length === before) return { next: null, result: false };
|
|
6077
|
+
return { next: c, result: true };
|
|
5947
6078
|
});
|
|
5948
6079
|
}
|
|
5949
6080
|
}
|
|
@@ -8378,7 +8509,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
8378
8509
|
}
|
|
8379
8510
|
const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
|
|
8380
8511
|
const sender = { name: context?.user?.email || context?.user?.id || "user", kind: "user", verified: true };
|
|
8381
|
-
const { toolsForRole } = await import('./sideband-
|
|
8512
|
+
const { toolsForRole } = await import('./sideband-D5Kan4J1.mjs');
|
|
8382
8513
|
const r2 = await runWiseAgent({ message: params.message, sender, config: { tools: toolsForRole(role2) }, deps, transport, model: resolved.model });
|
|
8383
8514
|
return fmt(r2);
|
|
8384
8515
|
}
|
|
@@ -8510,7 +8641,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
8510
8641
|
return { ok: false, call_id: callId, status: "busy", error: "channel is busy (too many concurrent requests) \u2014 retry shortly" };
|
|
8511
8642
|
}
|
|
8512
8643
|
const rendered = renderMessage(c, { sender: r.sender, body: { message: kwargs.message }, callId });
|
|
8513
|
-
const { queryCore } = await import('./commands-
|
|
8644
|
+
const { queryCore } = await import('./commands-Dw_oiiqX.mjs');
|
|
8514
8645
|
const timeout = c.reply?.timeout_sec || 120;
|
|
8515
8646
|
let result;
|
|
8516
8647
|
let thrownSessionId;
|
|
@@ -18428,6 +18559,22 @@ You share this machine and project folder with other agent sessions (same user,
|
|
|
18428
18559
|
}
|
|
18429
18560
|
|
|
18430
18561
|
const STANDARD_WINDOWS = [2e5, 1e6];
|
|
18562
|
+
const DEFAULT_MAX_CONTEXT_WINDOW = 2e5;
|
|
18563
|
+
function resolveMaxContextWindow(env = process.env) {
|
|
18564
|
+
const raw = (env.SVAMP_MAX_CONTEXT_WINDOW ?? "").trim().toLowerCase();
|
|
18565
|
+
if (!raw) return DEFAULT_MAX_CONTEXT_WINDOW;
|
|
18566
|
+
if (["0", "off", "none", "unlimited", "inf", "infinite", "-1", "false", "no"].includes(raw)) {
|
|
18567
|
+
return 0;
|
|
18568
|
+
}
|
|
18569
|
+
const n = Number(raw);
|
|
18570
|
+
if (Number.isFinite(n) && n > 0) return Math.floor(n);
|
|
18571
|
+
return DEFAULT_MAX_CONTEXT_WINDOW;
|
|
18572
|
+
}
|
|
18573
|
+
function applyMaxContextWindow(window, cap) {
|
|
18574
|
+
if (!cap || cap <= 0) return window;
|
|
18575
|
+
if (!window || window <= 0) return window;
|
|
18576
|
+
return Math.min(window, cap);
|
|
18577
|
+
}
|
|
18431
18578
|
function readWindow(entry) {
|
|
18432
18579
|
if (!entry) return 0;
|
|
18433
18580
|
const cw = entry.contextWindow ?? entry.context_window;
|
|
@@ -18520,6 +18667,16 @@ function decideProactiveCompaction(opts) {
|
|
|
18520
18667
|
if (pct >= ratio) return { shouldCompact: true, window, pct, reason: "threshold" };
|
|
18521
18668
|
return { shouldCompact: false, window, pct, reason: "below-threshold" };
|
|
18522
18669
|
}
|
|
18670
|
+
function shouldPreTurnCompact(input) {
|
|
18671
|
+
if (!input.enabled || input.disabled) return false;
|
|
18672
|
+
if (input.incomingIsCompact) return false;
|
|
18673
|
+
return decideProactiveCompaction({
|
|
18674
|
+
observed: input.observed,
|
|
18675
|
+
window: input.window,
|
|
18676
|
+
ratio: input.ratio,
|
|
18677
|
+
enabled: input.enabled
|
|
18678
|
+
}).shouldCompact;
|
|
18679
|
+
}
|
|
18523
18680
|
function fmtK(n) {
|
|
18524
18681
|
return `${Math.round((n || 0) / 1e3)}K`;
|
|
18525
18682
|
}
|
|
@@ -18584,6 +18741,26 @@ function decideCompactUnsupported(input) {
|
|
|
18584
18741
|
function describeNoBoundary(strikes, limit) {
|
|
18585
18742
|
return `Automatic compaction ran but reported no compaction boundary (attempt ${strikes} of ${limit}) \u2014 usually because there was nothing left to compact. Keeping automatic compaction enabled.`;
|
|
18586
18743
|
}
|
|
18744
|
+
function shouldAccountLoopTurn(state) {
|
|
18745
|
+
if (state.justCompacted) return false;
|
|
18746
|
+
if (state.compactPending) return false;
|
|
18747
|
+
return true;
|
|
18748
|
+
}
|
|
18749
|
+
function shouldPersistObservedContext(state) {
|
|
18750
|
+
if (state.justCompacted) return false;
|
|
18751
|
+
if (state.compactPending) return false;
|
|
18752
|
+
if (state.turnErrored) return false;
|
|
18753
|
+
return true;
|
|
18754
|
+
}
|
|
18755
|
+
function postCompactionObservedContext(compactMetadata) {
|
|
18756
|
+
const md = compactMetadata;
|
|
18757
|
+
if (!md || typeof md !== "object") return 0;
|
|
18758
|
+
for (const key of ["post_tokens", "postTokens", "post_token_count", "tokens_after", "after_tokens"]) {
|
|
18759
|
+
const v = md[key];
|
|
18760
|
+
if (typeof v === "number" && Number.isFinite(v) && v > 0) return Math.floor(v);
|
|
18761
|
+
}
|
|
18762
|
+
return 0;
|
|
18763
|
+
}
|
|
18587
18764
|
|
|
18588
18765
|
function fmtTokens(n) {
|
|
18589
18766
|
return Math.max(0, Math.round(n)).toLocaleString("en-US");
|
|
@@ -20715,7 +20892,7 @@ async function startDaemon(options) {
|
|
|
20715
20892
|
try {
|
|
20716
20893
|
const dir = loadSessionIndex()[sessionId]?.directory;
|
|
20717
20894
|
if (!dir) return;
|
|
20718
|
-
const { reconcileServiceLinks } = await import('./agentCommands-
|
|
20895
|
+
const { reconcileServiceLinks } = await import('./agentCommands-DmRCmbeU.mjs');
|
|
20719
20896
|
const configPath = getSvampConfigPath(dir, sessionId);
|
|
20720
20897
|
const config = readSvampConfig(configPath);
|
|
20721
20898
|
const entries = Array.from(urls.entries());
|
|
@@ -20737,7 +20914,7 @@ async function startDaemon(options) {
|
|
|
20737
20914
|
try {
|
|
20738
20915
|
const dir = loadSessionIndex()[sessionId]?.directory;
|
|
20739
20916
|
if (!dir) return;
|
|
20740
|
-
const { reconcileServiceLinks } = await import('./agentCommands-
|
|
20917
|
+
const { reconcileServiceLinks } = await import('./agentCommands-DmRCmbeU.mjs');
|
|
20741
20918
|
const configPath = getSvampConfigPath(dir, sessionId);
|
|
20742
20919
|
const config = readSvampConfig(configPath);
|
|
20743
20920
|
const incoming = [{
|
|
@@ -20758,7 +20935,7 @@ async function startDaemon(options) {
|
|
|
20758
20935
|
try {
|
|
20759
20936
|
const dir = loadSessionIndex()[sessionId]?.directory;
|
|
20760
20937
|
if (!dir) return;
|
|
20761
|
-
const { dropServiceLinks } = await import('./agentCommands-
|
|
20938
|
+
const { dropServiceLinks } = await import('./agentCommands-DmRCmbeU.mjs');
|
|
20762
20939
|
const configPath = getSvampConfigPath(dir, sessionId);
|
|
20763
20940
|
const config = readSvampConfig(configPath);
|
|
20764
20941
|
if (dropServiceLinks(config, "serve", mountName)) {
|
|
@@ -20774,7 +20951,7 @@ async function startDaemon(options) {
|
|
|
20774
20951
|
try {
|
|
20775
20952
|
const dir = loadSessionIndex()[sessionId]?.directory;
|
|
20776
20953
|
if (!dir) return;
|
|
20777
|
-
const { dropServiceLinks } = await import('./agentCommands-
|
|
20954
|
+
const { dropServiceLinks } = await import('./agentCommands-DmRCmbeU.mjs');
|
|
20778
20955
|
const configPath = getSvampConfigPath(dir, sessionId);
|
|
20779
20956
|
const config = readSvampConfig(configPath);
|
|
20780
20957
|
if (dropServiceLinks(config, "tunnel", tunnelName)) {
|
|
@@ -20786,9 +20963,12 @@ async function startDaemon(options) {
|
|
|
20786
20963
|
}
|
|
20787
20964
|
}
|
|
20788
20965
|
let liveSessionDirs;
|
|
20966
|
+
let serviceRegistriesRestored = false;
|
|
20967
|
+
const managedLinkSessions = /* @__PURE__ */ new Set();
|
|
20968
|
+
let managedLinkSessionsSeeded = false;
|
|
20789
20969
|
async function reconcileAllServiceLinks() {
|
|
20790
20970
|
try {
|
|
20791
|
-
const { reconcileServiceLinksExact } = await import('./agentCommands-
|
|
20971
|
+
const { reconcileServiceLinksExact, hasManagedServiceLink } = await import('./agentCommands-DmRCmbeU.mjs');
|
|
20792
20972
|
const desiredBySession = /* @__PURE__ */ new Map();
|
|
20793
20973
|
const push = (sessionId, entry) => {
|
|
20794
20974
|
if (!sessionId || !entry.url) return;
|
|
@@ -20816,7 +20996,19 @@ async function startDaemon(options) {
|
|
|
20816
20996
|
for (const s of liveSessionDirs?.() ?? []) {
|
|
20817
20997
|
if (s.sessionId && s.directory) dirBySession.set(s.sessionId, s.directory);
|
|
20818
20998
|
}
|
|
20819
|
-
|
|
20999
|
+
if (!managedLinkSessionsSeeded) {
|
|
21000
|
+
for (const [sessionId, dir] of dirBySession) {
|
|
21001
|
+
try {
|
|
21002
|
+
if (hasManagedServiceLink(readSvampConfig(getSvampConfigPath(dir, sessionId)), ["serve", "tunnel"])) {
|
|
21003
|
+
managedLinkSessions.add(sessionId);
|
|
21004
|
+
}
|
|
21005
|
+
} catch {
|
|
21006
|
+
}
|
|
21007
|
+
}
|
|
21008
|
+
managedLinkSessionsSeeded = true;
|
|
21009
|
+
logger.log(`[launchpad] Seeded managed-link set: ${managedLinkSessions.size} of ${dirBySession.size} known sessions carry a serve/tunnel link`);
|
|
21010
|
+
}
|
|
21011
|
+
const sessionIds = /* @__PURE__ */ new Set([...managedLinkSessions, ...desiredBySession.keys()]);
|
|
20820
21012
|
for (const sessionId of sessionIds) {
|
|
20821
21013
|
try {
|
|
20822
21014
|
const dir = dirBySession.get(sessionId);
|
|
@@ -20824,9 +21016,14 @@ async function startDaemon(options) {
|
|
|
20824
21016
|
const configPath = getSvampConfigPath(dir, sessionId);
|
|
20825
21017
|
const config = readSvampConfig(configPath);
|
|
20826
21018
|
const desired = desiredBySession.get(sessionId) ?? [];
|
|
20827
|
-
if (reconcileServiceLinksExact(config, ["serve", "tunnel"], desired)) {
|
|
21019
|
+
if (reconcileServiceLinksExact(config, ["serve", "tunnel"], desired, { dropStale: serviceRegistriesRestored })) {
|
|
20828
21020
|
writeSvampConfig(configPath, config);
|
|
20829
|
-
logger.log(`[launchpad] Reconciled service links for session ${sessionId} (${desired.length} live)`);
|
|
21021
|
+
logger.log(`[launchpad] Reconciled service links for session ${sessionId} (${desired.length} live${serviceRegistriesRestored ? "" : ", upsert-only \u2014 restore in flight"})`);
|
|
21022
|
+
}
|
|
21023
|
+
if (desired.length > 0 || hasManagedServiceLink(config, ["serve", "tunnel"])) {
|
|
21024
|
+
managedLinkSessions.add(sessionId);
|
|
21025
|
+
} else {
|
|
21026
|
+
managedLinkSessions.delete(sessionId);
|
|
20830
21027
|
}
|
|
20831
21028
|
} catch (err) {
|
|
20832
21029
|
logger.log(`[launchpad] Reconcile failed for session ${sessionId}: ${err?.message || err}`);
|
|
@@ -21817,10 +22014,12 @@ ${parts.join("\n")}`);
|
|
|
21817
22014
|
let pendingCompactTurn = false;
|
|
21818
22015
|
const autoCompactEnabled = resolveAutoCompactEnabled();
|
|
21819
22016
|
const autoCompactRatio = resolveAutoCompactRatio();
|
|
22017
|
+
const maxContextWindow = resolveMaxContextWindow();
|
|
21820
22018
|
let autoCompactWasAutoInjected = false;
|
|
21821
22019
|
let autoCompactDisabled = false;
|
|
21822
22020
|
let autoCompactRecoveryTried = false;
|
|
21823
22021
|
let justCompactedTurn = false;
|
|
22022
|
+
let compactTurnLatch = false;
|
|
21824
22023
|
let compactNoBoundaryStrikes = 0;
|
|
21825
22024
|
let compactSupported = void 0;
|
|
21826
22025
|
let noIsolationWarned = false;
|
|
@@ -22306,14 +22505,25 @@ ${parts.join("\n")}`);
|
|
|
22306
22505
|
}
|
|
22307
22506
|
signalProcessing(false);
|
|
22308
22507
|
sessionWasProcessing = false;
|
|
22309
|
-
const resolvedWindow = resolveContextWindow({
|
|
22508
|
+
const resolvedWindow = applyMaxContextWindow(resolveContextWindow({
|
|
22310
22509
|
modelUsage: msg.modelUsage,
|
|
22311
22510
|
usage: msg.usage,
|
|
22312
22511
|
mainModel: lastMainModel,
|
|
22313
22512
|
currentWindow: sessionMetadata.contextWindow
|
|
22513
|
+
}), maxContextWindow);
|
|
22514
|
+
const observedThisTurn = computeObservedContext(msg.usage);
|
|
22515
|
+
const mayPersistObserved = shouldPersistObservedContext({
|
|
22516
|
+
justCompacted: justCompactedTurn,
|
|
22517
|
+
compactPending: compactTurnLatch,
|
|
22518
|
+
turnErrored: !!msg.is_error
|
|
22314
22519
|
});
|
|
22315
|
-
|
|
22316
|
-
|
|
22520
|
+
const persistObserved = mayPersistObserved && observedThisTurn > 0 && observedThisTurn !== sessionMetadata.observedContext;
|
|
22521
|
+
if (resolvedWindow > 0 && resolvedWindow !== sessionMetadata.contextWindow || persistObserved) {
|
|
22522
|
+
sessionMetadata = {
|
|
22523
|
+
...sessionMetadata,
|
|
22524
|
+
...resolvedWindow > 0 ? { contextWindow: resolvedWindow } : {},
|
|
22525
|
+
...persistObserved ? { observedContext: observedThisTurn } : {}
|
|
22526
|
+
};
|
|
22317
22527
|
sessionService.updateMetadata(sessionMetadata);
|
|
22318
22528
|
}
|
|
22319
22529
|
if (claudeResumeId && !trackedSession.stopped) {
|
|
@@ -22333,7 +22543,11 @@ ${parts.join("\n")}`);
|
|
|
22333
22543
|
clearInboundContext(sessionId);
|
|
22334
22544
|
try {
|
|
22335
22545
|
const ls = readLoopState(directory, sessionId);
|
|
22336
|
-
|
|
22546
|
+
const accountLoopTurn = shouldAccountLoopTurn({
|
|
22547
|
+
justCompacted: justCompactedTurn,
|
|
22548
|
+
compactPending: compactTurnLatch
|
|
22549
|
+
});
|
|
22550
|
+
if (accountLoopTurn && ls && ls.engine === "goal" && ls.active !== false && ls.phase !== "dormant" && ls.phase !== "done" && ls.phase !== "gave_up" && ls.phase !== "cancelled") {
|
|
22337
22551
|
const now = Date.now();
|
|
22338
22552
|
const ledger = accumulateLedger(ls.ledger, {
|
|
22339
22553
|
turns: Number(msg.num_turns) || 0,
|
|
@@ -22390,6 +22604,7 @@ ${parts.join("\n")}`);
|
|
|
22390
22604
|
enabled: autoCompactEnabled
|
|
22391
22605
|
});
|
|
22392
22606
|
justCompactedTurn = false;
|
|
22607
|
+
compactTurnLatch = false;
|
|
22393
22608
|
if (mayEvaluateLayer1) {
|
|
22394
22609
|
try {
|
|
22395
22610
|
const observed = computeObservedContext(msg.usage);
|
|
@@ -22509,7 +22724,16 @@ ${parts.join("\n")}`);
|
|
|
22509
22724
|
autoCompactWasAutoInjected = false;
|
|
22510
22725
|
autoCompactRecoveryTried = false;
|
|
22511
22726
|
justCompactedTurn = true;
|
|
22727
|
+
compactTurnLatch = true;
|
|
22512
22728
|
compactNoBoundaryStrikes = 0;
|
|
22729
|
+
try {
|
|
22730
|
+
const postObserved = postCompactionObservedContext(msg.compact_metadata);
|
|
22731
|
+
if (sessionMetadata.observedContext !== postObserved) {
|
|
22732
|
+
sessionMetadata = { ...sessionMetadata, observedContext: postObserved };
|
|
22733
|
+
sessionService.updateMetadata(sessionMetadata);
|
|
22734
|
+
}
|
|
22735
|
+
} catch {
|
|
22736
|
+
}
|
|
22513
22737
|
const compactLine = describeCompaction(msg.compact_metadata);
|
|
22514
22738
|
logger.log(`[Session ${sessionId}] ${compactLine}`);
|
|
22515
22739
|
sessionService.pushMessage(
|
|
@@ -22938,10 +23162,41 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
22938
23162
|
rateLimitRetryTimer = null;
|
|
22939
23163
|
}
|
|
22940
23164
|
pendingCompactTurn = isCompactCommand(text);
|
|
23165
|
+
if (pendingCompactTurn) compactTurnLatch = true;
|
|
22941
23166
|
if (!claudeProcess || claudeProcess.exitCode !== null) {
|
|
22942
|
-
|
|
22943
|
-
|
|
22944
|
-
|
|
23167
|
+
const preTurnWindow = applyMaxContextWindow(sessionMetadata.contextWindow || 0, maxContextWindow);
|
|
23168
|
+
const preTurnObserved = sessionMetadata.observedContext || 0;
|
|
23169
|
+
if (shouldPreTurnCompact({
|
|
23170
|
+
observed: preTurnObserved,
|
|
23171
|
+
window: preTurnWindow,
|
|
23172
|
+
ratio: autoCompactRatio,
|
|
23173
|
+
enabled: autoCompactEnabled,
|
|
23174
|
+
disabled: autoCompactDisabled,
|
|
23175
|
+
incomingIsCompact: isCompactCommand(text)
|
|
23176
|
+
})) {
|
|
23177
|
+
const pct = preTurnWindow > 0 ? preTurnObserved / preTurnWindow : 0;
|
|
23178
|
+
logger.log(`[Session ${sessionId}] Pre-turn auto-compaction: persisted observed ${preTurnObserved} tokens at ${Math.round(pct * 100)}% of ${preTurnWindow} window \u2014 compacting before delivering message`);
|
|
23179
|
+
sessionService.pushMessage(
|
|
23180
|
+
{ type: "message", message: describeAutoCompact(preTurnObserved, preTurnWindow, pct), level: "warning" },
|
|
23181
|
+
"event"
|
|
23182
|
+
);
|
|
23183
|
+
const existingQueue = sessionMetadata.messageQueue || [];
|
|
23184
|
+
sessionMetadata = {
|
|
23185
|
+
...sessionMetadata,
|
|
23186
|
+
messageQueue: [...existingQueue, { id: randomUUID(), text, createdAt: Date.now(), alreadyStored: true }]
|
|
23187
|
+
};
|
|
23188
|
+
sessionService.updateMetadata(sessionMetadata);
|
|
23189
|
+
pendingCompactTurn = true;
|
|
23190
|
+
compactTurnLatch = true;
|
|
23191
|
+
autoCompactWasAutoInjected = true;
|
|
23192
|
+
spawnClaude("/compact", msgMeta);
|
|
23193
|
+
signalProcessing(true);
|
|
23194
|
+
sessionWasProcessing = true;
|
|
23195
|
+
} else {
|
|
23196
|
+
spawnClaude(text, msgMeta);
|
|
23197
|
+
signalProcessing(true);
|
|
23198
|
+
sessionWasProcessing = true;
|
|
23199
|
+
}
|
|
22945
23200
|
} else if (writeUserFrameToClaude(text)) {
|
|
22946
23201
|
signalProcessing(true);
|
|
22947
23202
|
sessionWasProcessing = true;
|
|
@@ -23326,11 +23581,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
23326
23581
|
});
|
|
23327
23582
|
},
|
|
23328
23583
|
onIssue: async (params) => {
|
|
23329
|
-
const { issueRpc } = await import('./rpc-
|
|
23584
|
+
const { issueRpc } = await import('./rpc-Cj31v2sm.mjs');
|
|
23330
23585
|
return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
|
|
23331
23586
|
},
|
|
23332
23587
|
onWorkflow: async (params) => {
|
|
23333
|
-
const { workflowRpc } = await import('./rpc-
|
|
23588
|
+
const { workflowRpc } = await import('./rpc-BSWkkPHd.mjs');
|
|
23334
23589
|
return workflowRpc(params?.cwd || directory, params || {});
|
|
23335
23590
|
},
|
|
23336
23591
|
onRipgrep: async (args, cwd) => {
|
|
@@ -23470,6 +23725,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
23470
23725
|
turnInitiatedByUser = true;
|
|
23471
23726
|
currentTurnMessage = next.text;
|
|
23472
23727
|
pendingCompactTurn = isCompactCommand(next.text);
|
|
23728
|
+
if (pendingCompactTurn) compactTurnLatch = true;
|
|
23473
23729
|
autoCompactWasAutoInjected = pendingCompactTurn && !!next.autoCompact;
|
|
23474
23730
|
resetRateLimitStreak();
|
|
23475
23731
|
if (rateLimitRetryTimer) {
|
|
@@ -24053,11 +24309,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
24053
24309
|
});
|
|
24054
24310
|
},
|
|
24055
24311
|
onIssue: async (params) => {
|
|
24056
|
-
const { issueRpc } = await import('./rpc-
|
|
24312
|
+
const { issueRpc } = await import('./rpc-Cj31v2sm.mjs');
|
|
24057
24313
|
return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
|
|
24058
24314
|
},
|
|
24059
24315
|
onWorkflow: async (params) => {
|
|
24060
|
-
const { workflowRpc } = await import('./rpc-
|
|
24316
|
+
const { workflowRpc } = await import('./rpc-BSWkkPHd.mjs');
|
|
24061
24317
|
return workflowRpc(params?.cwd || directory, params || {});
|
|
24062
24318
|
},
|
|
24063
24319
|
onRipgrep: async (args, cwd) => {
|
|
@@ -25058,8 +25314,12 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
25058
25314
|
} catch (err) {
|
|
25059
25315
|
logger.log(`[serve] mount link reconcile failed: ${err?.message || err}`);
|
|
25060
25316
|
}
|
|
25317
|
+
serviceRegistriesRestored = true;
|
|
25061
25318
|
scheduleServiceLinkReconcile(3e3);
|
|
25062
|
-
}).catch((err) =>
|
|
25319
|
+
}).catch((err) => {
|
|
25320
|
+
serviceRegistriesRestored = true;
|
|
25321
|
+
logger.error(`[serve] mount restore failed: ${err?.message || err}`);
|
|
25322
|
+
});
|
|
25063
25323
|
const daemonOwnerEmail = parseJwtEmail(process.env.HYPHA_TOKEN || "") || null;
|
|
25064
25324
|
serveManager$1.setSessionResolver((sessionId) => {
|
|
25065
25325
|
for (const [, session] of pidToTrackedSession) {
|
|
@@ -25408,7 +25668,7 @@ ${oracle.output.trim().slice(0, 500)}`, "\u{1F501} Continuing loop");
|
|
|
25408
25668
|
const PING_TIMEOUT_MS = 15e3;
|
|
25409
25669
|
const POST_RECONNECT_GRACE_MS = 2e4;
|
|
25410
25670
|
const RECONNECT_JITTER_MS = 2500;
|
|
25411
|
-
const { WorkflowScheduler } = await import('./scheduler-
|
|
25671
|
+
const { WorkflowScheduler } = await import('./scheduler-DkfqQjih.mjs');
|
|
25412
25672
|
const workflowProjectRoots = () => {
|
|
25413
25673
|
const dirs = /* @__PURE__ */ new Set();
|
|
25414
25674
|
for (const s of pidToTrackedSession.values()) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { f as resolveProjectRoot, E as listWorkflows, F as isWorkflowEnabled, G as workflowSchedules, H as inZone, x as runWorkflow, I as cronMatches } from './run-
|
|
1
|
+
import { f as resolveProjectRoot, E as listWorkflows, F as isWorkflowEnabled, G as workflowSchedules, H as inZone, x as runWorkflow, I as cronMatches } from './run-bo0i02Dy.mjs';
|
|
2
2
|
import 'os';
|
|
3
3
|
import 'fs/promises';
|
|
4
4
|
import 'fs';
|