Network Interceptor Architecture for the Pi Backend: How Craft Agents Unifies AI API Communication

The Pi backend utilizes a Unified Network Interceptor that proxies the global fetch function via a Node.js --require preload, enabling transparent request normalization, error recovery, and metadata injection across AI providers without modifying upstream SDK code.

The network interceptor architecture for the Pi backend serves as the central nervous system of the craft-ai-agents/craft-agents-oss repository, sitting between the Pi SDK (and other AI SDKs) and the HTTP transport layer. Its primary function is to normalize, augment, and monitor all outgoing AI-API requests—whether targeting Anthropic, OpenAI, or Bedrock—so that the broader Craft Agents ecosystem can treat heterogeneous provider interactions as a single, feature-rich stream.

Core Components of the Interceptor Stack

The architecture consists of four tightly integrated modules that handle everything from low-level network proxying to high-level request transformation.

unified-network-interceptor.ts

Located at packages/shared/src/unified-network-interceptor.ts, this file implements the central fetch proxy that intercepts every outbound HTTP request. It rewrites headers, upgrades cache control directives, strips beta flags like context-1m, and converts HTML error pages (common when corporate firewalls intercept requests) into synthetic JSON error objects with preserved HTTP status codes.

interceptor-common.ts

The packages/shared/src/interceptor-common.ts module provides shared utilities including runtime logging, feature-flag handling, and error-type definitions. This component maintains a diagnostic audit trail by writing raw request and response data to interceptor.log, enabling developers to inspect network behavior without attaching external debuggers.

interceptor-request-utils.ts

Found in packages/shared/src/interceptor-request-utils.ts, this utility library extracts request context including the target model, provider identity, and specific endpoint. It also handles the construction of transformed request bodies, ensuring that provider-specific payloads conform to the unified schema expected by the Pi backend.

Build and Injection Pipeline

The scripts/electron-build-main.ts script bundles the three source files into a single CommonJS file at apps/electron/dist/interceptor.cjs. The Pi Agent Server then loads this bundle using Node’s --require flag when spawning subprocesses, as implemented in packages/pi-agent-server/src/index.ts.

Runtime Execution Flow

When the Pi backend initializes, the interceptor establishes itself through a precise six-stage lifecycle:

  1. Bundle Creation: During the build phase, the TypeScript source files are compiled and bundled into apps/electron/dist/interceptor.cjs.
  2. Process Injection: The Pi server launches the Pi SDK subprocess with the command node --require apps/electron/dist/interceptor.cjs <entry-point>, forcing Node to evaluate the interceptor before any SDK code executes.
  3. Proxy Installation: The bundle registers a Proxy around the global fetch function (const fetchProxy = new Proxy(interceptedFetch, ...)), ensuring all subsequent fetch calls route through the interceptor’s logic.
  4. Request Transformation: The interceptedFetch function parses each request, normalizes payloads between Anthropic and OpenAI formats, applies feature-flag tweaks (such as upgrading cache_control TTL), and logs raw data to interceptor.log.
  5. Error Normalization: If the response contains an HTML error page rather than valid JSON, the interceptor converts it into a synthetic 400-style JSON error object, preserving the original HTTP status for downstream diagnostics while preventing SDK parsing failures.
  6. Metadata Injection: For tool invocations, the interceptor decorates outgoing tool definitions with additional fields such as toolId and displayName, making them visible to Craft’s higher-level orchestration layers without requiring SDK modifications.

Implementing the Interceptor in the Pi Backend

The Pi server automatically handles interceptor injection when spawning child processes. The following excerpt from packages/pi-agent-server/src/index.ts demonstrates how the server resolves the interceptor path and prepends it to the Node execution arguments:

// packages/pi-agent-server/src/index.ts (excerpt)
const interceptorPath = runtime.paths?.interceptor;
if (interceptorPath) {
  // Spawn the Pi subprocess with the interceptor pre-loaded.
  args.unshift('--require', interceptorPath);
}

For standalone usage outside the Pi server, you can manually preload the interceptor when launching any Node.js script that utilizes the Pi SDK:

// standalone.ts – demonstrates the same proxy outside of the Pi server
import { join } from 'path';
import { spawn } from 'child_process';

// Path to the built interceptor bundle
const interceptor = join(__dirname, '../../apps/electron/dist/interceptor.cjs');

// Launch a Node process that will load the interceptor first
const child = spawn('node', ['--require', interceptor, 'my-pi-client.js'], {
  stdio: 'inherit',
});

Request Normalization and Error Recovery

The interceptor automatically detects the target provider from the request URL and performs real-time payload transformation. For example, when communicating with Anthropic endpoints, it converts Claude-specific JSON structures into OpenAI-compatible shapes:

// Inside unified-network-interceptor.ts (simplified)
if (request.url.includes('anthropic.com')) {
  // Convert Claude-style JSON to OpenAI-compatible shape
  body = {
    model: request.body?.model ?? 'claude-sonnet-4',
    messages: [{ role: 'user', content: request.body?.prompt }],
    ...commonOptions,
  };
}

This normalization ensures that downstream tools in the Craft Agents pipeline can consume responses uniformly regardless of the upstream provider. Additionally, when corporate proxies or firewalls return HTML error pages instead of JSON, the interceptor catches these non-standard responses and wraps them in synthetic JSON error objects, preventing the Pi SDK from throwing parsing exceptions and allowing the system to degrade gracefully with full diagnostic context.

Summary

Frequently Asked Questions

How does the network interceptor attach to the Pi SDK without modifying its source code?

The interceptor leverages Node.js’s --require flag to preload interceptor.cjs before the Pi SDK loads. This script wraps the global fetch function with a JavaScript Proxy, intercepting all subsequent HTTP calls made by the SDK transparently. Because this occurs at the runtime level rather than the source code level, the SDK operates unaware that its network layer has been instrumented.

What purpose does the interceptor.log file serve in the Pi backend?

The interceptor.log file, written by the logging utilities in packages/shared/src/interceptor-common.ts, provides a complete audit trail of raw HTTP requests and responses. Developers can inspect this file to troubleshoot provider-specific request failures, verify header transformations, and diagnose network issues such as corporate proxy interference or malformed JSON responses.

How does the interceptor handle provider-specific request formats like Anthropic versus OpenAI?

The interceptor inspects the request URL to detect the provider, then uses transformation logic in packages/shared/src/unified-network-interceptor.ts to rewrite the payload structure. For instance, it converts Anthropic’s native message formats into OpenAI-compatible schemas, ensuring that the rest of the Craft Agents system receives a standardized data structure regardless of which AI provider serves the request.

Can the network interceptor be used outside the Pi backend in other Node.js applications?

Yes. Any Node.js application can load the interceptor by launching with --require /path/to/interceptor.cjs. The packages/shared/src modules are designed to be provider-agnostic, allowing the interceptor to normalize traffic for any AI SDK that relies on the global fetch function, including custom clients or other AI frameworks running within the same Craft Agents ecosystem.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →