How Instatic's AI Agent Drivers Work Without Vendor SDKs: A Deep Dive into the Provider-Agnostic Architecture

Instatic eliminates third-party SDK dependencies by implementing a provider-agnostic tool loop in server/ai/drivers/http/toolLoop.ts that uses lightweight ProviderAdapter interfaces to translate between canonical message formats and vendor REST APIs.

Instatic's architecture rejects the traditional approach of importing heavyweight vendor SDKs for each AI provider. Instead, the system relies on a single, reusable orchestration layer that communicates directly with REST endpoints through Server-Sent Events (SSE). This design allows Instatic AI agent drivers to support OpenAI, Anthropic, Ollama, and OpenRouter through minimal adapter implementations rather than bloated external dependencies.

The ProviderAdapter Interface: Minimal Vendor-Specific Code

Each AI driver implements a standardized ProviderAdapter interface that supplies only the essential translation logic needed to communicate with a provider's REST API. This interface lives in server/ai/drivers/http/toolLoop.ts and defines six critical methods:

  • buildHeaders – Constructs authentication headers, content-type declarations, and provider-specific metadata for the HTTP request.
  • mapHistory – Transforms the canonical AiMessage[] conversation log into the provider's native message format (e.g., OpenAI's chat completion format or Anthropic's message format).
  • buildRequestBody – Wraps the provider-specific message array into the final JSON payload, always setting stream: true to enable SSE responses.
  • buildToolResultMessage – Converts executed tool results back into a provider-native assistant message for the next turn.
  • createTurnTranslator – Instantiates a fresh SSE translator that converts provider-specific streaming frames into canonical AiStreamEvent objects.

Because the core runToolLoop function handles all orchestration, individual driver files remain remarkably thin. For example, server/ai/drivers/openai.ts simply imports runToolLoop and exports a function constructing the OpenAI-specific adapter, while server/ai/drivers/anthropic.ts follows the identical pattern for Claude's API.

The Tool Loop Architecture

The runToolLoop function in server/ai/drivers/http/toolLoop.ts drives the entire multi-turn conversation through a deterministic seven-step process:

Step 1: History Mapping and Request Construction

The loop begins by invoking the driver's mapHistory method to convert the stored AiMessage[] into the provider's expected format. The adapter's buildRequestBody method then assembles the complete payload including model parameters, tool definitions, and the mapped conversation history.

Step 2: HTTP POST and SSE Parsing

A single HTTP POST request is dispatched to the provider's endpoint using headers constructed by buildHeaders. The response stream feeds into parseSseStream (located in server/ai/drivers/http/sse.ts), which handles the low-level Server-Sent Events parsing. Each SSE frame passes through the adapter's createTurnTranslator, yielding standardized AiStreamEvent types including content, toolCall, error, and usage.

Step 3: Tool Execution and Retry Logic

When a turn concludes with tool calls, the loop invokes executeAiTool from server/ai/drivers/http/execTool.ts for each requested tool. Results are wrapped via buildToolResultMessage and appended to the conversation history. If the provider rejects a request due to excessive payload size—typically from embedded images—the loop implements automatic retry logic that strips older images and inserts the PROVIDER_RETRY_IMAGE_OMITTED placeholder.

Heavy Result Elision and Token Management

Multi-turn conversations with tool use risk exponential token growth when results contain large payloads like screenshots or full-page HTML. Instatic solves this through applyHeavyElision, which tracks "heavy" results across the conversation history.

Only the most recent heavy result retains its full verbatim content; earlier heavy results are stubbed to short breadcrumbs. This prevents token blow-up while preserving context that the most recent observation matters most. Token usage—including prompt tokens, completion tokens, cache reads, and cache creates—is aggregated across all turns and emitted as a single usage event when the loop terminates.

Implementing a Custom Driver Without SDKs

Creating a new driver requires only implementing the ProviderAdapter interface. Below is a complete example for a hypothetical self-hosted LLM:

import { runToolLoop } from '@/server/ai/drivers/http/toolLoop';
import type { ProviderAdapter } from '@/server/ai/drivers/http/toolLoop';

export const myAdapter: ProviderAdapter<MyMessage> = {
  label: 'MyLlama',
  endpoint: 'https://my-llama.local/v1/chat/completions',
  
  buildHeaders: (req) => ({
    Authorization: `Bearer ${process.env.MY_LLAMA_TOKEN}`,
    'Content-Type': 'application/json',
  }),
  
  mapHistory: (req) => req.messages.map((msg) => ({
    role: msg.role,
    content: msg.content.map((b) => 
      b.kind === 'text' ? b.text : '[image]'
    ).join(' '),
  })),
  
  buildRequestBody: (messages, req) => ({
    model: req.model,
    messages,
    stream: true,
    tools: req.tools,
  }),
  
  buildToolResultMessage: (results) => ({
    role: 'assistant',
    content: results.map((r) => ({
      tool_call_id: r.id,
      name: r.name,
      output: r.output,
    })),
  }),
  
  createTurnTranslator: () => ({
    translate: (frame) => [{ type: 'content', text: frame.data }],
    finish: () => ({
      stop: true,
      toolCalls: [],
      assistantMessage: null,
      usage: null,
    }),
  }),
};

// Usage remains identical to official drivers
for await (const event of runToolLoop(myAdapter, request)) {
  console.log(event);
}

This pattern—exemplified in server/ai/drivers/ollama.ts for local LLMs and server/ai/drivers/openrouter.ts for aggregated APIs—demonstrates how new providers integrate without adding SDK dependencies.

Summary

  • Instatic's AI agent drivers replace vendor SDKs with a provider-agnostic tool loop centered in server/ai/drivers/http/toolLoop.ts.
  • The ProviderAdapter interface requires only six methods to handle authentication, message mapping, request construction, and SSE translation.
  • The runToolLoop function manages multi-turn conversations, tool execution via executeAiTool, and automatic retries for oversized payloads.
  • Heavy result elision prevents token exhaustion by stubbing older large payloads while keeping the most recent verbatim.
  • Token usage aggregates across all turns and emits as a single event, with cost calculations handled provider-agnostically.

Frequently Asked Questions

How does Instatic handle streaming responses without using official SDKs?

Instatic parses Server-Sent Events (SSE) manually using parseSseStream in server/ai/drivers/http/sse.ts. Each provider's adapter implements createTurnTranslator to convert native SSE frames into canonical AiStreamEvent objects. This allows the system to handle streaming content, tool calls, and error conditions uniformly across OpenAI, Anthropic, and other providers without importing their respective streaming libraries.

What happens when a tool returns a massive payload like a full screenshot?

The system invokes applyHeavyElision to track "heavy" results across the conversation history. Only the most recent heavy result (such as a screenshot or HTML dump) remains verbatim in the context window. Previous heavy results are replaced with compact breadcrumb stubs. This algorithm prevents token limit violations while maintaining the semantic importance of the freshest observation.

Can I add support for a new AI provider without modifying the core loop?

Yes. Create a new file implementing the ProviderAdapter interface with the six required methods: buildHeaders, mapHistory, buildRequestBody, buildToolResultMessage, and createTurnTranslator. Import runToolLoop from server/ai/drivers/http/toolLoop.ts and export a function that invokes it with your adapter. The existing infrastructure in server/ai/drivers/http/execTool.ts and server/ai/drivers/http/sse.ts handles the rest automatically.

How does the retry mechanism work for requests that exceed token limits?

When the provider returns a payload-too-large error, the tool loop catches this via error classification logic in server/ai/drivers/http/errors.ts. It then retries the request after stripping older images from the conversation history and inserting the PROVIDER_RETRY_IMAGE_OMITTED placeholder text. This process preserves conversation flow while ensuring the request stays within the provider's context window limits.

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 →