Fly with the best agent. Anywhere. Anytime.
Tern is an open-source framework for running Coding Agents and Language Models through a common interoperability layer.
The project is built around a simple belief:
The AI ecosystem evolves too quickly to commit to a single agent forever.
New Coding Agents appear regularly. New Language Models continuously redefine what is possible. Local inference is becoming increasingly practical. Deployment requirements vary across organizations and projects.
Developers should be able to adapt to these changes without rebuilding their workflows each time.
Tern aims to make that possible by enabling portable context, agent interoperability, and model interoperability.
The Arctic Tern is known for making one of the longest migrations on Earth.
Rather than remaining in one place, it continuously moves toward better conditions.
We believe software tooling should be able to evolve in a similar way.
A developer may prefer Claude Code today, Codex tomorrow, and a completely different agent next year. The same is true for Language Models, where capabilities, pricing, privacy requirements, and deployment options continue to change.
Tern is designed to help developers adapt to these changes while preserving the most important asset in an AI workflow: context.
Tern focuses on three capabilities.
Work performed in one environment should not become inaccessible when moving to another.
Tern is being designed around portable context that can move with the developer as tools evolve.
Coding Agents should be interchangeable.
Applications should not need to be rewritten whenever a new Coding Agent becomes available.
Language Models should be interchangeable.
Developers should be free to choose between hosted services, private deployments, and local inference depending on their requirements.
The examples/ directory contains working samples that demonstrate Tern's core concepts.
Server (examples/minimal-server)
All built-in coding agents and LLM providers are auto-registered by the tern package.
Starting a server requires only a config path:
srv, err := tern.New(tern.WithConfigPath("config.yaml"))
srv.Launch(ctx)
defer srv.Shutdown(ctx)Client (examples/minimal-client)
Create a session, send a message, and stream the response:
c := client.New("http://localhost:3100")
session, _ := c.CreateSession(ctx, client.SessionRequest{
Agent: "wayfinder", // Use Wayfinder as the coding agent
Model: "qwen2.5-coder:7b", // ...route LLM calls to a local model via Ollama
WorkDir: ".",
})
defer session.Terminate(ctx)
stream, _ := session.SendMessage(ctx, "Create hello.txt with 'Hello, World!'")
stream.Output(os.Stdout)Switching agents or models is a matter of changing session parameters. The surrounding application remains unchanged:
client.SessionRequest{Agent: "claudecode", Model: "sonnet"} // Claude Code
client.SessionRequest{Agent: "codex", Model: "gpt-5.5"} // CodexOrganizations often standardize on a Coding Agent while allowing flexibility in model selection.
Examples include:
- Claude Code with hosted Anthropic models
- Claude Code with private Anthropic-compatible deployments
- Claude Code with local inference infrastructure
Teams should be able to experiment with new models without rebuilding integrations.
Examples include:
- GPT-5 → GPT-OSS
- Gemini → Gemma
- Hosted → Local
- Cloud → Private
The cost of changing tools often exceeds the cost of adopting them.
Tern aims to reduce that cost by providing common interfaces and portable context.
A long-term goal of the project is to support movement between Coding Agents while preserving context and workflow continuity.
Claude Code
↓
Codex
↓
Gemini CLI
without requiring developers to start over.
We envision a future where:
- Context is portable
- Coding Agents are interchangeable
- Language Models are interchangeable
- Local and cloud deployments coexist
- Vendor lock-in becomes optional
Tern is being built to support that future.
Tern consists of three major components.
Coding Agent Web API.
CAWA defines a common interface for Coding Agents.
LLM Gateway Protocol.
LLMGP defines a common interface for Language Models and inference backends.
The Integration Layer connects Coding Agents and Language Models through a unified abstraction.
Additional architectural details will be documented separately.
- CAWA API specification v1
- Key Vault support
- Claude Code CLI adapter
- Codex CLI adapter
- Gemini CLI adapter (replaced by Antigravity SDK?)
- OpenAI LLM backend
- Anthropic LLM backend
- Google LLM backend
- Ollama LLM backend
- Wayfinder adapter (Embedded Original Coding Agent - Beta)
- Agent interaction protocol
- MCP support
- Tern CLI
- Tern SDK
- Session portability
- Context export/import
- Agent switching
- Model switching
- Multimodal LLM support (Vision & Voice)
- Context-preserving agent migration
- Live agent handoff
- Multi-agent orchestration
- Scale-out deployment
- Statistic / Prometheus
Tern is currently in the early design and implementation phase.
Contributors, reviewers, and early adopters are welcome.
| Requirement | Version | Notes |
|---|---|---|
| Go | 1.26 or later | go.mod specifies go 1.26.3 |
| Git | 2.x or later | Used for submodule checkout |
| Docker / Docker Compose | Docker 20.10+ / Compose v2+ | Only required for container deployment |
| Claude Code CLI | 2.1.x or later | Update with claude update |
| Codex CLI | Latest | Optional, for Codex agent support |
| OS | Windows / macOS / Linux | Cross-platform |
Note: On Windows, Git Bash is recommended. Build scripts are written in bash.
Important: Claude Code CLI v2.0.x ignores the
ANTHROPIC_BASE_URLenvironment variable, which prevents Gateway-proxied requests from working. Make sure to update to v2.1.x or later by runningclaude update.
You will also need API keys for at least one LLM provider (OpenAI, Anthropic, or Google).
git clone https://github.com/axsh/arctic-tern.git
cd arctic-tern
# Build all features and examples
./scripts/process/build.shBuilt binaries are placed in the bin/ directory:
| Binary | Description |
|---|---|
bin/tern |
Tern server (full-featured, production) |
bin/ternctl |
CLI client for interacting with a running tern server |
bin/vault-cli |
Key Vault management CLI |
bin/minimal-server |
Minimal server example |
bin/minimal-client |
Minimal client example |
Option A: OS Keyring (recommended for local development)
# Store an API key using the provider shorthand
# (--provider anthropic expands to vault key: providers/anthropic/default)
$ ./bin/vault-cli set --provider anthropic
Enter value: sk-ant-...
# For non-default key names, use --key with a full path
$ ./bin/vault-cli set --key providers/openai/team-a
# Check which providers have keys registered
$ ./bin/vault-cli statusOption B: Environment Variables (recommended for Docker / CI)
Set vault.backend: "env" in your config.yaml, then export API keys as environment variables.
Vault references in model_profiles.yaml are mapped to environment variable names automatically:
# vault://providers/openai/default -> TERN_VAULT_OPENAI_DEFAULT
# vault://providers/anthropic/primary -> TERN_VAULT_ANTHROPIC_PRIMARY
export TERN_VAULT_OPENAI_DEFAULT="sk-proj-your-openai-api-key"
export TERN_VAULT_ANTHROPIC_PRIMARY="sk-ant-your-anthropic-api-key"
$ ./bin/tern --config settings/demo/config.yamlThe mapping rule is: vault://providers/{provider}/{key} becomes TERN_VAULT_{PROVIDER}_{KEY} (uppercased, hyphens replaced with underscores).
Configuration files are located in the settings/ directory:
settings/example/-- Minimal configuration for getting startedsettings/demo/-- Full-featured configuration for development
Create a config.yaml:
llm_gateway:
port: 14000
model_profiles_path: "model_profiles.yaml"
vault:
backend: "keyring"
agent_service:
port: 3100
log:
level: "info"
outputs:
- type: "stdout"Create a model_profiles.yaml:
default_profile:
provider: anthropic
model: claude-sonnet-4-20250514
providers:
anthropic:
api_keys:
- name: default
secret: vault://providers/anthropic/default
models:
- name: claude-sonnet-4-20250514
- name: claude-opus-4-20250514
openai:
api_keys:
- name: default
secret: vault://providers/openai/default
models:
- name: gpt-4o
- name: gpt-5.5
ollama:
api_keys:
- name: default
models:
- name: qwen2.5-coder:7b
behavior:
tool_call_fallback: true
network_config:
base_url: "http://localhost:11434"In one terminal, start the tern server:
$ ./bin/tern --config ./settings/demo/config.yaml
tern server started and running...The server exposes:
- CAWA Agent Service on port
3100(configurable viaagent_service.port) - LLM Gateway on port
14000(configurable viallm_gateway.port)
Open another terminal and interact with the server:
# Check server health
$ ./bin/ternctl health
# List available agents and models
$ ./bin/ternctl agents
$ ./bin/ternctl models
# Run a coding task
$ ./bin/ternctl run \
--agent claudecode \
--prompt "Analyze the current directory structure and create a summary report in REPORT.md" \
--work-dir ./tmpWhen the task completes, ternctl outputs session details as JSON:
{
"agent_name": "claudecode",
"id": "a95db64cb646901efb395a18d817a37d",
"status": "completed",
"work_dir": "tmp"
}Use --resume with the session id from the previous output to continue the conversation:
$ ./bin/ternctl run \
--resume a95db64cb646901efb395a18d817a37d \
--prompt "Add a table of contents to REPORT.md"The agent resumes the previous session with full context of the prior conversation.
c := client.New("http://localhost:3100")
session, _ := c.CreateSession(ctx, client.SessionRequest{
Agent: "claudecode",
WorkDir: ".",
})
stream, _ := session.SendMessage(ctx, "Create hello.txt")
stream.Output(os.Stdout)tern/
features/ # Deployable applications
tern/ # Main server (CAWA + LLMGP)
ternctl/ # CLI client
vault-cli/ # Key Vault management CLI
shared/libs/go/ # Shared Go libraries
client/ # Go client library
tern/ # Server framework
codingagent/ # Coding Agent adapters
llmgateway/ # LLM Gateway providers
config/ # Configuration loading
vault/ # Secret management
settings/ # Configuration files
example/ # Minimal config for getting started
demo/ # Full-featured config for development
examples/ # Working examples
minimal-server/ # Minimal server setup
minimal-client/ # Minimal client usage
scripts/ # Build and test scripts
docs/ # Documentation resources
Detailed API documentation and protocol specifications are planned for a future release.
Tern is built around two core protocols:
-
CAWA (Coding Agent Web API) -- A REST/WebSocket API that abstracts Coding Agent lifecycle and communication. Agents register themselves via Go
init()imports, making it simple to add support for new agents. -
LLMGP (LLM Gateway Protocol) -- A reverse-proxy layer that routes LLM requests to configured providers (OpenAI, Anthropic, Google, Ollama). API keys are managed through a secure vault with support for keyring, environment variables, and encrypted storage.
Full protocol specifications are being developed and will be published as the project matures.
# Full build + unit tests
./scripts/process/build.sh
# Integration tests
./scripts/process/integration_test.sh
# Unit tests for shared libraries only
cd shared/libs/go
go test ./... -vIdeas, experiments, implementation feedback, and specification discussions are welcome.
Please open an issue to start a conversation.
Apache License 2.0. See LICENSE for details.
