troxy-cli 1.30.12 → 1.30.13
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.30.
|
|
3
|
+
"version": "1.30.13",
|
|
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": {
|
|
@@ -107,6 +107,23 @@ function rebuildWithText(toolResponse, text) {
|
|
|
107
107
|
return { ...toolResponse, content: text };
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
// Only outputs Claude rarely fetches back get shortened. Measured 2026-10-23 on 1,435 shortened
|
|
111
|
+
// outputs: Grep 0% recalled; wc, ls, head, tail, test runs and logs 0-15%; Read of .js/.html
|
|
112
|
+
// 17-20%. Targeted reads (sed, cat, Bash grep, Read of .py/.md/.txt) came back 30-86%, so
|
|
113
|
+
// shortening them only adds a recall step.
|
|
114
|
+
const SHORTEN_COMMANDS = new Set(['wc', 'ls', 'head', 'tail', 'ps', 'gh', 'aws', 'pytest']);
|
|
115
|
+
const SHORTEN_READ_EXT = /\.(js|html)$/i;
|
|
116
|
+
|
|
117
|
+
export function isShortenable(toolName, toolInput) {
|
|
118
|
+
if (toolName === undefined) return true; // ponytail: payloads without tool_name (old tests); Claude Code always sends it
|
|
119
|
+
if (toolName === 'Grep') return true;
|
|
120
|
+
if (toolName === 'Read') return SHORTEN_READ_EXT.test(String(toolInput?.file_path || ''));
|
|
121
|
+
if (toolName !== 'Bash') return false;
|
|
122
|
+
const cmd = String(toolInput?.command || '').trim().replace(/^(cd\s+\S+\s*(&&|;)\s*)+/, '');
|
|
123
|
+
if (/^python3?\s+-m\s+pytest\b/.test(cmd)) return true;
|
|
124
|
+
return SHORTEN_COMMANDS.has(path.basename(cmd.split(/\s+/)[0] || ''));
|
|
125
|
+
}
|
|
126
|
+
|
|
110
127
|
export async function buildHookResponse(toolUseBlock, {
|
|
111
128
|
sandboxFraction = 1,
|
|
112
129
|
holdoutFraction = 0.2,
|
|
@@ -124,7 +141,7 @@ export async function buildHookResponse(toolUseBlock, {
|
|
|
124
141
|
return undefined;
|
|
125
142
|
}
|
|
126
143
|
const output = extractText(toolResponse);
|
|
127
|
-
if (output === null || !shouldSandbox(output)) {
|
|
144
|
+
if (output === null || !shouldSandbox(output) || !isShortenable(toolUseBlock?.tool_name, toolUseBlock?.tool_input)) {
|
|
128
145
|
writeState(storeDir, 'skipped');
|
|
129
146
|
return undefined;
|
|
130
147
|
}
|
|
@@ -271,3 +271,25 @@ test('posttooluse-hook is a no-op unless TROXY_COMPRESS=1', () => {
|
|
|
271
271
|
fs.rmSync(tmpHome, { recursive: true, force: true });
|
|
272
272
|
}
|
|
273
273
|
});
|
|
274
|
+
|
|
275
|
+
test('isShortenable: only output kinds Claude rarely fetches back', async () => {
|
|
276
|
+
const { isShortenable } = await import('../sandbox/posttooluse-hook.js');
|
|
277
|
+
assert.equal(isShortenable('Grep', {}), true);
|
|
278
|
+
assert.equal(isShortenable('Read', { file_path: '/a/b/app.js' }), true);
|
|
279
|
+
assert.equal(isShortenable('Read', { file_path: '/a/b/notes.md' }), false);
|
|
280
|
+
assert.equal(isShortenable('Read', { file_path: '/a/b/x.py' }), false);
|
|
281
|
+
assert.equal(isShortenable('Bash', { command: 'cd /repo && ls -la src' }), true);
|
|
282
|
+
assert.equal(isShortenable('Bash', { command: 'python3 -m pytest -q' }), true);
|
|
283
|
+
assert.equal(isShortenable('Bash', { command: 'sed -n 1,200p file.js' }), false);
|
|
284
|
+
assert.equal(isShortenable('Bash', { command: 'cat big.log' }), false);
|
|
285
|
+
assert.equal(isShortenable('mcp__claude_ai_Gmail__get_thread', {}), false);
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
test('a large sed output passes through unshortened', async () => {
|
|
289
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'troxy-sandbox-'));
|
|
290
|
+
const response = await buildHookResponse(
|
|
291
|
+
{ tool_name: 'Bash', tool_input: { command: 'sed -n 1,400p a.js' }, tool_response: bashResult('x'.repeat(5000)) },
|
|
292
|
+
{ sandboxFraction: 1, holdoutFraction: 0, randomFn: () => 0.5, storeDir: dir });
|
|
293
|
+
assert.equal(response, undefined);
|
|
294
|
+
assert.equal(readState(dir).state, 'skipped');
|
|
295
|
+
});
|