troxy-cli 1.29.60 → 1.29.61
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.61",
|
|
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": {
|
|
@@ -440,13 +440,17 @@ const held = new Map();
|
|
|
440
440
|
|
|
441
441
|
// Which model a step runs on. At a cold moment (first step, 55+ min idle, after a
|
|
442
442
|
// compaction) any model is free to pick. While warm, hold the model, except at the
|
|
443
|
-
// first step of a new prompt once it has held HOLD_PROMPTS prompts
|
|
444
|
-
//
|
|
443
|
+
// first step of a new prompt: up at once, down once it has held HOLD_PROMPTS prompts,
|
|
444
|
+
// re-picking by score. The user's own /model always wins and becomes the held model.
|
|
445
445
|
function stepModel({ arm, requested, switched, cold, score, warm, newPrompt, heldPrompts, tokens }) {
|
|
446
446
|
if (arm !== 'treated' || switched) return requested;
|
|
447
447
|
if (cold) return pickModel(score, requested, 0, null);
|
|
448
|
-
if (newPrompt && score !== null &&
|
|
449
|
-
|
|
448
|
+
if (newPrompt && score !== null && tokens <= WARM_SWITCH_MAX_TOKENS) {
|
|
449
|
+
const pick = pickModel(score, requested, 0, null);
|
|
450
|
+
// Up is immediate: a cheap model chosen for an easy first prompt must not keep
|
|
451
|
+
// running a harder one (the 2026-10-15 A/B: Haiku held a whole coding session and
|
|
452
|
+
// made 2-4x the calls). Down still waits HOLD_PROMPTS, since it re-writes the cache.
|
|
453
|
+
if (RANK[tierOf(pick)] > RANK[tierOf(warm)] || heldPrompts >= HOLD_PROMPTS) return pick;
|
|
450
454
|
}
|
|
451
455
|
const w = tierOf(warm), rq = tierOf(requested);
|
|
452
456
|
return w !== null && rq !== null && RANK[w] <= RANK[rq] ? warm : requested;
|
|
@@ -215,6 +215,18 @@ test('stepModel: warm switch at a new prompt only after the hold, and under the
|
|
|
215
215
|
assert.equal(stepModel({ ...base, arm: 'holdout', newPrompt: true, heldPrompts: HOLD_PROMPTS }), M.opus);
|
|
216
216
|
});
|
|
217
217
|
|
|
218
|
+
test('stepModel: a harder prompt upgrades a cheap warm session at once, a lighter one waits', () => {
|
|
219
|
+
const { stepModel, MODELS: M, HOLD_PROMPTS } = _internal;
|
|
220
|
+
const base = { arm: 'treated', requested: M.opus, switched: false, cold: false, warm: M.haiku, newPrompt: true, tokens: 5000 };
|
|
221
|
+
assert.equal(stepModel({ ...base, score: 0.9, heldPrompts: 0 }), M.opus); // hard prompt on haiku: up now
|
|
222
|
+
assert.equal(stepModel({ ...base, score: 0.5, heldPrompts: 0 }), M.sonnet);
|
|
223
|
+
assert.equal(stepModel({ ...base, score: 0.1, heldPrompts: 0 }), M.haiku); // still easy: stay
|
|
224
|
+
assert.equal(stepModel({ ...base, warm: M.opus, score: 0.1, heldPrompts: 0 }), M.opus); // down waits
|
|
225
|
+
assert.equal(stepModel({ ...base, warm: M.opus, score: 0.1, heldPrompts: HOLD_PROMPTS }), M.haiku); // ...for the hold
|
|
226
|
+
assert.equal(stepModel({ ...base, score: 0.9, heldPrompts: 0, tokens: 300000 }), M.haiku); // too big to re-write
|
|
227
|
+
assert.equal(stepModel({ ...base, score: 0.9, heldPrompts: 0, newPrompt: false }), M.haiku); // mid-turn: hold
|
|
228
|
+
});
|
|
229
|
+
|
|
218
230
|
test('pickModel: past the floor, stay on the warm model unless the prompt needs a stronger one', () => {
|
|
219
231
|
const { pickModel, MODELS } = _internal;
|
|
220
232
|
const big = 80000;
|