troxy-cli 1.29.53 → 1.29.54

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.53",
3
+ "version": "1.29.54",
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": {
@@ -118,9 +118,9 @@ function _readModRoute(dir, sessionId) {
118
118
  const arm = n.arm === 'treated' || n.arm === 'holdout' ? n.arm : null;
119
119
  const isFresh = Date.now() - n.ts < _MOD_ROUTE_MAX_AGE_MS;
120
120
  const routed = isFresh && typeof n.from === 'string' && typeof n.to === 'string';
121
- return { arm, from: routed ? n.from : null, to: routed ? n.to : null, compact: isFresh && n.compact === true };
121
+ return { arm, from: routed ? n.from : null, to: routed ? n.to : null, compact: isFresh && n.compact === true, cold: isFresh && typeof n.cold === 'boolean' ? n.cold : null };
122
122
  } catch {
123
- return { arm: null, from: null, to: null, compact: false };
123
+ return { arm: null, from: null, to: null, compact: false, cold: null };
124
124
  }
125
125
  }
126
126
 
@@ -183,6 +183,8 @@ export function buildTroxyForwardHeaders(incomingHeaders, troxyKey, sandboxState
183
183
  if (modRoute && modRoute.arm) headers['x-troxy-mod-arm'] = modRoute.arm;
184
184
  // The mod's idle compaction is in flight: this is its summary request.
185
185
  if (modRoute && modRoute.compact) headers['x-troxy-mod-compact'] = '1';
186
+ // Tagged: whether this request hit the cold or warm path (for measurement).
187
+ if (modRoute && typeof modRoute.cold === 'boolean') headers['x-troxy-switch'] = modRoute.cold ? 'cold' : 'warm';
186
188
  headers.authorization = `Bearer ${troxyKey}`;
187
189
  return headers;
188
190
  }
@@ -453,9 +453,10 @@ async function* routeStep($, e, next) {
453
453
  const h = held.get(sid) || { decision: null, prompts: 0 };
454
454
  const newPrompt = Boolean(decision) && decision !== h.decision;
455
455
  if (newPrompt) h.decision = decision;
456
+ const isCold = !ls || now - ls.at >= WARM_MS || coldNext.has(sid);
456
457
  const model = (await $.store.get('paused')) ? want : stepModel({
457
458
  arm, requested: want, switched,
458
- cold: !ls || now - ls.at >= WARM_MS || coldNext.has(sid),
459
+ cold: isCold,
459
460
  score: decision ? decision.score : null, warm: ls && ls.model,
460
461
  newPrompt, heldPrompts: h.prompts, tokens: decision ? decision.tokens : Infinity,
461
462
  });
@@ -470,7 +471,8 @@ async function* routeStep($, e, next) {
470
471
  const key = `${sid}:${arm}:${want}>${model}`;
471
472
  if (file && lastNote !== key) {
472
473
  lastNote = key;
473
- await $.fs.write(file, JSON.stringify(model === want ? { arm, ts: now } : { arm, from: want, to: model, ts: now }));
474
+ const route = model === want ? { arm, cold: isCold, ts: now } : { arm, from: want, to: model, cold: isCold, ts: now };
475
+ await $.fs.write(file, JSON.stringify(route));
474
476
  }
475
477
  try { await rememberAsked($, sid, want, model); } catch {} // a missed write only loses this on resume
476
478
  if (model !== want && turn) turn.ratio = PRICE[tierOf(want)] / PRICE[tierOf(model)];