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:
- Bundle Creation: During the build phase, the TypeScript source files are compiled and bundled into
apps/electron/dist/interceptor.cjs. - 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. - Proxy Installation: The bundle registers a
Proxyaround the globalfetchfunction (const fetchProxy = new Proxy(interceptedFetch, ...)), ensuring all subsequentfetchcalls route through the interceptor’s logic. - Request Transformation: The
interceptedFetchfunction parses each request, normalizes payloads between Anthropic and OpenAI formats, applies feature-flag tweaks (such as upgradingcache_controlTTL), and logs raw data tointerceptor.log. - 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.
- Metadata Injection: For tool invocations, the interceptor decorates outgoing tool definitions with additional fields such as
toolIdanddisplayName, 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
- The Unified Network Interceptor operates as a transparent
fetchproxy inside the same process as the Pi SDK, enabling low-level network observation without API changes. - Key source files include
packages/shared/src/unified-network-interceptor.tsfor the main proxy logic,packages/shared/src/interceptor-common.tsfor logging, andpackages/pi-agent-server/src/index.tsfor injection orchestration. - Runtime activation occurs via Node’s
--requireflag using the bundledinterceptor.cjsfile produced byscripts/electron-build-main.ts. - The interceptor provides cross-provider normalization (Anthropic ↔ OpenAI), error resilience (HTML-to-JSON conversion), and metadata augmentation (tool ID injection).
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →