Capture · Capture procedure

SDK request capture

How was the outgoing request intercepted?

What this proves

Agent SDK query() sends one Messages request to a loopback endpoint, which records it before returning a synthetic response. No inference occurs.

Input
Isolated Agent SDK query()
Output
Outgoing Messages request
File
scripts/capture-sdk-request.mjs

Inspect the capture procedure

View raw
Open the complete source file
JavaScript
#!/usr/bin/env node
import { execFile } from "node:child_process";
import { createServer } from "node:http";
import { mkdir, readFile, rm } from "node:fs/promises";
import { resolve } from "node:path";
import { promisify } from "node:util";
import { pathToFileURL } from "node:url";
import {
  provenanceDir,
  portablePath,
  redactHeaders,
  sha256,
  sha256File,
  stableJson,
  workspaceDir,
  writeJson,
} from "./lib.mjs";

const execFileAsync = promisify(execFile);
const sdkDir = resolve(workspaceDir, "work/npm-sdk");
const sdkEntry = resolve(sdkDir, "sdk.mjs");
const sdkPackage = resolve(sdkDir, "package.json");
const sdkTarball = resolve(workspaceDir, "work/anthropic-ai-claude-agent-sdk-0.3.168.tgz");
const captureRoot = resolve(workspaceDir, "work/token-count-pipeline-capture");
const prompt = "say hi";

async function command(command, args) {
  try {
    const { stdout } = await execFileAsync(command, args, { encoding: "utf8" });
    return stdout.trim();
  } catch (error) {
    return `unavailable: ${error.message}`;
  }
}

function syntheticSse(model) {
  const message = {
    id: "msg_capture",
    type: "message",
    role: "assistant",
    model,
    content: [],
    stop_reason: null,
    stop_sequence: null,
    usage: {
      input_tokens: 1,
      cache_creation_input_tokens: 0,
      cache_read_input_tokens: 0,
      output_tokens: 0,
    },
  };
  return [
    `event: message_start\ndata: ${JSON.stringify({ type: "message_start", message })}\n`,
    `event: content_block_start\ndata: ${JSON.stringify({ type: "content_block_start", index: 0, content_block: { type: "text", text: "" } })}\n`,
    `event: content_block_delta\ndata: ${JSON.stringify({ type: "content_block_delta", index: 0, delta: { type: "text_delta", text: "hi" } })}\n`,
    `event: content_block_stop\ndata: ${JSON.stringify({ type: "content_block_stop", index: 0 })}\n`,
    `event: message_delta\ndata: ${JSON.stringify({ type: "message_delta", delta: { stop_reason: "end_turn", stop_sequence: null }, usage: { output_tokens: 1 } })}\n`,
    `event: message_stop\ndata: ${JSON.stringify({ type: "message_stop" })}\n`,
  ].join("\n");
}

function normalizedLoadedContext(body) {
  const context = structuredClone({
    model: body.model,
    system: body.system,
    messages: body.messages,
    tools: body.tools,
    thinking: body.thinking,
    output_config: body.output_config,
  });
  if (context.system?.[0]?.text) {
    context.system[0].text = context.system[0].text.replace(/cch=[^;]+;/, "cch=<dynamic>;");
  }
  return context;
}

async function main() {
  const claudePath = await command("which", ["claude"]);
  if (!claudePath.startsWith("/")) {
    throw new Error(`Could not resolve a Claude Code executable: ${claudePath}`);
  }
  await rm(captureRoot, { recursive: true, force: true });
  const home = resolve(captureRoot, "home");
  const cwd = resolve(captureRoot, "cwd");
  const config = resolve(captureRoot, "config");
  await Promise.all([
    mkdir(home, { recursive: true }),
    mkdir(cwd, { recursive: true }),
    mkdir(config, { recursive: true }),
  ]);

  let resolveCapture;
  let rejectCapture;
  const captured = new Promise((resolvePromise, rejectPromise) => {
    resolveCapture = resolvePromise;
    rejectCapture = rejectPromise;
  });

  const server = createServer((request, response) => {
    const chunks = [];
    request.on("data", (chunk) => chunks.push(chunk));
    request.on("error", rejectCapture);
    request.on("end", () => {
      const rawBody = Buffer.concat(chunks).toString("utf8");
      if (request.method !== "POST" || !request.url?.startsWith("/v1/messages") || !rawBody) {
        response.writeHead(204);
        response.end();
        return;
      }
      try {
        const body = JSON.parse(rawBody);
        resolveCapture({
          method: request.method,
          url: request.url,
          headers: redactHeaders(request.headers),
          body,
          rawBodySha256: sha256(rawBody),
        });
        response.writeHead(200, {
          "content-type": "text/event-stream",
          "cache-control": "no-cache",
          connection: "close",
        });
        response.end(syntheticSse(body.model));
      } catch (error) {
        rejectCapture(error);
        response.writeHead(400, { "content-type": "application/json" });
        response.end(
          JSON.stringify({
            type: "error",
            error: { type: "invalid_request_error", message: error.message },
          }),
        );
      }
    });
  });

  let capture;
  let query;
  let timeoutId;
  let consume = Promise.resolve();

  try {
    await new Promise((resolvePromise, rejectPromise) => {
      server.once("error", rejectPromise);
      server.listen(0, "127.0.0.1", resolvePromise);
    });

    const address = server.address();
    if (!address || typeof address === "string")
      throw new Error("Capture server has no TCP address");
    const baseUrl = `http://127.0.0.1:${address.port}`;
    const sdk = await import(`${pathToFileURL(sdkEntry).href}?capture=${Date.now()}`);
    const cleanEnv = {
      ...process.env,
      HOME: home,
      CLAUDE_CONFIG_DIR: config,
      ANTHROPIC_BASE_URL: baseUrl,
      ANTHROPIC_API_KEY: "capture-placeholder-not-a-real-key",
      CLAUDE_CODE_DISABLE_AUTO_MEMORY: "1",
    };
    for (const name of [
      "ANTHROPIC_AUTH_TOKEN",
      "ANTHROPIC_MODEL",
      "ANTHROPIC_SMALL_FAST_MODEL",
      "CLAUDE_CODE_OAUTH_TOKEN",
    ])
      delete cleanEnv[name];

    query = sdk.query({
      prompt,
      options: {
        cwd,
        settingSources: [],
        env: cleanEnv,
        pathToClaudeCodeExecutable: claudePath,
      },
    });
    consume = (async () => {
      try {
        for await (const _message of query) {
          // Draining the iterator lets the embedded CLI finish normally.
        }
      } catch {
        // The outgoing request is the artifact under test; capture remains valid.
      }
    })();

    const timeout = new Promise((_, rejectPromise) => {
      timeoutId = setTimeout(
        () => rejectPromise(new Error("Timed out waiting for the SDK request")),
        30_000,
      );
    });
    capture = await Promise.race([captured, timeout]);
  } finally {
    clearTimeout(timeoutId);
    try {
      query?.close();
    } catch {
      // Cleanup continues even if the SDK iterator is already closed.
    }
    await consume;
    if (server.listening) {
      await new Promise((resolvePromise, rejectPromise) => {
        server.close((error) => (error ? rejectPromise(error) : resolvePromise()));
      });
    }
  }

  if (!capture) throw new Error("The SDK request was not captured");

  const sdkMetadata = JSON.parse(await readFile(sdkPackage, "utf8"));
  const claudeVersion = await command("claude", ["--version"]);
  const claudeSha256 = claudePath.startsWith("/") ? await sha256File(claudePath) : null;
  const piebaldCommit = await command("git", [
    "-C",
    resolve(workspaceDir, "work/claude-code-system-prompts"),
    "rev-parse",
    "HEAD",
  ]);
  const redactedBody = structuredClone(capture.body);
  if (redactedBody.metadata?.user_id) redactedBody.metadata.user_id = "<redacted>";
  const normalizedContext = normalizedLoadedContext(redactedBody);
  const priorCapturePath = resolve(
    workspaceDir,
    "outputs/sdk-builtin-default-query-request.redacted.json",
  );
  let priorComparison = null;
  try {
    const priorCapture = JSON.parse(await readFile(priorCapturePath, "utf8"));
    const priorNormalized = normalizedLoadedContext(priorCapture);
    priorComparison = {
      path: portablePath(priorCapturePath),
      normalizedLoadedContextSha256: sha256(stableJson(priorNormalized)),
      exactMatchAfterDynamicFieldNormalization:
        stableJson(priorNormalized) === stableJson(normalizedContext),
    };
  } catch {
    // A prior capture is optional; the fresh capture remains self-contained.
  }

  await writeJson(resolve(provenanceDir, "sdk-query-request.redacted.json"), redactedBody);
  await writeJson(
    resolve(provenanceDir, "sdk-query-request-headers.redacted.json"),
    capture.headers,
  );
  await writeJson(resolve(provenanceDir, "capture-provenance.json"), {
    generatedAt: new Date().toISOString(),
    captureMethod: "Claude Agent SDK query() sent to a loopback fake Anthropic endpoint",
    isolation: {
      settingSources: [],
      freshHome: true,
      freshClaudeConfigDir: true,
      autoMemoryDisabled: true,
      prompt,
    },
    request: {
      method: capture.method,
      url: capture.url,
      redactedBodySha256: sha256(`${JSON.stringify(redactedBody, null, 2)}\n`),
      redactedCanonicalBodySha256: sha256(stableJson(redactedBody)),
      rawBodySha256: capture.rawBodySha256,
      normalizedLoadedContextSha256: sha256(stableJson(normalizedContext)),
      priorCaptureComparison: priorComparison,
    },
    sdk: {
      package: sdkMetadata.name,
      version: sdkMetadata.version,
      claudeCodeVersion: sdkMetadata.claudeCodeVersion,
      entryPath: portablePath(sdkEntry),
      entrySha256: await sha256File(sdkEntry),
      tarballPath: portablePath(sdkTarball),
      tarballSha256: await sha256File(sdkTarball),
    },
    runtime: {
      node: process.version,
      platform: process.platform,
      arch: process.arch,
      claudeExecutable: portablePath(claudePath),
      claudeVersion,
      claudeSha256,
    },
    comparisonCatalog: {
      repository: "https://github.com/Piebald-AI/claude-code-system-prompts",
      commit: piebaldCommit,
    },
  });

  process.stdout.write(
    `Captured ${capture.body.tools?.length ?? 0} tools for ${capture.body.model}.\n`,
  );
}

main().catch((error) => {
  console.error(error.stack || error.message);
  process.exitCode = 1;
});