Standalone Go binary that powers AI agent pods in AgentOps. Built on the Charm Fantasy SDK with a three-layer memory system backed by agentops-memory, Kubernetes-native agent orchestration, MCP tool integration, and a streaming protocol (FEP) for the AgentOps console.
- Architecture
- Features
- Modes
- Prerequisites
- Quick Start
- HTTP API
- Fantasy Event Protocol (FEP)
- Built-in Tools
- MCP Integration
- Memory
- Providers
- Agent Orchestration
- Tool Security
- Configuration
- Project Structure
- Building
- CI/CD
- Related Projects
- Contributing
- License
+-----------------------------------------------------+
| Agent Pod |
| |
| +------------------------------------------------+ |
| | agentops-runtime (this binary) | |
| | | |
| | Fantasy SDK Agent | |
| | +-- Provider (Anthropic/OpenAI/Google/...) | |
| | +-- Built-in tools (bash, read, edit, ...) | |
| | +-- OCI tools (kubectl, helm, ...) | |
| | +-- Permission gate | |
| | +-- Question gate | |
| | +-- Security hooks | |
| | | |
| | Memory | |
| | +-- Working memory (sliding window) | |
| | +-- Memory client (short + long term) | |
| | | |
| | HTTP Server (:4096) | |
| | +-- FEP SSE streaming | |
| +------------------------------------------------+ |
| |
| +-------------+ +-----------------------------+ |
| | MCP Gateway | | OCI Tool Sidecars | |
| | (optional) | | (stdio MCP servers) | |
| +-------------+ +-----------------------------+ |
+-----------------------------------------------------+
| |
| SSE/stdio | HTTP REST
v v
MCPServer CRs Memory Service
(e.g. kubernetes) (agentops-memory)
- Charm Fantasy SDK --- sole agent framework, with streaming callbacks, tool system, and multi-provider support
- Two execution modes --- long-running daemon (HTTP server) or one-shot task (Job)
- 14 tools --- 8 built-in + 3 memory + 2 orchestration + 1 interactive question
- Three-layer memory --- working memory (sliding window) + agentops-memory short-term (session summaries) + agentops-memory long-term (explicit saves via FTS5 BM25)
- MCP integration --- OCI-packaged tools via stdio and gateway MCP servers via SSE
- Multi-provider --- Anthropic, OpenAI, Google/Gemini, OpenRouter, and any OpenAI-compatible endpoint
- Automatic fallback --- cycles through fallback models on retryable errors (429, 5xx, rate limits)
- FEP streaming --- 21 event types over SSE for real-time console integration
- Permission gates --- user approval before tool execution with once/always/deny responses
- Interactive questions --- agents can ask structured questions with selectable options
- Tool security hooks --- blocked commands, allowed path restrictions, audit logging
- Agent orchestration --- agents can spawn and monitor sub-agent runs via Kubernetes CRDs
- Resource context injection --- per-turn context from git forges, K8s, and documentation
- AI-assisted memory extraction --- extracts structured observations from conversations
Long-running HTTP server for Deployment-backed agents. Serves the FEP streaming protocol on port 4096, maintains conversation state in working memory, persists knowledge to agentops-memory.
agentops-runtime daemonOne-shot execution for Job-backed agents. Reads AGENT_PROMPT from environment, runs the agent, writes JSON result to stdout, then exits.
AGENT_PROMPT="List all files in /workspace" agentops-runtime taskTask output format:
{
"output": "...",
"model": "anthropic/claude-sonnet-4-20250514",
"usage": { "input_tokens": 1234, "output_tokens": 567 }
}- Kubernetes cluster with agentops-core operator installed
- Agent CRD deployed (the operator generates
/etc/operator/config.jsonand manages the pod) - At least one LLM provider API key configured as a K8s Secret
- For development:
- Go 1.26
The runtime is not typically run directly --- the AgentOps operator creates and manages agent pods. But for development/testing:
# Build
CGO_ENABLED=0 go build -o agentops-runtime .
# Run (requires /etc/operator/config.json and API key env vars)
ANTHROPIC_API_KEY=sk-... AGENT_NAME=test-agent ./agentops-runtime daemonOr via Docker:
docker build -t agentops-runtime .
docker run -p 4096:4096 \
-e ANTHROPIC_API_KEY=sk-... \
-e AGENT_NAME=test-agent \
-v /path/to/config.json:/etc/operator/config.json:ro \
agentops-runtimeAll routes served on port 4096. The streaming endpoint speaks FEP over SSE.
| Method | Path | Description |
|---|---|---|
POST |
/prompt |
Non-streaming prompt. Returns {"output":"...","model":"..."}. Returns 429 if busy. |
POST |
/prompt/stream |
Streaming prompt via FEP SSE. Returns text/event-stream. |
POST |
/steer |
Inject steering message mid-execution ([STEER] prefix at next step). |
DELETE |
/abort |
Cancel the running generation. |
| Method | Path | Description |
|---|---|---|
POST |
/permission/{pid}/reply |
Reply to permission gate: {"response":"once|always|deny"} |
POST |
/question/{qid}/reply |
Reply to agent question: {"answers":[["label1"],["label2"]]} |
| Method | Path | Description |
|---|---|---|
GET |
/working-memory |
Get serialized sliding window messages. |
DELETE |
/working-memory |
Clear all messages and reset turn counter. |
PATCH |
/config/window-size |
Set window size: {"size": N} (min 2). |
POST |
/memory/extract |
AI-assisted memory extraction from working memory. |
| Method | Path | Description |
|---|---|---|
GET |
/healthz |
Health probe: {"status":"ok"} |
GET |
/status |
Runtime status: model, steps, busy, messages, window size, turns, memory enabled. |
Streaming prompt:
curl -N -X POST http://localhost:4096/prompt/stream \
-H "Content-Type: application/json" \
-d '{"prompt": "List files in the workspace", "context": []}'Permission reply:
curl -X POST http://localhost:4096/permission/abc-123/reply \
-H "Content-Type: application/json" \
-d '{"response": "once"}'FEP is the streaming protocol used to communicate with the AgentOps console over SSE. Each event is a JSON data: line:
data: {"type":"text_delta","timestamp":"2026-04-09T10:00:00Z","id":"abc","delta":"Hello"}
| Event | Key Fields | Description |
|---|---|---|
agent_start |
session_id, prompt | Execution started |
agent_finish |
session_id, total_usage, step_count, model | Execution complete |
agent_error |
session_id, error, retryable | Execution failed |
step_start |
step_number, session_id | Step began |
step_finish |
step_number, usage, finish_reason, tool_call_count | Step complete |
text_start |
id | Text block started |
text_delta |
id, delta | Incremental text token |
text_end |
id | Text block complete |
reasoning_start |
id | Reasoning/thinking started |
reasoning_delta |
id, delta | Incremental reasoning token |
reasoning_end |
id | Reasoning complete |
tool_input_start |
id, tool_name | Tool input streaming started |
tool_input_delta |
id, delta | Incremental tool input JSON |
tool_input_end |
id | Tool input complete |
tool_call |
id, tool_name, input, provider_executed | Tool invoked |
tool_result |
id, tool_name, output, is_error, metadata | Tool result (with UI hints) |
source |
source_type, url, title | Citation/source reference |
warnings |
warnings | Provider warnings |
stream_finish |
usage, finish_reason | Per-step stream complete |
permission_asked |
id, tool_name, input, description | Permission gate triggered |
question_asked |
id, questions | Agent asking user a question |
session_idle |
session_id | Agent idle, ready for next prompt |
Every step_finish and agent_finish event includes token usage:
{
"input_tokens": 1234,
"output_tokens": 567,
"total_tokens": 1801,
"reasoning_tokens": 200,
"cache_creation_tokens": 0,
"cache_read_tokens": 800
}Eight tools available out of the box, selectable via spec.builtinTools on the Agent CRD:
| Tool | Description | UI Hint | Details |
|---|---|---|---|
bash |
Execute shell commands | terminal |
120s default timeout, reports exit code, CWD, duration |
read |
Read file contents | code |
Supports offset (1-indexed) and limit for partial reads |
edit |
Exact find-and-replace | diff |
Requires unique match, rejects ambiguous (>1 occurrence) |
write |
Write/create files | file-created |
Auto-creates parent directories |
grep |
Regex search | search-results |
Uses ripgrep (rg --line-number --no-heading) |
ls |
List directory contents | file-tree |
Shows mode, size, name |
glob |
Find files by pattern | file-tree |
Recursive walk with filepath.Match |
fetch |
Fetch URL content | web-fetch |
Uses curl -sSL --max-time 30 |
All tools emit ui metadata hints consumed by the console to render specialized tool cards (terminal output, syntax-highlighted code, unified diffs, file trees, search results).
Two transport modes for external tools:
Tool packages pulled as OCI artifacts by the operator's crane init container. Each tool directory contains:
manifest.json--- name, command, transport typebin/--- the MCP server binary
Started as stdio subprocesses via mcp.CommandTransport. Tools discovered via session.ListTools() and prefixed as mcp_{server}_{tool}.
Shared MCP servers (AgentTool CRs with mcpServer source) accessed through the MCP gateway sidecar. Connected via SSE to http://localhost:{port}/sse on the per-server port.
MCP tools automatically receive UI hints for the console:
kubernetes-resourcesfor kubectl/kubernetes/k8s toolshelm-releasefor helm tools
Three-layer memory system replacing unbounded session replay:
| Layer | Storage | Survives Restart | Managed By |
|---|---|---|---|
| Working | Go memory (sliding window) | No | Automatic |
| Short-term | agentops-memory SQLite (PVC) | Yes | Automatic (session summaries) |
| Long-term | agentops-memory SQLite (PVC) | Yes | User + agent (explicit saves via mem_save) |
Fixed-size sliding window (default 20 messages). Trims at user-message boundaries to avoid orphaning tool-result messages that need their preceding assistant tool_use message. Dynamically adjustable at runtime via PATCH /config/window-size.
Persistent memory via agentops-memory's HTTP REST API:
| Operation | Endpoint | When |
|---|---|---|
| Create session | POST /sessions |
On runtime startup |
| Fetch context | GET /context?project=X&limit=N&query=PROMPT |
Before each prompt (relevance-ranked injection) |
| Save observation | POST /observations |
Via mem_save tool |
| Search memories | GET /search?q=X&project=Y |
Via mem_search tool |
| End session | POST /sessions/{id}/end |
On graceful shutdown |
Three tools added when agentops-memory is configured:
| Tool | Description |
|---|---|
mem_save |
Save an observation (type, title, content, tags) to long-term memory |
mem_search |
Full-text search across all memories (FTS5) |
mem_context |
Retrieve recent memory context (observations + session summaries) |
Via the Agent CRD spec.memory:
spec:
memory:
serverRef: agentops-memory # Service name or AgentTool CR
project: my-agent # Memory scope (defaults to agent name)
contextLimit: 5 # Max context items per turn
windowSize: 20 # Working memory message limit
autoSummarize: true # Auto-capture session summariesSupports any LLM provider through the Fantasy SDK provider system:
| Provider | Env Var | SDK Package |
|---|---|---|
anthropic |
ANTHROPIC_API_KEY |
fantasy/providers/anthropic |
openai |
OPENAI_API_KEY |
fantasy/providers/openai |
google / gemini |
GOOGLE_API_KEY |
fantasy/providers/google |
openrouter |
OPENROUTER_API_KEY |
fantasy/providers/openrouter |
| Custom | {NAME}_API_KEY + {NAME}_BASE_URL |
fantasy/providers/openaicompat |
Models are specified as provider/model-id (e.g., anthropic/claude-sonnet-4-20250514). If no provider prefix is given, primaryProvider from config is used.
When the primary model fails with a retryable error (HTTP 429, 500, 502, 503, rate limit, overloaded), the runtime automatically tries each model in fallbackModels in order.
Two built-in tools allow agents to delegate work to other agents:
| Tool | Description |
|---|---|
run_agent |
Creates an AgentRun CR to trigger another agent with a prompt |
get_agent_run |
Checks the status and output of a running/completed agent run |
Uses the Kubernetes dynamic client with in-cluster credentials. When running outside a cluster, stubs are provided that return informational messages.
AgentRun names follow the pattern {agent}-run-{unixMilli}.
Tools listed in spec.permissionTools require user approval before execution:
- Runtime emits
permission_askedFEP event with tool name, input, and description - Blocks on a Go channel (5-minute timeout)
- Console delivers reply via
POST /permission/{pid}/reply - Responses:
once(allow this call),always(permanently allow),deny(block) - Timeout defaults to
deny
spec.toolHooks provides three layers of protection:
| Hook | Description |
|---|---|
| blockedCommands | Reject bash commands containing these string patterns |
| allowedPaths | Restrict file tools to these path prefixes |
| auditTools | Log execution of these tools with structured audit entries |
Tools pass through multiple wrapping layers in order:
- Build --- built-in, OCI, gateway, memory, orchestration, question tools
- Security hooks --- blocked commands, allowed paths, audit logging
- Permission gates --- user approval for specified tools
The runtime reads /etc/operator/config.json, generated by the AgentOps operator from the Agent CRD spec. Example:
{
"runtime": "fantasy",
"primaryModel": "anthropic/claude-sonnet-4-20250514",
"primaryProvider": "anthropic",
"providers": [{ "name": "anthropic" }],
"fallbackModels": ["openai/gpt-4o"],
"systemPrompt": "You are a helpful assistant.",
"builtinTools": ["bash", "read", "edit", "write", "grep", "ls", "glob"],
"tools": [{ "name": "k8s-helper", "path": "/tools/k8s-helper" }],
"mcpServers": [{ "name": "gitlab-mcp", "port": 8080 }],
"toolHooks": {
"blockedCommands": ["rm -rf /"],
"allowedPaths": ["/workspace", "/data"],
"auditTools": ["bash"]
},
"permissionTools": ["bash"],
"enableQuestionTool": true,
"temperature": 0.3,
"maxOutputTokens": 8192,
"maxSteps": 50,
"memory": {
"serverURL": "http://agentops-memory.agents.svc:7437",
"project": "my-agent",
"contextLimit": 5,
"windowSize": 20,
"autoSummarize": true
}
}| Variable | Required | Description |
|---|---|---|
AGENT_NAME |
Yes | Agent identity, used for memory project scoping |
AGENT_NAMESPACE |
No | Kubernetes namespace for AgentRun CRs (default: default) |
AGENT_PROMPT |
Task only | The prompt for one-shot task mode |
ANTHROPIC_API_KEY |
Per provider | Anthropic API key |
OPENAI_API_KEY |
Per provider | OpenAI API key |
GOOGLE_API_KEY |
Per provider | Google/Gemini API key |
OPENROUTER_API_KEY |
Per provider | OpenRouter API key |
{NAME}_API_KEY |
Per provider | Custom provider API key |
{NAME}_BASE_URL |
Custom only | OpenAI-compatible base URL |
| Constant | Value | Description |
|---|---|---|
| Config path | /etc/operator/config.json |
Operator-generated config |
| HTTP port | 4096 |
Server listen port |
| Default window size | 20 |
Working memory messages |
| Default context limit | 5 |
Memory context items per turn |
| Permission timeout | 5 min |
Time to wait for user approval |
| Question timeout | 10 min |
Time to wait for user answer |
| Bash timeout | 120s |
Default command timeout |
| Fetch timeout | 30s |
curl max-time |
agentops-runtime/
main.go # Entry point, HTTP server, daemon/task modes, agent bundle,
# fallback logic, orchestration tools, streaming handler
config.go # Config types parsed from /etc/operator/config.json
fep.go # FEP SSE emitter — 21 event type methods
tools.go # 8 built-in tools (bash, read, edit, write, grep, ls, glob, fetch)
memory.go # Working memory (sliding window) + memory service client + 3 memory tools
mcp.go # MCP tool loading — OCI (stdio) + gateway (SSE) + tool adapter
permission.go # Permission gate — user approval before tool execution
question.go # Interactive question tool
hooks.go # Tool security hooks — blocked commands, allowed paths, audit
provider.go # LLM provider resolution (Anthropic, OpenAI, Google, etc.)
k8s.go # Kubernetes dynamic client for AgentRun CRs
resources.go # Per-turn resource context formatting
Dockerfile # Multi-stage build: golang:1.26 -> alpine:3.21
go.mod # 5 direct dependencies (Fantasy SDK, MCP SDK, K8s, UUID)
Single main package --- 12 source files, ~3,900 lines of Go.
CGO_ENABLED=0 go build -o agentops-runtime .docker build -t agentops-runtime .The image is based on alpine:3.21 with bash, curl, and ripgrep installed for the built-in tools. Runs as non-root user 1000:1000. Creates /data/sessions, /data/repos, /data/scratch directories.
ghcr.io/samyn92/agentops-runtime-fantasy:<version>
ghcr.io/samyn92/agentops-runtime-fantasy:latest
Runs on push to main and pull requests:
CGO_ENABLED=0 go build--- compile checkgo vet ./...--- static analysis
Triggered by v* tags:
- Build and vet (same as CI)
- Build and push Docker image to GHCR with version tag +
latest - Create GitHub Release with auto-generated notes
| Repository | Description |
|---|---|
| agentops-core | Kubernetes operator (CRDs, controllers, webhooks) |
| agentops-console | Web console (Go BFF + SolidJS PWA) |
| agent-channels | Channel bridge images (Telegram, Slack, GitLab, etc.) |
| agent-tools | OCI tool/agent packaging CLI + tool packages |
| agentops-memory | Purpose-built memory service (SQLite + FTS5 BM25) |
| Charm Fantasy SDK | AI agent framework |
Contributions are welcome. To get started:
- Fork the repository
- Create a feature branch (
git checkout -b feature/my-feature) - Install Go 1.26 and run
go mod download - Make your changes
- Ensure the build passes (
CGO_ENABLED=0 go build -o agentops-runtime .) - Run static analysis (
go vet ./...) - Commit your changes and open a Pull Request
Apache 2.0