codai docs
Agents

Tools and sub-agents

The built-in tools an agent run can use, the web tools contract shared by every codai agent, the standalone /v1/tools and /v1/research endpoints, and how spawn_agent fans work out to typed sub-agents.

Inside an agent run — or a chat completion with x-codai-server-tools: 1 — the model gets a set of gateway-executed tools. It calls them like any function tool; the gateway runs every call of a turn in parallel and feeds the results back. You never see the tool traffic unless you read the run's steps.

Built-in tools

ToolWhat it doesAvailable when
http_fetchFetch a URL server-side.Your key's tool level allows HTTP (l1_http or higher).
json_queryQuery a JSON document.Same as http_fetch.
exec_pythonRun Python in a sandbox — the "code interpreter" capability.Sandbox enabled on the gateway.
read_artifactRead back a large tool result the gateway spilled to storage instead of inlining.Spilling enabled and at least one tool active.
memory_recall, memory_rememberLong-term memory scoped to your account.Your plan includes persistent memory.
use_skillLoad one of your skills into the loop.At least one skill visible to the key.
web_search, web_fetchVerified web search and page reading, same contract as every other surface (below).Web search configured on the gateway.
MCP toolsTools exposed by the MCP servers attached to your key.Tool level is not none.
delegate_to_modelcodai's internal single-completion router across the models you may call.Always, when ≥ 2 models are allowed.
spawn_agentA typed sub-agent with its own context and tool loop.See below.

Tool level is a property of your key. none disables every executed tool; l1_http allows network reads; l3_sandbox (cloud sandbox: shell, Python, files) and l4_browser unlock the heavier sub-agent roles.

spawn_agent

When server-side tools are on, the model also gets spawn_agent. Unlike delegate_to_model (one completion), a spawned child runs its own loop with its own context. The model picks a role; the gateway picks the tier, tool level and step budget for it.

Prop

Type

RoleTierMax stepsMax tool levelPurpose
exploresmall6l1_httpRead-only facts. The default.
researchsmall8l1_httpweb_search + http_fetch, returns a sourced summary.
codelarge12caller's level (up to l4_browser)Implement and verify; returns diff or output.
verifysmall6l3_sandboxRun checks, report PASS/FAIL per check.
summarizesmall3l1_httpDigest one source.

Semantics:

  • Parallel by design. Every spawn_agent call in one assistant turn runs concurrently, and concurrently with the turn's other tool calls. The fan-out is capped per turn (default 4); calls past the cap receive {"error":"delegation_fanout_capped"} as their tool result.
  • Children never spawn. A child has spawn_agent removed, runs on the parent's alias with max_tokens: 4096, and bills as a continuation of the parent turn — it does not start a new task.
  • Cost ceiling. Child spend counts toward the request's cost ceiling (default 5 USD); once reached, further spawns return {"error":"request_cost_ceiling"}.
  • Result shape. The child's answer comes back to the parent as an ordinary tool message prefixed SUB-AGENT[<role>] RESULT:.
  • Progress frames. On a streamed chat completion, the gateway emits vendor frames inside the SSE delta so a UI can render live sub-agent cards: {"codai":{"event":"subagent_started","id","role","task"}} and {"codai":{"event":"subagent_done","id","role","ok","preview"}}. Ignore them if you only want text.
  • Invalid arguments → {"error":"invalid_arguments"}; a child that fails → {"error":"sub_agent_failed"}.

spawn_agent is injected only when the request carries no client-side tools of its own — if you send tools, you own the loop and the gateway stays out.

Web tools: one contract everywhere

codai has three web tools, and every agent surface exposes exactly the same ones — same names, same JSON schemas, same limits, same gateway endpoints and the same text rendering for the model. The single source is packages/agent-core/spec/tools-web.json; a parity test fails the build when a surface drifts from it.

ToolEndpointWhat it does
web_searchPOST /v1/tools/searchVerified search: up to 10 results, rendered as [S1]..[Sn] lines the model cites.
web_fetchPOST /v1/tools/fetchOne page as clean markdown, optionally windowed by focus; the text is fenced as untrusted content.
deep_researchPOST /v1/research, GET /v1/research/{id}Several searches + page reads → a report where every claim cites [Sn], or a JSON object with output_schema.
SurfaceHow it shows up
VS Code extension (0.7.0+)codai_web_search, codai_web_fetch, codai_deep_research in Copilot agent mode
Desktop and the Android appBuilt-in agent tools with the same names
Headless CLI (codaid run)Same tools in the hosted agent
Hosted MCP (https://ai.codai.ro/mcp)web_search, web_fetch, deep_research
SDKstools.search, tools.fetch, research.create / retrieve / wait / run
Server-side agent loop (this page)web_search, web_fetch (no nested research)

Rules every surface follows:

  • The gateway is the only search backend for a signed-in user. A 4xx answer (rules refusal, bad arguments, rate limit) is final: the model sees an ERROR <code>: <message> line and nothing retries through another provider or an LLM.
  • lang and country are left unset unless the user asked for a market; the gateway infers them from the query.
  • published_at is never made up: when a page has no known publish date the result shows none. crawled_at is when codai fetched it.
  • Google-grounded rows, when the automatic fallback runs, are a separate block rendered as [G1]..[Gn] with the attribution line.

Pricing

Web tools are not model calls, so they write no usage row; each call is metered on its own and charged in EUR from your wallet:

CallPrice
web_search, depth: fast$3 per 1,000
web_search, depth: deep$8 per 1,000
web_fetch$0.50 per 1,000
deep_research job$0.20 per job, plus the job's own model tokens

The first 1,000 calls per user per UTC month are free, and calls made inside a codai agent turn (any surface where codai is the model) are never charged. Errors and cache hits are never charged.

Standalone search and fetch — /v1/tools

The same search and fetch engines are exposed as plain endpoints, so a client-side agent can use them without going through a model. Both need only your codai_ key, count against your per-minute / per-hour request limits (X-RateLimit-* headers, 429 rate_limit_exceeded), and do not create a usage row — they are metered as web tool calls (see pricing).

POST /v1/tools/search

Prop

Type

Any date filter (freshness, published_after, published_before) drops pages whose publish date is unknown — a crawl date is never used in its place. Query operators work too: site:, -site:, filetype:, intitle:, inurl:, "exact phrase", -term, A OR B, after:, before:, lang:xx.

curl https://ai.codai.ro/v1/tools/search \
  -H "Authorization: Bearer $CODAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "query": "react compiler release", "include_domains": ["react.dev"], "published_after": "2026-01-01" }'

Response — every result is verified: the snippet was found on the live page before it was returned.

{
  "results": [
    {
      "title": "React Compiler v1.0",
      "url": "https://react.dev/blog/…",
      "snippet": "React Compiler is now stable …",
      "source": "docs",
      "verified": true,
      "published_at": "2026-02-10T00:00:00Z",
      "crawled_at": "2026-10-18T07:12:44Z"
    }
  ],
  "provider": "codai",
  "took_ms": 412,
  "cached": false,
  "coverage": 1,
  "sources_tried": ["docs", "index"],
  "degraded": [],
  "intent": "docs"
}

published_at is null when the page has no known publish date; page_age is a legacy alias kept only when it is known. degraded lists real source failures only; informational pipeline codes go to notes. If the search service is unreachable the gateway answers 503 search_unavailable (type: server_error) — retry later; nothing was charged.

POST /v1/tools/fetch

Prop

Type

curl https://ai.codai.ro/v1/tools/fetch \
  -H "Authorization: Bearer $CODAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://nodejs.org/en/blog/release/v24.0.0", "max_chars": 4000, "focus": "ESM loader hooks" }'
{
  "url": "https://nodejs.org/en/blog/release/v24.0.0",
  "final_url": "https://nodejs.org/en/blog/release/v24.0.0",
  "title": "Node.js 24.0.0 (Current)",
  "byline": "The Node.js Project",
  "description": "…",
  "canonical": "https://nodejs.org/en/blog/release/v24.0.0",
  "lang": "en",
  "content_type": "text/html",
  "content": "## Module customization hooks\n\n…",
  "chars": 3811,
  "truncated": true,
  "took_ms": 688,
  "cached": false
}

Limits: 10 s timeout, 3 MB body cap, at most 3 redirects, and an SSRF guard on every hop — private, loopback and credential-bearing URLs, or non-HTTP schemes, are 400 bad_request. Accepted content types are HTML, plain text, JSON, XHTML, Markdown and XML; a non-2xx upstream is 502 upstream_error with details: { status, url }. Fetches are cached for 15 minutes, searches for 10.

The fetched text is third-party content. Agent surfaces wrap it in an untrusted fence and tell the model never to follow instructions found inside it; do the same if you feed it to a model yourself.

Deep research — /v1/research

POST /v1/research starts an asynchronous job: codai plans several searches, reads the best pages and writes a report where every claim cites [Sn], followed by the sources. It takes 20 s to a few minutes. Full reference: Research API.

Prop

Type

const job = await codai.research.run({ question: 'Which EU countries require B2B e-invoicing in 2026?', max_sources: 15 });
console.log(job.report, job.sources);

These endpoints fetch from the public internet on your behalf. Do not pass them user-supplied URLs you have not validated yourself — the SSRF guard protects codai's network, not your intent.

Last updated:

On this page