AgentCC

Documentation

Everything a developer needs to wire AgentCC into an agent: install, wrap, enforce budgets, route and cache calls, track outcomes, and let the platform open fix PRs — without a single prompt or completion ever leaving your process.

On this page

What is AgentCC?

AgentCC is drop-in middleware for LLM clients. You wrap your existing client in one line; it meters every call and acts on the waste for you — routes easy calls to cheaper models, replays repeat responses from cache, and auto-stops runaway agents before they torch your budget. Every lever is flipped from the dashboard, never from your code, so you integrate once and tune it for life without a redeploy.

CapabilityWhat you get
Cost per successThe board metric, not raw tokens: spend ÷ successful completions, tracked by workflow. Cheap answers that cause rework don't count as savings.
Routing & cascadingSend low-difficulty work to cheaper models — auto-heuristic or explicit policy, set from the dashboard and pushed to the SDK live.
Response cachingReplay identical requests for $0 from memory, the hosted store, or your own Redis/Upstash. Zero added latency, fails open.
EnforcementBudgets + auto-stop, a hard call cap, and a kill switch that actually stops a runaway agent mid-loop. Visibility tools only watch.
Auto-remediationPolicies act on waste signals automatically — downshift or kill on threshold, with cooldowns and a full audit log.
Privacy by designNo prompts, completions, or API keys ever leave your process — only the usage object and content-free metadata.
One line, any frameworkSame methods, types, and return values. Telemetry is post-response and fire-and-forget — it never adds latency.
Scope & honesty: routing and caching currently take effect on the OpenAI adapter; the other adapters record telemetry and honor the kill switch. Today's cache is exact-match (same prompt + model) — semantic caching is on the roadmap, not claimed as shipped.

Quickstart

  1. Get an API key

    Create an account and mint a key on the API Keys page, then set it as ACC_KEY in your environment.

  2. Install the SDK (Node 18+)
    terminal
    npm install agentcc
    # plus whichever framework you already use:
    #   openai · ai · @langchain/core · @openai/agents

    Every framework is an optional peer dependency — install only the one you already use.

  3. Wrap your client

    One line, then keep calling the client exactly as before — same methods, same types, same return values. Import from the subpath matching your framework:

    FrameworkImportWrapper
    OpenAI · Anthropic · Gemini · Mistral · CohereagentccwithCostControl(client, opts)
    Vercel AI SDK / Mastraagentcc/aiwithCostControl(model, opts)
    LangChain.js / LangGraph.jsagentcc/langchainwrapModel(model, opts)
    OpenAI Agents SDKagentcc/agentswrapAgentsModel(model, opts)

How it works

Each adapter hooks its framework at the model boundary and normalizes the finished call into one record fed to a single shared core. The core reads the usage object after the response returns, computes cost from a price table, fingerprints the prompt (sizes + one-way hash — never content), and pushes the record onto an in-memory queue that flushes in batches over HTTPS.

architecture
your code ──▶ withCostControl(client) ──▶ looks identical ──▶ create() as normal
                       │
                       └─▶ CallRecord ──▶ core ──▶ queue ──▶ fetch ──▶ dashboard

Telemetry is fire-and-forget: it never adds latency and never throws into your request path. Batches flush every 5 seconds by default, or early once 50 events queue. Streaming works too — the SDK auto-requests usage stats and reads them off the final chunk.

Framework adapters

The same agentId / accKey / kill-switch options apply to every adapter. Pick your SDK:

agent.ts
import { withCostControl } from "agentcc";
import OpenAI from "openai";

const client = withCostControl(new OpenAI({ apiKey: process.env.OPENAI_API_KEY }), {
  agentId: "my-agent",                 // a stable name per agent
  accKey: process.env.ACC_KEY,  // create one under API Keys
  // killCheck is on by default — it's inert until you set a budget + auto-stop.
});

// Use it exactly like the OpenAI client.
await client.chat.completions.create({
  model: "gpt-4o",
  messages: [{ role: "user", content: "Hello" }],
});
Mastra is built on the Vercel AI SDK — wrap the model with withCostControl from agentcc/ai and pass it to your Mastra Agent. All adapters feed the same privacy-safe pipeline, so the guarantees hold everywhere.

Configuration reference

Only agentId and accKey are required — everything else has a safe default.

OptionDefaultDescription
agentIdrequiredIdentifies the agent for this client.
accKeyrequiredBearer token for the telemetry endpoint.
endpointhttps://agentcc.ca/v1/eventsTelemetry ingest URL.
flushInterval5000Max ms before a buffered batch is sent.
batchSize50Send early once this many events queue.
killChecktrueCheck kill status before each call; throw AgentKilledError if killed. Inert until a budget + auto-stop is set in the dashboard.
onKilledthrowRun instead of throwing when a killed agent's call is blocked; its return value becomes the response (graceful containment).
onErrorswallowCalled on telemetry/pricing failures.
routeroffModel routing: "auto" or a RoutePolicy. OpenAI adapter only.
cacheoffExact-match response cache (memory · upstash · redis · managed). OpenAI adapter only.

Budgets & kill switch

Set a budget or hard call cap on an agent in the dashboard and turn on auto-stop; once spend or call count crosses the limit the backend marks the agent killed. With killCheck on (the default), the SDK checks the agent's status before each call and throws AgentKilledError instead of hitting the LLM — stopping a runaway loop before it spends more.

kill-switch.ts
import { withCostControl, AgentKilledError } from "agentcc";

const client = withCostControl(new OpenAI(), {
  agentId: "support-bot",
  accKey: process.env.ACC_KEY,
  killCheck: true, // the default
});

try {
  await client.chat.completions.create({ model: "gpt-4o", messages });
} catch (err) {
  if (err instanceof AgentKilledError) {
    // agent was killed from the dashboard — stop looping
  }
}

Status is cached briefly and fails open — a status-endpoint outage never blocks your calls. Killing agent X never touches agent Y. Prefer graceful degradation over a thrown error? Pass onKilled and its return value becomes the call's response, so one killed sub-agent doesn't crash the whole run. A killed agent's streaming call returns an empty stream, so for await simply ends.

Model routing

Send cheap, simple calls to a cheaper model before the call is made. Opt-in, zero-latency, fails open, and the response shape stays identical. OpenAI adapter only.

routing.ts
import { withCostControl } from "agentcc";

// "auto": tool-free calls under ~2k tokens downshift (e.g. gpt-4 → gpt-4o-mini).
const client = withCostControl(new OpenAI(), {
  agentId: "bot",
  accKey: process.env.ACC_KEY,
  router: "auto",
});

// …or an explicit policy (priority-sorted; first match wins):
const client2 = withCostControl(new OpenAI(), {
  agentId: "bot",
  accKey: process.env.ACC_KEY,
  router: {
    routes: [
      {
        name: "tiny",
        condition: { type: "token_estimate", max: 500 },
        targetModel: "gpt-4o-mini",
      },
    ],
  },
});

The model actually used is what's recorded, so cost telemetry reflects routing. A routed call that errors automatically retries on the original model. The dashboard can also push a routing policy down via the status endpoint — that's how auto-remediation's "downshift" action takes effect with no code change.

Response cache

Replay an identical request instead of paying for it again. Keyed on the content-free prompt fingerprint + model — one keyed read, no embedding call. A hit skips the LLM entirely and is recorded with cache.hit: true and $0 cost.

cache.ts
const client = withCostControl(new OpenAI(), {
  agentId: "bot",
  accKey: process.env.ACC_KEY,
  cache: { provider: "memory" }, // or "upstash" / "redis" with { url, token? } — BYODB
});

// No store to manage? Use the hosted cache — proxied with your accKey,
// storage-capped per account, our credentials never reach your process:
const client2 = withCostControl(new OpenAI(), {
  agentId: "bot",
  accKey: process.env.ACC_KEY,
  cache: { provider: "managed" },
});

The cache stores the raw provider response, which lives only in your store (memory / your Redis / Upstash) — it is never sent to the telemetry endpoint. The cache backend (off / managed / bring-your-own-DB) can also be set per agent in the dashboard and pushed to the SDK, so you can turn caching on or swap credentials without a code change. A pushed config takes precedence over the local cache option.

Outcome tracking

Token savings only count if reliability holds — a cheap-but-wrong answer that needs rework isn't cheap. reportOutcome marks how a finished workflow turned out, so the dashboard can divide spend by successful completions and show failure / rework rates.

outcome.ts
import { reportOutcome } from "agentcc";

const res = await client.chat.completions.create({ model: "gpt-4o", messages });

// Judge success however you already do (schema validated? user accepted? eval passed?):
await reportOutcome("success", { agentId: "support-bot", accKey: process.env.ACC_KEY });
// …or "failure" / "rework" — plus an optional `workflow` label for by-workflow rollups.

It's a standalone, stateless function — it works identically across every adapter, POSTs with your accKey, and fails open (a transport error never throws into your code). Only the enum and an optional workflow label leave the process.

What leaves your process

Never your API keys, prompts, or completions. Each event carries the usage object plus content-free metadata: a prompt fingerprint, tool names (never arguments), and a one-way output hash.

fingerprint.jsonc
"prompt": {
  "message_count": 14,
  "total_chars": 21840,
  "roles": {
    "system":    { "count": 1, "chars": 1800 },
    "user":      { "count": 7, "chars": 9200 },
    "assistant": { "count": 6, "chars": 10840 }
  },
  "hash": "9f2c…"   // SHA-256 of the message array (one-way)
}

The fingerprint is enough to diagnose why an agent burns tokens without shipping a character of content: climbing total_chars means history is re-sent and growing (prompt bloat); the same hash recurring means the agent is looping; a large roles.system.chars means a fat system prompt. The hash is one-way — identical prompts collide so you can correlate them, but the original text is never recoverable. See the privacy policy for the full picture.

HTTP API reference

The SDK speaks a small HTTP contract — you can also hit it directly. All endpoints authenticate with Authorization: Bearer <accKey>.

POST /v1/events — ingest telemetry

Accepts { events: [...] }. Each event needs an agent_id; unknown agents are created on first sight. Cost is recomputed server-side from the model's price table — a client can't under-report spend. Cache hits are forced to $0. The response tells you whether budget enforcement or auto-remediation fired.

terminal
curl -X POST https://agentcc.ca/v1/events \
  -H "Authorization: Bearer $ACC_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "events": [{
      "agent_id": "support-bot",
      "model": "gpt-4o",
      "input_tokens": 1200,
      "output_tokens": 300,
      "latency_ms": 900
    }]
  }'

# → { "accepted": 1, "stopped": false, "remediated": false }

GET /v1/agents/:agentId/status — kill switch + config

Polled by the SDK before calls. Returns the agent's status and any dashboard-pushed config (routing policy, cache backend). Fails open: an unknown agent reads as active.

terminal
curl https://agentcc.ca/v1/agents/support-bot/status \
  -H "Authorization: Bearer $ACC_KEY"

# → { "status": "active" }
# → { "status": "killed" }                        # over budget with auto-stop on
# → { "status": "active", "config": { "routing": "auto" } }   # dashboard-pushed config

POST /v1/outcomes — outcome ingest

Accepts { outcomes: [...] } where each outcome is success, failure, or rework, with an optional workflow label.

terminal
curl -X POST https://agentcc.ca/v1/outcomes \
  -H "Authorization: Bearer $ACC_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "outcomes": [{
      "agent_id": "support-bot",
      "outcome": "success",
      "workflow": "close-ticket"
    }]
  }'

# → { "accepted": 1 }

GitHub Autofix

Alerts tell you an agent is wasting money; Autofix fixes the cause at the source. Connect the AgentCC GitHub App from the dashboard, map an agent to its repository, and every detected issue gets a Create PR button.

  1. You click Create PR on an alert (always manual — nothing is dispatched automatically).
  2. A worker downloads the repo snapshot into a sandbox and runs a Claude-powered fix engine with read/grep/edit tools, jailed to that directory, guided by a playbook for the specific alert type (loop, prompt bloat, cost spike…).
  3. The diff passes guardrails — at most 12 files and 150 KB, and it can never touch .github/, .env*, or lockfiles.
  4. The change is pushed to a fresh acc/fix-* branch (never your default branch) and opened as a pull request you review like any other.

The content-free invariant holds here too: your repository's code and the model's output exist only in the sandbox and in the PR on your repo — AgentCC's database stores only numeric evidence and job status.

Next steps

  • Watch the demo — the whole product in under a minute, plus a guided screenshot tour.
  • agentcc on npm — package, changelog, and the full readme.
  • Support — questions, privacy, and security.
  • Sign up — mint an API key and wrap your first agent.