Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11), free to diverge. Tree copied sans upstream .git; upstream remote added for future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19. Preserves docs/ARCHITECTURE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
571 lines
19 KiB
TypeScript
571 lines
19 KiB
TypeScript
/**
|
||
* Real Proof: Thinking Block Signature Integrity
|
||
*
|
||
* This script validates that the fixed anthropic-transport-stream correctly
|
||
* concatenates signature_delta chunks.
|
||
*
|
||
* Section 1 — Patched Code Path Proof:
|
||
* Exercises createAnthropicMessagesTransportStreamFn directly with a mocked
|
||
* fetch returning crafted SSE events with multiple signature_delta chunks.
|
||
* Verifies the transport stream concatenates (not overwrites) them.
|
||
* Does NOT require a real API key.
|
||
*
|
||
* Section 2 — Live API Replay Proof:
|
||
* Sends a real request to the Anthropic Messages API, collects raw SSE events,
|
||
* and verifies:
|
||
* 1. Multiple signature_delta events are received (confirming chunked delivery)
|
||
* 2. The concatenated signature matches the final message's signature
|
||
* 3. The signature is valid for replay (not truncated)
|
||
*
|
||
* Usage: ANTHROPIC_API_KEY=sk-ant-xxx npx tsx test/proof/thinking-signature-real-proof.ts
|
||
* or: OPENAI_API_KEY=sk-xxx ANTHROPIC_BASE_URL=http://localhost:xxx/v1 npx tsx ...
|
||
*/
|
||
|
||
const BASE_URL = process.env.ANTHROPIC_BASE_URL || "https://api.anthropic.com";
|
||
const ANTHROPIC_API_KEY =
|
||
process.env.ANTHROPIC_API_KEY ||
|
||
(process.env.ANTHROPIC_BASE_URL ? process.env.OPENAI_API_KEY : undefined);
|
||
|
||
// ============================================================================
|
||
// Section 1: Patched Code Path Proof
|
||
// Exercises createAnthropicMessagesTransportStreamFn directly.
|
||
// No real API key required — uses mocked fetch with crafted SSE response.
|
||
// ============================================================================
|
||
|
||
async function runPatchedCodePathProof(): Promise<{ passed: number; failed: number }> {
|
||
console.log("=== Patched Code Path Proof: createAnthropicMessagesTransportStreamFn ===\n");
|
||
console.log(" This section exercises the ACTUAL patched transport stream function");
|
||
console.log(" with a mocked fetch serving crafted SSE events.\n");
|
||
|
||
let passed = 0;
|
||
let failed = 0;
|
||
|
||
function assert(condition: boolean, description: string): void {
|
||
if (condition) {
|
||
console.log(` ✅ PASS: ${description}`);
|
||
passed++;
|
||
} else {
|
||
console.log(` ❌ FAIL: ${description}`);
|
||
failed++;
|
||
}
|
||
}
|
||
|
||
// Craft SSE events with 5 signature_delta chunks to prove concatenation
|
||
const signatureChunks = [
|
||
"EqMBCkYKMHd2YWl",
|
||
"Mn9kSEpweFGblQt",
|
||
"R2Bf8mKL3xNpvZw",
|
||
"Hy7TcDfG2jAqWsE",
|
||
"rX4uVnB9oI6mKpL",
|
||
];
|
||
const expectedSignature = signatureChunks.join("");
|
||
const seededStartSignature = "stale_seed_should_not_prefix_";
|
||
const thinkingText = "Let me analyze this step by step. The key insight is...";
|
||
|
||
const sseEvents: Record<string, unknown>[] = [
|
||
{
|
||
type: "message_start",
|
||
message: { id: "msg_proof_patched_1", usage: { input_tokens: 10, output_tokens: 150 } },
|
||
},
|
||
{
|
||
type: "content_block_start",
|
||
index: 0,
|
||
content_block: { type: "thinking", thinking: "", signature: seededStartSignature },
|
||
},
|
||
{
|
||
type: "content_block_delta",
|
||
index: 0,
|
||
delta: { type: "thinking_delta", thinking: thinkingText },
|
||
},
|
||
// 5 separate signature_delta events — the fix concatenates them
|
||
...signatureChunks.map((chunk) => ({
|
||
type: "content_block_delta",
|
||
index: 0,
|
||
delta: { type: "signature_delta", signature: chunk },
|
||
})),
|
||
{ type: "content_block_stop", index: 0 },
|
||
{
|
||
type: "content_block_start",
|
||
index: 1,
|
||
content_block: { type: "text", text: "" },
|
||
},
|
||
{
|
||
type: "content_block_delta",
|
||
index: 1,
|
||
delta: { type: "text_delta", text: "The answer is 42." },
|
||
},
|
||
{ type: "content_block_stop", index: 1 },
|
||
{ type: "message_delta", delta: { stop_reason: "end_turn" }, usage: { output_tokens: 150 } },
|
||
];
|
||
|
||
const sseBody = sseEvents.map((e) => `data: ${JSON.stringify(e)}\n\n`).join("");
|
||
|
||
// Mock globalThis.fetch — the transport stream's guarded fetch recognizes a mock
|
||
// (via .mock property) and skips SSRF DNS pinning, calling it directly.
|
||
const originalFetch = globalThis.fetch;
|
||
const mockFetch = async (
|
||
_url: string | URL | Request,
|
||
_init?: RequestInit,
|
||
): Promise<Response> => {
|
||
return new Response(sseBody, {
|
||
status: 200,
|
||
headers: { "content-type": "text/event-stream" },
|
||
});
|
||
};
|
||
(mockFetch as unknown as { mock: object }).mock = {};
|
||
globalThis.fetch = mockFetch as typeof fetch;
|
||
|
||
try {
|
||
const { createAnthropicMessagesTransportStreamFn } =
|
||
await import("../../src/agents/anthropic-transport-stream.js");
|
||
|
||
const streamFn = createAnthropicMessagesTransportStreamFn();
|
||
|
||
// Minimal model satisfying AnthropicTransportModel shape
|
||
const model = {
|
||
id: "claude-sonnet-4-6",
|
||
name: "Claude Sonnet 4.6",
|
||
api: "anthropic-messages" as const,
|
||
provider: "anthropic",
|
||
baseUrl: "",
|
||
reasoning: false,
|
||
input: ["text"] as ("text" | "image")[],
|
||
cost: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
|
||
contextWindow: 200000,
|
||
maxTokens: 8192,
|
||
};
|
||
|
||
const context = {
|
||
messages: [{ role: "user" as const, content: "Explain Gödel's theorems" }],
|
||
};
|
||
|
||
const options = {
|
||
apiKey: "sk-ant-proof-test-key-not-real",
|
||
maxTokens: 8192,
|
||
};
|
||
|
||
console.log(" Calling createAnthropicMessagesTransportStreamFn()...");
|
||
console.log(
|
||
` SSE payload: seeded start signature + ${signatureChunks.length} signature_delta events\n`,
|
||
);
|
||
|
||
const eventStream = streamFn(model as never, context as never, options as never);
|
||
const result = (await (eventStream as { result(): Promise<unknown> }).result()) as {
|
||
content: Array<{
|
||
type: string;
|
||
thinking?: string;
|
||
thinkingSignature?: string;
|
||
text?: string;
|
||
}>;
|
||
};
|
||
|
||
// Find the thinking block in the output
|
||
const thinkingBlock = result.content.find((b) => b.type === "thinking");
|
||
const textBlock = result.content.find((b) => b.type === "text");
|
||
|
||
assert(thinkingBlock !== undefined, "Transport stream produced a thinking block");
|
||
assert(textBlock !== undefined, "Transport stream produced a text block");
|
||
|
||
if (thinkingBlock) {
|
||
assert(
|
||
thinkingBlock.thinking === thinkingText,
|
||
`Thinking text captured correctly (${thinkingBlock.thinking!.length} chars)`,
|
||
);
|
||
|
||
assert(
|
||
thinkingBlock.thinkingSignature === expectedSignature,
|
||
`Signature equals concatenation of all ${signatureChunks.length} delta chunks (${expectedSignature.length} chars)`,
|
||
);
|
||
|
||
assert(
|
||
!thinkingBlock.thinkingSignature!.startsWith(seededStartSignature),
|
||
"Signature delta accumulation replaces the seeded start signature instead of prefixing it",
|
||
);
|
||
|
||
// The OLD bug would only keep the LAST chunk
|
||
const lastChunkOnly = signatureChunks[signatureChunks.length - 1];
|
||
assert(
|
||
thinkingBlock.thinkingSignature !== lastChunkOnly,
|
||
`Signature is NOT just the last chunk (old overwrite bug would produce "${lastChunkOnly}")`,
|
||
);
|
||
|
||
// Verify full length proves all chunks concatenated
|
||
const expectedLength = signatureChunks.reduce((sum, c) => sum + c.length, 0);
|
||
assert(
|
||
thinkingBlock.thinkingSignature!.length === expectedLength,
|
||
`Signature length ${thinkingBlock.thinkingSignature!.length} === sum of chunk lengths ${expectedLength}`,
|
||
);
|
||
|
||
// Verify each chunk appears in order within the signature
|
||
let offset = 0;
|
||
let allChunksInOrder = true;
|
||
for (const chunk of signatureChunks) {
|
||
const idx = thinkingBlock.thinkingSignature!.indexOf(chunk, offset);
|
||
if (idx !== offset) {
|
||
allChunksInOrder = false;
|
||
break;
|
||
}
|
||
offset += chunk.length;
|
||
}
|
||
assert(
|
||
allChunksInOrder,
|
||
"All signature chunks appear in order (verifies append, not prepend/shuffle)",
|
||
);
|
||
}
|
||
|
||
if (textBlock) {
|
||
assert(textBlock.text === "The answer is 42.", "Text block content preserved correctly");
|
||
}
|
||
} finally {
|
||
globalThis.fetch = originalFetch;
|
||
}
|
||
|
||
console.log(`\n--- Patched Code Path: ${passed} passed, ${failed} failed ---\n`);
|
||
return { passed, failed };
|
||
}
|
||
|
||
// ============================================================================
|
||
// Section 2: Live API Replay Proof
|
||
// Validates signature integrity against a real Anthropic API endpoint.
|
||
// Requires ANTHROPIC_API_KEY or OPENAI_API_KEY environment variable.
|
||
// ============================================================================
|
||
|
||
async function runProof(): Promise<{ passed: number; failed: number }> {
|
||
console.log("=== Live API Replay Proof: Thinking Block Signature Integrity ===\n");
|
||
|
||
if (!ANTHROPIC_API_KEY) {
|
||
console.log(" ⚠️ SKIPPED: Set ANTHROPIC_API_KEY or OPENAI_API_KEY to run live API proof\n");
|
||
return { passed: 0, failed: 0 };
|
||
}
|
||
|
||
const MODEL = process.env.ANTHROPIC_MODEL || "claude-sonnet-4-6";
|
||
const INITIAL_PROMPT =
|
||
"Analyze the philosophical implications of Gödel's incompleteness theorems on the foundations of mathematics. Consider: 1) How do they relate to Hilbert's program? 2) What are the epistemological consequences? 3) How do they connect to Turing's halting problem? Think deeply and thoroughly.";
|
||
console.log(`API Base: ${BASE_URL}`);
|
||
console.log(`Model: ${MODEL}\n`);
|
||
|
||
// Step 1: Send a request with thinking enabled
|
||
console.log("Step 1: Sending request with extended thinking enabled...");
|
||
|
||
const response = await fetch(`${BASE_URL}/v1/messages`, {
|
||
method: "POST",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
"x-api-key": ANTHROPIC_API_KEY,
|
||
"anthropic-version": "2023-06-01",
|
||
},
|
||
body: JSON.stringify({
|
||
model: MODEL,
|
||
max_tokens: 16000,
|
||
thinking: {
|
||
type: "enabled",
|
||
budget_tokens: 10000,
|
||
},
|
||
stream: true,
|
||
messages: [
|
||
{
|
||
role: "user",
|
||
content: INITIAL_PROMPT,
|
||
},
|
||
],
|
||
}),
|
||
});
|
||
|
||
if (!response.ok) {
|
||
const errorText = await response.text();
|
||
console.error(` API Error ${response.status}: ${errorText.slice(0, 200)}`);
|
||
console.log(" ❌ FAIL: Initial API request failed — cannot proceed with live proof");
|
||
console.log("\n--- Live API Replay: 0 passed, 1 failed ---");
|
||
return { passed: 0, failed: 1 };
|
||
}
|
||
|
||
// Step 2: Parse SSE stream, collect signature_delta events
|
||
console.log("Step 2: Parsing SSE stream...\n");
|
||
|
||
const reader = response.body!.getReader();
|
||
const decoder = new TextDecoder();
|
||
let buffer = "";
|
||
|
||
const signatureDeltas: string[] = [];
|
||
let thinkingText = "";
|
||
const allEvents: string[] = [];
|
||
|
||
// Simulate transport stream behavior (BEFORE fix = overwrite, AFTER fix = append)
|
||
let simulatedOverwrite = "";
|
||
let simulatedAppend = "";
|
||
|
||
while (true) {
|
||
const { done, value } = await reader.read();
|
||
if (done) {
|
||
break;
|
||
}
|
||
|
||
buffer += decoder.decode(value, { stream: true });
|
||
const lines = buffer.split("\n");
|
||
buffer = lines.pop() || "";
|
||
|
||
for (const line of lines) {
|
||
if (!line.startsWith("data: ")) {
|
||
continue;
|
||
}
|
||
const data = line.slice(6).trim();
|
||
if (data === "[DONE]") {
|
||
continue;
|
||
}
|
||
|
||
try {
|
||
const event = JSON.parse(data);
|
||
allEvents.push(event.type);
|
||
|
||
if (event.type === "content_block_delta" && event.delta?.type === "signature_delta") {
|
||
signatureDeltas.push(event.delta.signature);
|
||
// Simulate OLD behavior (overwrite)
|
||
simulatedOverwrite = event.delta.signature;
|
||
// Simulate NEW behavior (append)
|
||
simulatedAppend += event.delta.signature;
|
||
}
|
||
|
||
if (event.type === "content_block_delta" && event.delta?.type === "thinking_delta") {
|
||
thinkingText += event.delta.thinking;
|
||
}
|
||
|
||
// Capture the final message to get the complete thinking block
|
||
if (event.type === "message_stop") {
|
||
// We already have everything from deltas
|
||
}
|
||
} catch {
|
||
// Skip non-JSON lines
|
||
}
|
||
}
|
||
}
|
||
|
||
// Step 3: Validate results
|
||
console.log("Step 3: Validation Results\n");
|
||
console.log("--- Event Summary ---");
|
||
const eventCounts: Record<string, number> = {};
|
||
for (const e of allEvents) {
|
||
eventCounts[e] = (eventCounts[e] || 0) + 1;
|
||
}
|
||
for (const [type, count] of Object.entries(eventCounts)) {
|
||
console.log(` ${type}: ${count}`);
|
||
}
|
||
|
||
console.log(`\n--- Signature Delta Analysis ---`);
|
||
console.log(` Total signature_delta events: ${signatureDeltas.length}`);
|
||
for (let i = 0; i < signatureDeltas.length; i++) {
|
||
console.log(
|
||
` Chunk ${i + 1}: ${signatureDeltas[i].length} chars "${signatureDeltas[i].slice(0, 30)}..."`,
|
||
);
|
||
}
|
||
|
||
console.log(`\n--- Signature Comparison ---`);
|
||
console.log(` Full signature (concatenated): ${simulatedAppend.length} chars`);
|
||
console.log(` Overwrite-only (OLD BUG): ${simulatedOverwrite.length} chars`);
|
||
console.log(` Thinking text length: ${thinkingText.length} chars`);
|
||
|
||
// Step 4: Assertions
|
||
console.log(`\n--- Assertions ---`);
|
||
|
||
let passed = 0;
|
||
let failed = 0;
|
||
|
||
function assert(condition: boolean, description: string): void {
|
||
if (condition) {
|
||
console.log(` ✅ PASS: ${description}`);
|
||
passed++;
|
||
} else {
|
||
console.log(` ❌ FAIL: ${description}`);
|
||
failed++;
|
||
}
|
||
}
|
||
|
||
assert(signatureDeltas.length > 0, "At least one signature_delta event received");
|
||
if (signatureDeltas.length >= 2) {
|
||
assert(
|
||
true,
|
||
`Multiple signature_delta chunks received (got ${signatureDeltas.length} — confirms chunked delivery)`,
|
||
);
|
||
} else {
|
||
console.log(
|
||
` ℹ️ INFO: Only ${signatureDeltas.length} signature_delta chunk received (proxy/Bedrock may coalesce chunks)`,
|
||
);
|
||
console.log(
|
||
` Multi-chunk concatenation verified by unit tests; real proof focuses on replay integrity.`,
|
||
);
|
||
}
|
||
assert(
|
||
simulatedAppend.length > 0,
|
||
`Signature captured successfully (${simulatedAppend.length} chars)`,
|
||
);
|
||
assert(thinkingText.length > 0, "Thinking text was captured from thinking_delta events");
|
||
|
||
if (signatureDeltas.length >= 2) {
|
||
assert(
|
||
simulatedOverwrite !== simulatedAppend,
|
||
"OLD behavior (overwrite) produces DIFFERENT result than NEW behavior (append) — confirms the bug",
|
||
);
|
||
const truncationRatio = simulatedOverwrite.length / simulatedAppend.length;
|
||
console.log(
|
||
`\n 📊 Truncation ratio: ${(truncationRatio * 100).toFixed(1)}% — old behavior kept only ${(truncationRatio * 100).toFixed(1)}% of the full signature`,
|
||
);
|
||
} else {
|
||
console.log(`\n ℹ️ Single-chunk delivery — simulating truncation for negative proof`);
|
||
}
|
||
|
||
// Step 5: Replay validation — verify the concatenated signature can be sent back
|
||
console.log(`\n--- Replay Validation ---`);
|
||
console.log(" Sending replay request with the captured signature...");
|
||
|
||
const replayResponse = await fetch(`${BASE_URL}/v1/messages`, {
|
||
method: "POST",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
"x-api-key": ANTHROPIC_API_KEY,
|
||
"anthropic-version": "2023-06-01",
|
||
},
|
||
body: JSON.stringify({
|
||
model: MODEL,
|
||
max_tokens: 2048,
|
||
thinking: {
|
||
type: "enabled",
|
||
budget_tokens: 1024,
|
||
},
|
||
messages: [
|
||
{
|
||
role: "user",
|
||
content: INITIAL_PROMPT,
|
||
},
|
||
{
|
||
role: "assistant",
|
||
content: [
|
||
{
|
||
type: "thinking",
|
||
thinking: thinkingText,
|
||
signature: simulatedAppend,
|
||
},
|
||
{
|
||
type: "text",
|
||
text: "2 + 2 = 4",
|
||
},
|
||
],
|
||
},
|
||
{
|
||
role: "user",
|
||
content: "And 3+3?",
|
||
},
|
||
],
|
||
}),
|
||
});
|
||
|
||
assert(
|
||
replayResponse.ok,
|
||
`Replay with CORRECT (concatenated) signature succeeds: HTTP ${replayResponse.status}`,
|
||
);
|
||
if (!replayResponse.ok) {
|
||
const errBody = await replayResponse.text();
|
||
console.log(` Replay error: ${errBody.slice(0, 200)}`);
|
||
} else {
|
||
// Consume the response
|
||
await replayResponse.text();
|
||
}
|
||
|
||
// Step 6: Negative proof — replay with truncated signature should fail
|
||
{
|
||
// Always run negative proof (artificially truncate if needed)
|
||
const truncatedSig =
|
||
signatureDeltas.length >= 2
|
||
? simulatedOverwrite
|
||
: simulatedAppend.slice(0, Math.floor(simulatedAppend.length / 3));
|
||
console.log(" Sending replay with TRUNCATED (old bug) signature...");
|
||
const badReplayResponse = await fetch(`${BASE_URL}/v1/messages`, {
|
||
method: "POST",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
"x-api-key": ANTHROPIC_API_KEY,
|
||
"anthropic-version": "2023-06-01",
|
||
},
|
||
body: JSON.stringify({
|
||
model: MODEL,
|
||
max_tokens: 2048,
|
||
thinking: {
|
||
type: "enabled",
|
||
budget_tokens: 1024,
|
||
},
|
||
messages: [
|
||
{
|
||
role: "user",
|
||
content: INITIAL_PROMPT,
|
||
},
|
||
{
|
||
role: "assistant",
|
||
content: [
|
||
{
|
||
type: "thinking",
|
||
thinking: thinkingText,
|
||
signature: truncatedSig, // TRUNCATED!
|
||
},
|
||
{
|
||
type: "text",
|
||
text: "2 + 2 = 4",
|
||
},
|
||
],
|
||
},
|
||
{
|
||
role: "user",
|
||
content: "And 3+3?",
|
||
},
|
||
],
|
||
}),
|
||
});
|
||
|
||
assert(
|
||
!badReplayResponse.ok,
|
||
`Replay with TRUNCATED (old bug) signature fails: HTTP ${badReplayResponse.status} — confirms the bug causes API rejection`,
|
||
);
|
||
if (!badReplayResponse.ok) {
|
||
const errBody = await badReplayResponse.text();
|
||
const parsed = JSON.parse(errBody);
|
||
console.log(` Expected error: ${parsed.error?.message?.slice(0, 150)}`);
|
||
} else {
|
||
await badReplayResponse.text();
|
||
}
|
||
}
|
||
|
||
// Final summary
|
||
console.log(`\n--- Live API Replay: ${passed} passed, ${failed} failed ---`);
|
||
|
||
return { passed, failed };
|
||
}
|
||
|
||
async function main(): Promise<void> {
|
||
// Section 1: Patched code path proof (no API key needed)
|
||
const patchedResult = await runPatchedCodePathProof();
|
||
|
||
// Section 2: Live API replay proof (requires API key)
|
||
const liveResult = await runProof();
|
||
|
||
// Overall summary
|
||
const totalPassed = patchedResult.passed + liveResult.passed;
|
||
const totalFailed = patchedResult.failed + liveResult.failed;
|
||
|
||
console.log(`\n=== OVERALL PROOF SUMMARY ===`);
|
||
console.log(
|
||
` Patched code path: ${patchedResult.passed} passed, ${patchedResult.failed} failed`,
|
||
);
|
||
console.log(` Live API replay: ${liveResult.passed} passed, ${liveResult.failed} failed`);
|
||
console.log(` Total: ${totalPassed} passed, ${totalFailed} failed`);
|
||
|
||
if (totalFailed > 0) {
|
||
console.log("\n❌ PROOF FAILED — some assertions did not pass");
|
||
process.exit(1);
|
||
} else {
|
||
console.log("\n✅ PROOF PASSED — signature concatenation fix verified");
|
||
process.exit(0);
|
||
}
|
||
}
|
||
|
||
main().catch((err: unknown) => {
|
||
console.error("Proof script error:", err);
|
||
process.exit(1);
|
||
});
|