troxy-cli 1.29.50 → 1.29.51
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "troxy-cli",
|
|
3
|
-
"version": "1.29.
|
|
3
|
+
"version": "1.29.51",
|
|
4
4
|
"description": "A secure control layer for AI agents: policies across payments, messages, logins, destructive actions, model usage, and secrets, all enforceable from the CLI",
|
|
5
5
|
"homepage": "https://troxy.io",
|
|
6
6
|
"bugs": {
|
|
@@ -280,9 +280,14 @@ function cancelCompact() {
|
|
|
280
280
|
// stayAwake: hold off system sleep (the screen still sleeps) until the timer fires, so a
|
|
281
281
|
// big session compacts on time instead of when the user is back. macOS caffeinate; where
|
|
282
282
|
// it is missing the spawn fails quietly and compact-on-return in scorePrompt covers it.
|
|
283
|
-
function armCompact($, ms, { stayAwake = false } = {}) {
|
|
283
|
+
function armCompact($, ms, { stayAwake = false, persist = false } = {}) {
|
|
284
284
|
cancelCompact();
|
|
285
285
|
if (stayAwake) keepAwake($, ms + 2 * 60 * 1000);
|
|
286
|
+
// Persisted so a restart (which loses lastSent and the timer) still finds the idle start.
|
|
287
|
+
if (persist) try {
|
|
288
|
+
Promise.all([$.session.id(), $.clock.now()])
|
|
289
|
+
.then(([sid, at]) => $.store.set(`idle_started_${sid}`, at)).catch(() => {});
|
|
290
|
+
} catch {}
|
|
286
291
|
compactTimer = $.clock.after(ms, () => {
|
|
287
292
|
compactTimer = null;
|
|
288
293
|
idleCompact($).catch(() => {}).finally(stopAwake);
|
|
@@ -346,10 +351,16 @@ async function scorePrompt($, e, next) {
|
|
|
346
351
|
cancelCompact();
|
|
347
352
|
if ((await $.state.get(COMPACTED)).value) await $.state.set(COMPACTED, null);
|
|
348
353
|
const sid = await $.session.id();
|
|
349
|
-
// Idle past the timer by the wall clock: a sleeping Mac freezes the timer
|
|
350
|
-
// compaction it missed runs here, before this prompt goes out
|
|
354
|
+
// Idle past the timer by the wall clock: a sleeping Mac freezes the timer and a restart
|
|
355
|
+
// loses it, so the compaction it missed runs here, before this prompt goes out.
|
|
356
|
+
const now = await $.clock.now();
|
|
351
357
|
const ls = lastSent.get(sid);
|
|
352
|
-
|
|
358
|
+
let idleSince = ls ? ls.at : null;
|
|
359
|
+
try {
|
|
360
|
+
const stored = await $.store.get(`idle_started_${sid}`);
|
|
361
|
+
if (typeof stored === 'number' && (idleSince == null || stored > idleSince)) idleSince = stored;
|
|
362
|
+
} catch {}
|
|
363
|
+
if (idleSince != null && now - idleSince >= COMPACT_IDLE_MS) {
|
|
353
364
|
try { await idleCompact($, { late: true }); } catch {} // fail open: send the prompt as-is
|
|
354
365
|
}
|
|
355
366
|
turn = { start: await snapshot($), ratio: 1 };
|
|
@@ -502,7 +513,7 @@ export function register(on) {
|
|
|
502
513
|
} catch {} // a missed fetch only leaves the band as it was
|
|
503
514
|
let stayAwake = false;
|
|
504
515
|
try { stayAwake = await bigSession($); } catch {}
|
|
505
|
-
armCompact($, COMPACT_IDLE_MS, { stayAwake });
|
|
516
|
+
armCompact($, COMPACT_IDLE_MS, { stayAwake, persist: true });
|
|
506
517
|
return r;
|
|
507
518
|
});
|
|
508
519
|
}
|
|
@@ -233,6 +233,28 @@ test('scorePrompt: back after 55+ min by the wall clock (Mac slept through the t
|
|
|
233
233
|
assert.ok(await runTurn($, opus)); // a failed compaction never blocks the prompt
|
|
234
234
|
});
|
|
235
235
|
|
|
236
|
+
test('scorePrompt: idle 55+ min across a Claude Code restart still compacts, from the saved idle start', async () => {
|
|
237
|
+
const { armCompact, cancelCompact } = _internal;
|
|
238
|
+
const $ = routingApi();
|
|
239
|
+
const db = new Map();
|
|
240
|
+
let now = 1000, compacts = 0;
|
|
241
|
+
$.store = { get: async k => db.get(k), set: async (k, v) => { db.set(k, v); } };
|
|
242
|
+
$.clock.now = async () => now;
|
|
243
|
+
$.clock.after = () => ({ cancel() {} });
|
|
244
|
+
$.session.usage = async () => ({ context: { tokens: 174000 }, rateLimits: [] });
|
|
245
|
+
$.session.compact = async () => { compacts++; return { tokensBefore: 174000, tokensAfter: 20000 }; };
|
|
246
|
+
armCompact($, 55 * 60 * 1000, { persist: true }); // end of the last turn before the restart
|
|
247
|
+
await new Promise(r => setImmediate(r));
|
|
248
|
+
assert.equal(typeof [...db.values()][0], 'number');
|
|
249
|
+
now += 40 * 60 * 1000;
|
|
250
|
+
armCompact($, 55 * 60 * 1000); // session.start after the restart: must not reset it
|
|
251
|
+
await new Promise(r => setImmediate(r));
|
|
252
|
+
now += 16 * 60 * 1000;
|
|
253
|
+
await runTurn($, _internal.MODELS.opus); // fresh process: no lastSent, only the store
|
|
254
|
+
cancelCompact();
|
|
255
|
+
assert.equal(compacts, 1);
|
|
256
|
+
});
|
|
257
|
+
|
|
236
258
|
test('armCompact: a big session keeps the Mac awake until the timer fires, then lets it sleep', async () => {
|
|
237
259
|
const { armCompact, cancelCompact } = _internal;
|
|
238
260
|
const spawned = [], stopped = [];
|