Files
adolf/test/proof/thinking-signature-real-proof.ts
alvis bedb527145
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
Vendor OpenClaw source as Adolf fork baseline
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
2026-07-05 09:36:54 +00:00

571 lines
19 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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);
});