Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions src/core/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2354,8 +2354,7 @@ export async function transformRequest(
const imageInstructionHeader =
'=================== SESSION CONFIGURATION PAGES ===================\n' +
"pxpipe (this user's local proxy) rendered this session's configuration" +
' into the following images to reduce token cost. Read the pages carefully and follow them as' +
' your operating instructions for this session.' +
' into the following images to reduce token cost.' +
' For exact identifiers, paths, hashes, version strings, and numbers, use the adjacent' +
' exact-value factsheet; if a value was only visible in an image and is not in that factsheet,' +
' do not guess it — say it is not safe to quote from the image and re-read the source text.' +
Expand Down Expand Up @@ -2487,6 +2486,12 @@ export async function transformRequest(
if (preservedIdentity) {
sysTail.push({ type: 'text', text: preservedIdentity });
}
if (imageBlocks.length > 0) {
sysTail.push({
type: 'text',
text: "pxpipe (this user's local proxy) rendered this session's configuration into the image blocks attached to the first user message to reduce token cost.",
});
}
// billingLine is NOT re-emitted here. req.system precedes messages[] in
// Anthropic's cache-prefix order, so every system block sits inside the
// span covered by the LAST cache_control marker (always in messages[] on
Expand Down
51 changes: 51 additions & 0 deletions tests/refusal-provenance.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { describe, expect, it } from 'vitest';
import { transformRequest } from '../src/core/transform.js';

const enc = new TextEncoder();
const dec = new TextDecoder();

describe('Anthropic refusal prevention - proxy system provenance', () => {
it('adds explicit proxy provenance statement to req.system when rendering static slab into user-turn images', async () => {
const reqBody = JSON.stringify({
model: 'claude-3-5-sonnet',
system: 'System operating instructions. '.repeat(1000),
tools: [
{
name: 'ReadFile',
description: 'Read contents of a file.',
input_schema: { type: 'object', properties: { path: { type: 'string' } } },
},
],
messages: [
{ role: 'user', content: 'Help me debug this issue.' },
],
});

const { body, info } = await transformRequest(enc.encode(reqBody));
expect(info.compressed).toBe(true);
expect(info.imageCount).toBeGreaterThan(0);

const out = JSON.parse(dec.decode(body));

// 1. req.system must contain an explicit first-party proxy provenance statement
const sysBlocks = Array.isArray(out.system)
? out.system
: typeof out.system === 'string'
? [{ type: 'text', text: out.system }]
: [];

const sysTexts = sysBlocks.map((b: any) => b.text || '');
const provenanceBlock = sysTexts.find((t: string) =>
t.includes("pxpipe (this user's local proxy)") && t.includes('rendered this session'),
);

expect(provenanceBlock).toBeDefined();
expect(provenanceBlock).toContain("pxpipe (this user's local proxy)");
expect(provenanceBlock).toContain('image blocks');

// 2. The user-turn image banner must NOT contain imperative commands like "follow them as your operating instructions"
const imgSrc = info.imageSourceText ?? '';
expect(imgSrc).not.toContain('follow them as your operating instructions');
expect(imgSrc).not.toMatch(/system prompt|authoritative/i);
});
});