troxy-cli 1.16.0 → 1.17.0
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 +1 -1
- package/src/mcp-server.js +18 -6
- package/src/tests/model-checkpoint.test.js +34 -0
package/package.json
CHANGED
package/src/mcp-server.js
CHANGED
|
@@ -51,7 +51,18 @@ export async function runMcp() {
|
|
|
51
51
|
// its own is the agent's own prediction, and the dashboard labels every
|
|
52
52
|
// agent that never reports back as unverified, so the instruction has
|
|
53
53
|
// to make the second call as non-optional as the first.
|
|
54
|
-
|
|
54
|
+
//
|
|
55
|
+
// The previous wording gated this on whether the task felt big
|
|
56
|
+
// enough, which was a judgment call, not a rule. Driven live: the
|
|
57
|
+
// same agent, same session, called evaluate_model to review two
|
|
58
|
+
// source files, then minutes later pulled real data from an actual
|
|
59
|
+
// connected business (orders, customer messages, an artifact) and
|
|
60
|
+
// called nothing. Both were real work by any reasonable reading. The
|
|
61
|
+
// old wording meant whatever the model felt like in the moment, and
|
|
62
|
+
// Token Optimization is supposed to be a precise record, not a mood.
|
|
63
|
+
// The trigger below is mechanical instead: did this turn use a tool,
|
|
64
|
+
// yes or no. That is a fact you can check, not a judgment you make.
|
|
65
|
+
'You MUST also call the evaluate_model tool at the start of ANY turn where you will use one or more tools of any kind — reading or writing a file, running a command, browsing, calling an external API, anything beyond a plain text reply — and whenever you switch model or effort level partway through the turn. This is not a judgment call about how the turn feels: if you are about to use a tool, call evaluate_model first, every time, regardless of how small the turn seems. A turn that is pure conversation with no tool use at all is the only kind that does not need it. Give the exact model id and how many tokens you expect to use. Do not use any tool without first receiving an ALLOW or NOTIFY decision; if BLOCK, pick a cheaper or smaller model and call evaluate_model again with the new id. Then, at the end of that same turn, you MUST call report_model_usage with the audit_id from that decision and the real token total from your model API. Both halves are required: without the second call Troxy has only your prediction, and the account owner is shown that this agent\'s costs are unverified. Report the true number even when it is far above your estimate.',
|
|
55
66
|
},
|
|
56
67
|
{ capabilities: { tools: {} } },
|
|
57
68
|
);
|
|
@@ -268,8 +279,9 @@ export async function runMcp() {
|
|
|
268
279
|
description:
|
|
269
280
|
'Evaluate whether running a task on a given AI model should be allowed, blocked, or escalated ' +
|
|
270
281
|
'based on your Troxy policies, and record what it is expected to cost. ' +
|
|
271
|
-
'Call this
|
|
272
|
-
'
|
|
282
|
+
'Call this at the start of any turn where you will use a tool of any kind, and whenever you switch model or effort level mid-turn. ' +
|
|
283
|
+
'This is mechanical, not a judgment call: tool use in this turn means call it, no tool use means you can skip it. ' +
|
|
284
|
+
'Returns an audit_id. Keep it: you must pass it to report_model_usage at the end of the turn.',
|
|
273
285
|
inputSchema: {
|
|
274
286
|
type: 'object',
|
|
275
287
|
required: ['model'],
|
|
@@ -309,8 +321,8 @@ export async function runMcp() {
|
|
|
309
321
|
{
|
|
310
322
|
name: 'report_model_usage',
|
|
311
323
|
description:
|
|
312
|
-
'Report how many tokens
|
|
313
|
-
'Call this after every ALLOW or NOTIFY from evaluate_model, using the audit_id it returned. ' +
|
|
324
|
+
'Report how many tokens the turn ACTUALLY used, once it has finished. ' +
|
|
325
|
+
'Call this after every ALLOW or NOTIFY from evaluate_model, using the audit_id it returned — no judgment call, every approved evaluate_model gets a matching report. ' +
|
|
314
326
|
'Without it Troxy only has your prediction, and the account owner is told, per agent, that its figures are unverified. ' +
|
|
315
327
|
'Report the real number even when it is far above what you estimated: the gap is the point.',
|
|
316
328
|
inputSchema: {
|
|
@@ -489,7 +501,7 @@ export async function runMcp() {
|
|
|
489
501
|
// Every non-blocked branch repeats the audit_id and the instruction to
|
|
490
502
|
// report back. Half the value of this checkpoint is the second call, and
|
|
491
503
|
// an agent that is only told "approved" has no reason to make it.
|
|
492
|
-
const followUp = ` When
|
|
504
|
+
const followUp = ` When this turn finishes, call report_model_usage(audit_id="${audit_id}", actual_tokens=<real total>).`;
|
|
493
505
|
let modelText;
|
|
494
506
|
switch (decision) {
|
|
495
507
|
case 'ALLOW':
|
|
@@ -132,3 +132,37 @@ test('the version is read from package.json, not hardcoded', () => {
|
|
|
132
132
|
'CLI_VERSION is not derived from package.json',
|
|
133
133
|
);
|
|
134
134
|
});
|
|
135
|
+
|
|
136
|
+
test('the checkpoint trigger is mechanical, not a judgment call', () => {
|
|
137
|
+
// "When you begin a substantial task" was tried first and driven live: the
|
|
138
|
+
// same agent, same session, called evaluate_model to review two source
|
|
139
|
+
// files, then minutes later pulled real data from an actual connected
|
|
140
|
+
// business (orders, customer messages, an artifact) and called nothing.
|
|
141
|
+
// Both were real work. "Substantial" meant whatever the model felt like in
|
|
142
|
+
// the moment, which is not what a precise cost record can be built on.
|
|
143
|
+
// Sliced from the opening quote of the instructions STRING, not the
|
|
144
|
+
// instructions: field or file start, so the explanatory code comment
|
|
145
|
+
// above it (which deliberately quotes the retired word for context) does
|
|
146
|
+
// not trip this check.
|
|
147
|
+
const instructions = src.slice(src.indexOf("'You are connected to Troxy"), src.indexOf('capabilities'));
|
|
148
|
+
assert.ok(
|
|
149
|
+
!/substantial/i.test(instructions),
|
|
150
|
+
'the instructions still gate the checkpoint on "substantial", a judgment '
|
|
151
|
+
+ 'call proven inconsistent on the same agent in the same session',
|
|
152
|
+
);
|
|
153
|
+
assert.ok(
|
|
154
|
+
/tool/i.test(instructions) && /every time/i.test(instructions),
|
|
155
|
+
'the instructions do not state a mechanical, checkable trigger (tool use) '
|
|
156
|
+
+ 'to replace the judgment call that was removed',
|
|
157
|
+
);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test('evaluate_model\'s own tool description matches the mechanical trigger', () => {
|
|
161
|
+
// The top-level instructions are what an agent reads once; the tool
|
|
162
|
+
// description is what it sees every time it considers calling this
|
|
163
|
+
// specific tool. Both have to agree, or whichever one it reads last wins.
|
|
164
|
+
const schema = src.slice(src.indexOf("name: 'evaluate_model'"));
|
|
165
|
+
const desc = schema.slice(0, schema.indexOf('inputSchema'));
|
|
166
|
+
assert.ok(!/substantial/i.test(desc), 'the tool description still says "substantial"');
|
|
167
|
+
assert.ok(/tool/i.test(desc), 'the tool description does not mention the tool-use trigger');
|
|
168
|
+
});
|