How the unified-network-interceptor Handles Pi-Only Requests in craft-agents-oss
The unified-network-interceptor.ts acts as a global fetch interceptor that preloads exclusively into the Pi SDK subprocess via Bun's --preload mechanism, rewriting tool metadata and normalizing SSE streams while remaining isolated from the Claude SDK.
The unified-network-interceptor is a specialized network shim within the craft-ai-agents/craft-agents-oss repository designed to intercept and modify HTTP requests made specifically by the Pi agent. Because the Pi SDK runs on Bun rather than Node.js, the interceptor leverages Bun's preload mechanism to patch globalThis.fetch before any application code executes. This architecture ensures that every outgoing request—whether to Anthropic or OpenAI APIs—passes through metadata injection, SSE normalization, and proxy handling logic that is intentionally excluded from the Claude SDK's native binary runtime.
Preload Mechanism and Bun Configuration
The interceptor activates through a pre-load mechanism declared in the repository's bunfig.toml. This configuration ensures the interceptor executes before the Pi SDK imports fetch, guaranteeing complete request interception.
In bunfig.toml, the interceptor is declared as a preload script:
# https://github.com/craft-ai-agents/craft-agents-oss/blob/main/bunfig.toml
preload = ["./packages/shared/src/unified-network-interceptor.ts"]
When the Pi process starts with bun run, Bun automatically applies the --preload flag to this path. This execution order is critical because it allows the interceptor to replace the native fetch implementation before any SDK code loads, ensuring 100% request coverage for the Pi agent.
Runtime Resolver and Bundle Path Resolution
Production environments (packed Electron builds) use a different loading strategy. The interceptor is bundled as dist/interceptor.cjs, and the runtime resolver locates the correct file via resolveInterceptorBundlePath in packages/agent/backend/internal/runtime-resolver.ts.
This resolver implements a fallback chain:
- Production: Load the bundled CJS file from the Electron distribution
- Development: Fall back to the TypeScript source file to enable hot-reloading without rebuilds
Build scripts in scripts/build/common.ts and scripts/electron-build-main.ts handle copying the interceptor source or bundled .cjs into the Electron distribution during the packaging process.
Core Interception Logic for Pi-Only Requests
The unified-network-interceptor.ts file in packages/shared/src/ performs several transformation layers on Pi SDK network traffic.
Patching globalThis.fetch
The interceptor replaces globalThis.fetch with a wrapper function that inspects and rewrites requests before they reach the network layer. This monkey-patching approach captures all HTTP traffic from the Pi subprocess without requiring changes to the SDK itself.
Tool Metadata Injection
For both Anthropic and OpenAI-style requests, the interceptor performs bidirectional metadata handling:
- Outgoing requests: The
injectMetadataIntoToolSchemafunction adds_intentand_displayNamefields to tool schemas before they reach the API - Incoming responses: When the Pi SDK receives a
tool_useortool_callblock lacking metadata, the interceptor looks up stored values intoolMetadataStoreand reinserts them viainjectMetadataIntoHistory
This ensures conversation history retains human-readable tool names and intents even when the underlying API strips these fields.
SSE Stream Processing
The interceptor normalizes Server-Sent Events (SSE) streams differently for each provider:
Anthropic: createAnthropicSseStrippingStream buffers tool_use deltas, extracts metadata from the JSON payload, strips these internal fields, and re-emits clean SSE events to the Pi SDK.
OpenAI: createOpenAiSseStrippingStream consolidates fragmented tool_calls—including handling "DeepSeek" quirks—into single SSE events containing id, name, and cleaned arguments. This prevents the Pi SDK from misinterpreting argument-only deltas as new tool invocations.
Error Handling and Proxy Support
The interceptor implements robust error handling by throwing MalformedBodyError for locally detectable request body problems. It converts these exceptions into synthetic 400 responses so the Pi SDK receives actionable error messages rather than network failures.
For proxy support, the interceptor reads HTTPS_PROXY, HTTP_PROXY, and NO_PROXY environment variables, applying proxying selectively while logging sanitized URLs (with credentials redacted) for security.
Why the Interceptor Is Pi-Only
The unified-network-interceptor is explicitly restricted from the Claude SDK. According to packages/shared/CLAUDE.md:
"The network interceptor (
unified-network-interceptor.ts) is currently Pi-only: it preloads into the Pi subprocess via Bun--preload. The Claude SDK no longer runs under Bun…"
This limitation exists because the Claude SDK now spawns a native binary rather than a Bun process, making the preload mechanism incompatible. Consequently, all interceptor features—metadata injection, SSE stripping, fast-mode handling, and proxy logic—execute exclusively for Pi agent requests.
Summary
- The
unified-network-interceptoris a global fetch interceptor that preloads viabunfig.tomlbefore Pi SDK execution - It patches
globalThis.fetchto inject_intentand_displayNamemetadata into tool schemas usinginjectMetadataIntoToolSchemaandtoolMetadataStore - SSE streams are normalized via
createAnthropicSseStrippingStreamandcreateOpenAiSseStrippingStreamto handle fragmented tool calls - The interceptor is Pi-only because the Claude SDK uses native binaries rather than Bun, making the preload mechanism incompatible
- Production builds use
resolveInterceptorBundlePathto load bundled versions while development falls back to source files
Frequently Asked Questions
Why is the unified-network-interceptor limited to Pi-only requests?
The interceptor relies on Bun's --preload mechanism to patch globalThis.fetch before application code loads. The Claude SDK now runs as a native binary rather than a Bun process, making preload injection impossible. This architectural difference restricts the interceptor to the Pi SDK, which executes within a Bun subprocess where the preload hook remains effective.
How does the interceptor inject metadata into tool schemas?
The interceptor uses injectMetadataIntoToolSchema to add _intent and _displayName fields to outgoing tool definitions. When responses return, injectMetadataIntoHistory retrieves stored values from toolMetadataStore and reinserts them into conversation history. This ensures the Pi SDK maintains rich tool metadata even when APIs strip these internal fields from their responses.
What SSE processing does the interceptor perform for OpenAI requests?
For OpenAI streams, the interceptor uses createOpenAiSseStrippingStream to consolidate fragmented tool_calls into unified SSE events. This processor handles edge cases like "DeepSeek" quirks and ensures that argument-only deltas are not misinterpreted as new tool invocations. The result is a clean SSE stream containing complete id, name, and sanitized argument payloads.
How does the interceptor handle proxy configuration?
The interceptor reads HTTPS_PROXY, HTTP_PROXY, and NO_PROXY environment variables at runtime. It applies proxy routing only when necessary and logs sanitized URLs with credentials redacted for security. This proxy logic is integrated directly into the patched fetch implementation, ensuring all Pi SDK requests respect network policies without requiring manual proxy configuration in the application code.
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 →