DEV Community

Cover image for Provider-Proof Your Agent Stack
Max Quimby
Max Quimby

Posted on Originally published at agentconn.com

Provider-Proof Your Agent Stack

Provider-Proof Your Agent Stack

On August 28, 2026, OpenAI announced it would terminate Cursor's access to GPT models by November 12. The trigger was not a technical failure, a billing dispute, or a terms-of-service violation by Cursor's engineering team. It was an ownership change: SpaceX acquired Cursor's parent company Anysphere for $60 billion, and OpenAI's change-of-control clause gave them the exit they wanted.

📖 Read the full version with charts and embedded sources on AgentConn →

This is not an isolated incident. It is the fourth documented API revocation in eighteen months. Anthropic cut Windsurf's Claude access with less than five days' notice in June 2025. Anthropic revoked OpenAI's own API access after engineers were caught using Claude Code internally. In January 2026, Anthropic server-side blocked third-party harnesses using subscription OAuth tokens. The pattern is unmistakable: model providers are using API access as a competitive weapon, and the casualties are teams that hardwired their agent stack to a single provider.

If your agents depend on one model endpoint, you are running on borrowed time. The fix is not switching providers — it is building a harness that treats every provider as replaceable.

Here are five patterns that make your agent stack provider-proof.

@OpenAI on X — We're ending our partnership with Cursor following its acquisition by SpaceX

View original post on X →

The Cutoff Ledger: Why This Keeps Happening

Before diving into patterns, understand the failure mode. Every documented cutoff shares the same root cause: a business relationship changed, and the technical dependency had no insulation layer.

Date Lab Target Notice Trigger
Jun 2025 Anthropic Windsurf <5 days OpenAI acquisition talks
Jul 2025 Anthropic OpenAI Immediate Engineers using Claude Code
Jan 2026 Anthropic Third-party harnesses None "Unusual traffic patterns"
Aug 2026 OpenAI Cursor 76 days SpaceX acquisition

CNBC reported that Cursor co-founder Michael Truell disclosed OpenAI models account for roughly 5% of Cursor's traffic — the company had already diversified. Most teams building agents have not.

Hacker News discussion — Our decision on Cursor following its acquisition by SpaceX — 302 comments

View discussion on Hacker News →

The Hacker News discussion of OpenAI's decision surfaced a recurring theme: developers asking what happens to their workflows when a provider disappears. The answer should be "nothing" — if your harness is built right.

Pattern 1: Provider Abstraction Layer

The most fundamental pattern. Your agent code should never import a provider SDK directly. Every LLM call goes through an abstraction that normalizes request and response formats across providers.

from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import AsyncIterator

@dataclass
class LLMResponse:
    content: str
    model: str
    provider: str
    usage: dict

class LLMProvider(ABC):
    """Every provider implements this interface.
    Your agent code calls this — never the SDK directly."""

    @abstractmethod
    async def complete(
        self,
        messages: list[dict],
        model: str,
        temperature: float = 0.7,
        tools: list[dict] | None = None,
    ) -> LLMResponse: ...

    @abstractmethod
    async def stream(
        self,
        messages: list[dict],
        model: str,
    ) -> AsyncIterator[str]: ...

class AnthropicProvider(LLMProvider):
    async def complete(self, messages, model, temperature=0.7, tools=None):
        # Translate to Anthropic's format, call API, normalize response
        ...

class OpenAIProvider(LLMProvider):
    async def complete(self, messages, model, temperature=0.7, tools=None):
        # Translate to OpenAI's format, call API, normalize response
        ...
Enter fullscreen mode Exit fullscreen mode

The key insight from Ken Huang's deep-dive on model routing: Claude Code implements compile-time provider abstraction supporting Anthropic Direct, AWS Bedrock, Google Vertex, and Azure Foundry. Hermes Agent goes further — it treats routing as a runtime concern, dynamically detecting API formats from URLs and rebuilding clients mid-session without a restart.

Ken Huang on Substack — Chapter 14: Model Routing and Provider Abstraction

View on Substack →

The practical choice: If you are building a new agent stack, use LiteLLM as your abstraction layer. It normalizes 100+ providers behind one OpenAI-format endpoint and handles the translation you would otherwise write yourself. If you need more control, build the thin interface above and wrap each provider SDK behind it.

â„šī¸ Implementation cost: Under one day to retrofit an existing agent. Replace direct SDK imports with the abstraction interface, write one adapter per provider you use, and route all calls through a factory. The interface stabilizes your agent code — provider SDKs can change without touching your agent logic.

Pattern 2: Model Routing with Fallback Chains

Abstraction gets you a uniform interface. Routing decides which provider handles each request — and what happens when the primary fails.

A production routing layer needs three capabilities:

  1. Primary selection — route by task type, cost, or latency requirements
  2. Health-aware fallback — when the primary returns errors, automatically try the next provider in the chain
  3. Context translation — strip provider-specific features (thinking blocks, tool schemas) that the fallback model cannot parse
# routing-config.yaml
routes:
  reasoning:
    primary: { provider: anthropic, model: claude-sonnet-5 }
    fallback:
      - { provider: openai, model: gpt-5.5 }
      - { provider: google, model: gemini-2.5-pro }
    strategy: first_healthy

  fast_tasks:
    primary: { provider: anthropic, model: claude-haiku-4 }
    fallback:
      - { provider: openai, model: gpt-4.1-mini }
    strategy: cheapest_available

  coding:
    primary: { provider: anthropic, model: claude-sonnet-5 }
    fallback:
      - { provider: google, model: gemini-2.5-pro }
      - { provider: xai, model: grok-4.6 }
    strategy: first_healthy
Enter fullscreen mode Exit fullscreen mode
class ModelRouter:
    def __init__(self, config: dict, providers: dict[str, LLMProvider]):
        self.routes = config["routes"]
        self.providers = providers

    async def route(self, task_type: str, messages: list, **kwargs):
        route = self.routes[task_type]
        chain = [route["primary"]] + route.get("fallback", [])

        for candidate in chain:
            provider = self.providers[candidate["provider"]]
            try:
                return await provider.complete(
                    messages=messages,
                    model=candidate["model"],
                    **kwargs
                )
            except (AuthError, RateLimitError, ServiceUnavailable) as e:
                logger.warning(
                    f"Provider {candidate['provider']} failed: {e}. "
                    f"Trying next in chain."
                )
                continue

        raise AllProvidersExhausted(task_type, chain)
Enter fullscreen mode Exit fullscreen mode

Hermes Agent's implementation is instructive here: fallback is turn-scoped, with each new message starting fresh with the primary model. The fallback fires at most once within a single turn, and normal error handling takes over if the fallback provider also fails. This prevents cascading retry storms.

âš ī¸ Contrarian Corner: Is this over-engineering? Counter-argument: most teams never get cut off. Four incidents in 18 months across the entire industry is a low base rate. Over-engineering for a risk that has hit only a handful of companies might be premature. But consider two things. First, these patterns cost almost nothing to implement — the provider abstraction layer is a day of work. Second, the Cursor and Windsurf precedents show that ownership changes, not engineering failures, trigger cutoffs. You cannot predict M&A. And the patterns deliver value even without a cutoff: multi-provider routing enables cost optimization, latency reduction, and capability matching that a single-provider stack cannot offer.

Pattern 3: Credential and Secret Isolation

When you hardwire API keys into environment variables alongside your application code, a provider cutoff does not just remove a model — it forces an emergency credential rotation across your entire deployment.

The pattern: decouple credentials from configuration, and scope them per provider.

import time

class CredentialBroker:
    """Fetches provider credentials at runtime.
    Never stores keys in code, env vars, or config files."""

    def __init__(self, vault_client):
        self.vault = vault_client
        self._cache: dict[str, tuple[str, float]] = {}

    async def get_key(self, provider: str) -> str:
        # Check cache (TTL-based)
        if provider in self._cache:
            key, expires = self._cache[provider]
            if time.time() < expires:
                return key

        # Fetch from vault with least-privilege scope
        secret = await self.vault.get_secret(
            path=f"llm-providers/{provider}/api-key",
            scope="agent-runtime"
        )
        self._cache[provider] = (secret.value, time.time() + 300)
        return secret.value
Enter fullscreen mode Exit fullscreen mode

Portkey's approach takes this further: instead of API keys directly, applications use references to secrets stored in AWS Secrets Manager, Azure Key Vault, or HashiCorp Vault. The gateway resolves the reference at runtime. Your agent code never sees the actual key.

Why this matters for cutoffs: When OpenAI cuts you off, you disable one vault path and enable the replacement. No redeployment. No emergency PR to rotate hardcoded keys. No audit trail gaps from keys that lived in .env files across 30 developer machines.

â„šī¸ Practical minimum: If a full vault setup is overkill for your team, at least isolate provider keys into separate environment variables with a naming convention (LLM_ANTHROPIC_KEY, LLM_OPENAI_KEY, LLM_GOOGLE_KEY) and load them through the credential broker pattern above. The indirection layer — even a simple one — is what makes the cutoff response a config change instead of a code change.

Pattern 4: Circuit Breakers for Model Endpoints

A provider does not always announce a cutoff with a blog post and 76 days' notice. Sometimes the endpoint starts returning 403s at 2 AM on a Friday. Your harness needs to detect this and react automatically.

The circuit breaker pattern, adapted from distributed systems for AI agents, tracks per-provider failure rates and automatically trips when a threshold is crossed.

from enum import Enum

class CircuitState(Enum):
    CLOSED = "closed"        # Normal operation
    OPEN = "open"            # Provider is down — skip it
    HALF_OPEN = "half_open"  # Testing if provider recovered

class ProviderCircuitBreaker:
    def __init__(
        self,
        provider_name: str,
        failure_threshold: int = 5,
        recovery_timeout: float = 60.0,
    ):
        self.provider = provider_name
        self.state = CircuitState.CLOSED
        self.failure_count = 0
        self.threshold = failure_threshold
        self.recovery_timeout = recovery_timeout
        self.last_failure_time = 0.0

    def can_execute(self) -> bool:
        if self.state == CircuitState.CLOSED:
            return True
        if self.state == CircuitState.OPEN:
            if time.time() - self.last_failure_time > self.recovery_timeout:
                self.state = CircuitState.HALF_OPEN
                return True  # Allow one probe request
            return False
        return True  # HALF_OPEN: allow the probe

    def record_success(self):
        self.failure_count = 0
        self.state = CircuitState.CLOSED

    def record_failure(self):
        self.failure_count += 1
        self.last_failure_time = time.time()
        if self.failure_count >= self.threshold:
            self.state = CircuitState.OPEN
Enter fullscreen mode Exit fullscreen mode

Integrate this with the router from Pattern 2: the router checks the circuit breaker before attempting each provider in the fallback chain. A tripped breaker skips that provider entirely — no wasted latency on requests you already know will fail.

The critical nuance: Set different thresholds for different error types. A 401 (unauthorized) or 403 (forbidden) should trip the breaker immediately — that is a cutoff, not a transient failure. A 429 (rate limit) or 503 (service unavailable) should use exponential backoff before tripping. A timeout should count as a failure but with a higher threshold.

Pattern 5: Graceful Degradation

The final pattern handles the worst case: all providers in your fallback chain are down, or the one model your agent needs for a specific capability is unavailable.

Graceful degradation means your agent reduces functionality rather than crashing entirely.

class DegradationPolicy:
    """Define what happens when capabilities are unavailable."""

    POLICIES = {
        "reasoning": {
            "degrade_to": "fast_tasks",  # Use simpler model
            "user_message": "Using simplified reasoning — "
                          "results may be less detailed.",
        },
        "code_generation": {
            "degrade_to": "reasoning",
            "user_message": "Code generation is temporarily limited.",
        },
        "vision": {
            "degrade_to": None,  # No fallback — skip gracefully
            "user_message": "Image analysis is currently unavailable. "
                          "Please describe the image in text.",
        },
    }

    @classmethod
    def degrade(cls, task_type: str) -> dict | None:
        return cls.POLICIES.get(task_type)
Enter fullscreen mode Exit fullscreen mode

The hierarchy matters. When your reasoning model (Claude Sonnet 5) is unavailable, degrade to a capable alternative (GPT-5.5), not to a model that cannot handle the task. When no alternative exists for a specific capability (vision, for instance), tell the user what is unavailable and offer a workaround — do not silently fail or throw an unhandled exception.

Putting It Together: The Provider-Proof Harness

These five patterns compose into a layered architecture:

Agent Logic
    |
    v
Model Router (Pattern 2)
    |--- Circuit Breakers (Pattern 4) per provider
    |--- Degradation Policy (Pattern 5) when all fail
    |
    v
Provider Abstraction Layer (Pattern 1)
    |--- AnthropicProvider
    |--- OpenAIProvider
    |--- GoogleProvider
    |--- XAIProvider
    |
    v
Credential Broker (Pattern 3)
    |--- Vault / Secret Manager
Enter fullscreen mode Exit fullscreen mode

The LLM Gateways Compared 2026 analysis covers the build-versus-buy decision in depth. For most teams, the practical answer is: use LiteLLM or OpenRouter as your gateway layer, and build the routing and degradation logic in your harness. The gateway handles provider normalization and credential management. Your harness handles the business logic of what to do when things break.

The Maturity Checklist

Where does your agent stack sit?

Level Capability Test
0 — Fragile Single provider, direct SDK calls If your provider returns 403, your agent crashes
1 — Abstracted Provider abstraction layer in place You can swap providers by changing config, not code
2 — Resilient Fallback chains with health checks Your agent survives a provider outage without human intervention
3 — Isolated Credentials managed through vault/broker A cutoff is a config change, not an emergency rotation
4 — Adaptive Circuit breakers + graceful degradation Your agent adjusts capability in real time based on what is available

Most production agent stacks today sit at Level 0 or 1. The Cursor cutoff is a reminder that Level 2 is the minimum for anything running in production.

What the Community Is Saying

The developer reaction to the OpenAI-Cursor cutoff has been pointed. On Dev.to, one widely-shared post framed it as "The AI Coding Lock-In Lesson Every Developer Needs", offering a "15-minute exit checklist" emphasizing portable workflows: store coding rules in repositories, keep MCP configs as checked-in files, maintain prompt libraries in git, and test alternate models monthly.

Dev.to article — OpenAI Is Cutting Off Cursor: The AI Coding Lock-In Lesson Every Developer Needs

View on Dev.to →

Meanwhile, the personnel fallout is already visible. Sasha Rush — a prominent ML researcher — posted "Last day at Cursor," signaling that the SpaceX acquisition is reshaping the team even as OpenAI exits.

@srush_nlp on X — Last day at Cursor — 1.2K likes, 61K views

View original post on X →

Cursor CEO Michael Truell responded directly, noting that OpenAI models serve only about 5% of user traffic — but framing the broader point: "We've trusted their platform to be neutral infrastructure for our business."

@mntruell on X — We're sorry to see that OpenAI put out a note saying they plan to block Cursor users from accessing OpenAI models

View original post on X →

The broader signal from OpenRouter's $1.3 billion valuation is that the market has already priced in the multi-model future. Enterprises are quietly building multi-provider infrastructure not because they have been cut off, but because they have watched others get cut off and decided not to be next.

What This Means for You

If you are building agents today, here is the action list:

  1. Today: Audit your agent code for direct provider SDK imports. Every one is a single point of failure.
  2. This week: Implement Pattern 1 (provider abstraction). If you are using LiteLLM, you may already have this. If not, the interface is 50 lines of code.
  3. This sprint: Add fallback chains (Pattern 2) for your critical task types. Start with your most-used model and add one alternative.
  4. This quarter: Move credentials to a vault (Pattern 3) and add circuit breakers (Pattern 4). These are infrastructure investments that pay dividends across your entire stack, not just LLM calls.
  5. Ongoing: Test your fallback chains monthly. A fallback you have never triggered is a fallback you do not have.

The Cursor cutoff gave teams 76 days' notice. The Windsurf cutoff gave less than five. The third-party harness block gave none. Build your harness so the notice period does not matter.

For the industry and security analysis of the OpenAI-Cursor cutoff, see our companion piece on ComputeLeap: OpenAI's Cursor Cutoff Exposes AI's Supply-Chain Risk.

For more on why the harness — not the model — is the moat, see DeepSeek Open-Sourced a Harness to Rival Claude Code and Stop Chasing Models. Fix Your Harness.

Originally published at AgentConn

Top comments (0)