engines

An engine is what powers the loop. One field in axon.config.ts, swappable without touching agent code:

import { Axon } from "@arcforge/engines"

export default defineAgent({
    engine: Axon(),
})
EngineInference runsYou pay
Axon()ManagedYour Axon balance
Codex()OpenAIYour ChatGPT subscription
OpenRouter()OpenRouterOpenRouter directly, no markup
Cerebras()CerebrasCerebras directly
Ollama()Your machineNothing
Mock()NowhereNothing — it's for tests

Whatever the config says, * in the terminal overrides the model for the current session. See Models.

Axon

The default. If you are signed in it works with no configuration, billed against your Axon balance.

import { Axon } from "@arcforge/engines"

engine: Axon()
engine: Axon({ model: "claude-sonnet-4-6" })

model is optional — omit it for the account default, or name any model in the Axon catalogue, which includes everything reachable through OpenRouter.

No key management: authentication derives from your signed-in session. Top up from the account page at axon.arclabs.it.

Codex

OpenAI's Codex, over OAuth. Requires a ChatGPT Plus or Pro subscription — there is no API key, and Axon takes nothing.

import { Codex } from "@arcforge/engines"

engine: Codex()
engine: Codex({
    model:  "gpt-5.5",      // default
    effort: "medium",       // low | medium | high | xhigh
})

Connect once from the terminal:

:provider codex connect
:provider codex disconnect

That opens a browser to approve. The connection refreshes itself, so it holds until you disconnect deliberately.

effort controls reasoning depth on models that support it — higher costs latency and tokens. Omit it for non-reasoning models.

OpenRouter

Your own key, 100+ models across providers, charged at OpenRouter's rates with no Axon markup.

import { OpenRouter } from "@arcforge/engines"

engine: OpenRouter({ model: "openai/gpt-4o" })

Get a key at openrouter.ai/keys, then:

:provider openrouter connect
:provider openrouter disconnect

model is an OpenRouter ID in provider/model-name form, as listed on openrouter.ai/models:

OpenRouter({ model: "anthropic/claude-sonnet-4-6" })
OpenRouter({ model: "google/gemini-2.5-pro" })
OpenRouter({ model: "meta-llama/llama-3.3-70b-instruct" })

The key is stored in your account vault, so it holds across sessions and a deployed agent uses the same connection.

Cerebras

Hosted inference built for speed. Reach for it when latency and throughput matter and the workload suits open-weight models.

import { Cerebras } from "@arcforge/engines"

engine: Cerebras({ model: "gpt-oss-120b" })
engine: Cerebras({ model: "gpt-oss-120b", temperature: 0.2 })

Needs a key in the agent's environment:

CEREBRAS_API_KEY=csk-...

For a deployed agent set it as a runtime secret rather than committing it. model is a Cerebras model ID — see the Cerebras console for what is currently available.

Ollama

Local inference. No API cost, and nothing leaves the machine. Requires Ollama installed; Axon manages the server lifecycle.

import { Ollama } from "@arcforge/engines"

engine: Ollama({ model: "qwen2.5:7b" })
engine: Ollama({
    model: "qwen2.5:7b",
    host:  "http://localhost:11434",   // default
})

model is the name as it appears in ollama list — anything you have pulled.

The route appears in * as soon as the daemon answers. Models are listed with their download size and whether they suit your hardware; selecting one you do not have pulls it with progress in the palette. Or pull directly:

ollama pull qwen2.5:7b

Mock

A deterministic engine for tests. It replaces the inference step and nothing else — the full loop still runs, tools execute, the session log accumulates, stop conditions fire. Production behaviour without a model call.

import { Mock } from "@arcforge/engines/mock"

engine: Mock()

Bare Mock() echoes the last user message back. That is what a fresh axon init boots with, before an engine is configured.

Response map. Patterns match case-insensitively as substrings against the last user message; no match falls back to echo.

engine: Mock({
    "hello":         "Hi there!",
    "sprint status": "Two issues remain in review.",
})

A reply is a step: spoken text ends the wake, or run() executes code so the loop continues and the model sees the result.

Sequences. An array plays one step per tick, in order; once exhausted the last step repeats.

import { Mock, run } from "@arcforge/engines/mock"

engine: Mock({
    "review the file": [
        run(`fs.read("src/index.ts")`),   // tick 1: act
        "The file looks correct.",         // tick 2: see the result, speak
    ],
})

run() executes real code in the real capsule under the agent's real policy. The result commits to the session log and re-enters the loop on the next tick — exactly the path a real model's tool call takes. That is what makes multi-step flows, failure handling and policy rejections deterministically testable.

Function form for full control:

engine: Mock(async (req) => {
    const last = req.messages.at(-1)?.content ?? ""
    return `You said: ${last}`
})

Return a string to speak, or run(code) to act.

Output grammar. You never choose one. A step describes intent, and Mock writes it in whichever dialect the agent's brain rendered, read from the contract block in the request:

stepclassicsfc
"some text"<text><template>
run(code)<typescript><script>

This is why one Mock({ ... }) works against any cognet — the steps are about behaviour, not syntax. It also means Mock exercises structured output for real: a run() step becomes the <script> a declared output type is checked against.

Mock streams spoken text by chunking words with small delays, so tests iterate the stream exactly as they would a real engine.