troxy-cli 1.29.58 → 1.29.60
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.60",
|
|
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": {
|
|
@@ -188,9 +188,9 @@ function sessionLine(t) {
|
|
|
188
188
|
// by the server. undefined until something was saved.
|
|
189
189
|
function savingsText(s) {
|
|
190
190
|
const c = (s && s.compressed_tokens) || 0, t = (s && s.trimmed_tokens) || 0;
|
|
191
|
-
const k = n => n >= 1000 ? `${Math.round(n / 1000)}k` : String(n);
|
|
191
|
+
const k = n => n >= 1e6 ? `${Math.round(n / 1e5) / 10}M`.replace('.0M', 'M') : n >= 1000 ? `${Math.round(n / 1000)}k` : String(n);
|
|
192
192
|
// Always show, even if 0, so the band is transparent: 0 means no trimming/compression on this turn
|
|
193
|
-
return `${k(c + t)} tokens
|
|
193
|
+
return `${k(c + t)} tokens not re-sent, thanks to compression and trimming`;
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
// This session's savings from the server, or null when there is no key, no session id or
|
|
@@ -252,6 +252,15 @@ function addTurn(totals, start, end, ratio) {
|
|
|
252
252
|
return { ...t, paid: t.paid + paid, full: t.full + paid * ratio, meter }; // keeps t.kept
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
+
// kept_usd from the server is the whole session's total, but paid only counts turns since
|
|
256
|
+
// the mod began tracking. Count only what arrived after the first reading (the first turn's
|
|
257
|
+
// own clearing is missed, so this undercounts slightly rather than inflating the multiplier).
|
|
258
|
+
function withKept(t, keptUsd) {
|
|
259
|
+
if (!(keptUsd >= 0)) return {};
|
|
260
|
+
const keptBase = t.keptBase === undefined ? keptUsd : t.keptBase;
|
|
261
|
+
return { keptBase, kept: Math.max(0, keptUsd - keptBase) };
|
|
262
|
+
}
|
|
263
|
+
|
|
255
264
|
// Idle compaction: shrink a big session just before its prompt cache expires (1h), so
|
|
256
265
|
// the summary reads the conversation from cache (~0.1x) and the user comes back to a
|
|
257
266
|
// small session instead of re-sending the whole thing. Never while the plan meter is
|
|
@@ -541,9 +550,7 @@ export function register(on) {
|
|
|
541
550
|
try {
|
|
542
551
|
const { value } = await $.state.get(SAVED);
|
|
543
552
|
const totals = addTurn(value, start, await snapshot($), ratio);
|
|
544
|
-
|
|
545
|
-
// since the mod started tracking; close enough once the session has a few turns.
|
|
546
|
-
if (savings && savings.kept_usd >= 0) totals.kept = savings.kept_usd;
|
|
553
|
+
if (savings) Object.assign(totals, withKept(totals, savings.kept_usd));
|
|
547
554
|
await $.state.set(SAVED, totals);
|
|
548
555
|
const line = sessionLine(totals);
|
|
549
556
|
if (line) await $.state.set(LINE, line);
|
|
@@ -557,4 +564,4 @@ export function register(on) {
|
|
|
557
564
|
}
|
|
558
565
|
|
|
559
566
|
// Exported for tests only - not part of the mod's runtime contract.
|
|
560
|
-
export const _internal = { askedFor, rememberAsked, asked, armCompact, cancelCompact, toDomainAndFields, guardBash, failClosed, parseScore, asRequest, pickModel, troxyCommand, modRouteFile, estimateTokens, MODELS, savedLine, sessionLine, addTurn, statusText, compactPlan, savingsText, fetchSavings, scorePrompt, routeStep, stepModel, HOLD_PROMPTS };
|
|
567
|
+
export const _internal = { askedFor, rememberAsked, asked, armCompact, cancelCompact, toDomainAndFields, guardBash, failClosed, parseScore, asRequest, pickModel, troxyCommand, modRouteFile, estimateTokens, MODELS, savedLine, sessionLine, addTurn, withKept, statusText, compactPlan, savingsText, fetchSavings, scorePrompt, routeStep, stepModel, HOLD_PROMPTS };
|
|
@@ -92,6 +92,14 @@ test('addTurn: a session total from its own turns, a routed one counted at the a
|
|
|
92
92
|
t = addTurn({ ...t, kept: 1.5 }, { cost: 2, meter: {} }, { cost: 2, meter: {} }, 1);
|
|
93
93
|
assert.equal(t.kept, 1.5);
|
|
94
94
|
assert.match(sessionLine(t), /your bar lasts 3\.0x longer · 5h: 3 instead of 9/);
|
|
95
|
+
// Server total of a session already 28 dollars deep: only growth after the first reading counts.
|
|
96
|
+
const { withKept } = _internal;
|
|
97
|
+
let k = { paid: 1, full: 1, meter: {} };
|
|
98
|
+
Object.assign(k, withKept(k, 28));
|
|
99
|
+
assert.deepEqual([k.keptBase, k.kept], [28, 0]);
|
|
100
|
+
Object.assign(k, withKept(k, 29.5));
|
|
101
|
+
assert.deepEqual([k.keptBase, k.kept], [28, 1.5]);
|
|
102
|
+
assert.deepEqual(withKept(k, undefined), {});
|
|
95
103
|
// No routed turn yet: no band.
|
|
96
104
|
assert.equal(sessionLine(addTurn(undefined, { cost: 0, meter: {} }, { cost: 1, meter: {} }, 1)), undefined);
|
|
97
105
|
});
|
|
@@ -126,10 +134,12 @@ test('compactPlan: big idle sessions compact, small ones and a nearly full bar d
|
|
|
126
134
|
|
|
127
135
|
test('savingsText: shows compression + trimming together, 0 when nothing was saved', () => {
|
|
128
136
|
const { savingsText } = _internal;
|
|
129
|
-
assert.equal(savingsText({ compressed_tokens: 5200, trimmed_tokens: 40100 }), '45k tokens
|
|
130
|
-
assert.equal(savingsText({ compressed_tokens: 300, trimmed_tokens: 0 }), '300 tokens
|
|
131
|
-
assert.equal(savingsText({ compressed_tokens: 0, trimmed_tokens: 0 }), '0 tokens
|
|
132
|
-
assert.equal(savingsText(
|
|
137
|
+
assert.equal(savingsText({ compressed_tokens: 5200, trimmed_tokens: 40100 }), '45k tokens not re-sent, thanks to compression and trimming');
|
|
138
|
+
assert.equal(savingsText({ compressed_tokens: 300, trimmed_tokens: 0 }), '300 tokens not re-sent, thanks to compression and trimming');
|
|
139
|
+
assert.equal(savingsText({ compressed_tokens: 0, trimmed_tokens: 0 }), '0 tokens not re-sent, thanks to compression and trimming');
|
|
140
|
+
assert.equal(savingsText({ compressed_tokens: 123277, trimmed_tokens: 154512861 }), '154.6M tokens not re-sent, thanks to compression and trimming');
|
|
141
|
+
assert.equal(savingsText({ compressed_tokens: 0, trimmed_tokens: 2000000 }), '2M tokens not re-sent, thanks to compression and trimming');
|
|
142
|
+
assert.equal(savingsText(null), '0 tokens not re-sent, thanks to compression and trimming');
|
|
133
143
|
});
|
|
134
144
|
|
|
135
145
|
test('fetchSavings: sends the saved key and the session id; null without a key or on an error', async () => {
|