Documentation
¶
Overview ¶
Package odek is a minimal Go agent loop runtime.
odek implements the ReAct (Reasoning + Acting) pattern — the "think, therefore act" loop that powers autonomous AI agents. It is not a framework or an SDK. It is a runtime: one loop, one binary, minimal deps.
Design ¶
- Minimal external dependencies. stdlib + a few focused packages.
- Session isolation via Docker containers (--sandbox).
- LLM-agnostic. Any OpenAI-compatible endpoint works.
- Tool-first. Tools are the only extension point.
Security ¶
When running with --sandbox, each session executes in a fresh Docker container. The container has no network access, no host mounts beyond the working directory, and is destroyed on exit. The agent can never access files outside its working directory.
Index ¶
- Constants
- Variables
- func BuildRuntimeContext(platform string) string
- func LoadProjectFile() string
- func ProfileLabel(model string) string
- type Agent
- func (a *Agent) Close() error
- func (a *Agent) Memory() *memory.MemoryManager
- func (a *Agent) Run(ctx context.Context, task string) (string, error)
- func (a *Agent) RunWithMessages(ctx context.Context, messages []llm.Message) (string, []llm.Message, error)
- func (a *Agent) SkillManager() *skills.SkillManager
- func (a *Agent) SwitchModel(model string)
- func (a *Agent) SwitchThinking(thinking string)
- func (a *Agent) TotalCacheCreationTokens() int
- func (a *Agent) TotalCacheReadTokens() int
- func (a *Agent) TotalCachedTokens() int
- func (a *Agent) TotalInputTokens() int
- func (a *Agent) TotalOutputTokens() int
- type Config
- type ModelProfile
- type Tool
Constants ¶
const ProjectFileName = "AGENTS.md"
ProjectFileName is the name of the project-level instructions file that odek automatically loads from the working directory.
Variables ¶
var KnownProfiles = []struct { Prefix string Profile ModelProfile }{ { Prefix: "deepseek-v4-pro", Profile: ModelProfile{ Label: "DeepSeek v4 Pro", DefaultThinking: "enabled", Timeout: 180, MaxContext: 1_000_000, }, }, { Prefix: "deepseek-v4-flash", Profile: ModelProfile{ Label: "DeepSeek v4 Flash", DefaultThinking: "", Timeout: 90, MaxContext: 131_072, }, }, { Prefix: "deepseek-", Profile: ModelProfile{ Label: "DeepSeek (generic)", MaxContext: 131_072, }, }, }
KnownProfiles lists all built-in model profiles. Each entry is matched by longest prefix — "deepseek-v4-flash" matches before "deepseek-" would. Add new profiles here; the rest of odek consumes them automatically.
Functions ¶
func BuildRuntimeContext ¶
BuildRuntimeContext returns a system prompt header with OS, hostname, working directory, current date/time, and platform-specific formatting rules for the given transport (platform). platform can be "telegram", "terminal", "web", or empty for generic.
This context eliminates the need for the agent to run shell commands to discover its own environment — the most common waste of tokens in CLI agent usage.
func LoadProjectFile ¶
func LoadProjectFile() string
LoadProjectFile reads ProjectFileName from the current working directory. Returns the file content (trimmed) if it exists and is readable. Returns empty string if the file doesn't exist or can't be read. Checks for symlinks to prevent following attacker-controlled paths. The content is intended to be appended to the system message with a clear header — use it for project conventions, architecture notes, etc.
func ProfileLabel ¶
ProfileLabel returns the human-readable label for a model, or the model name itself if no profile matches. Used in CLI headers and status output.
Types ¶
type Agent ¶
type Agent struct {
// contains filtered or unexported fields
}
Agent is the agent loop runtime.
func New ¶
New creates a new Agent with the given configuration.
If Config.SandboxCleanup is set, the cleanup function is called when Close() is invoked. The caller is responsible for creating the sandbox container and wiring up tool executables to use it before calling New().
func (*Agent) Close ¶
Close cleans up resources. If a sandbox container was created, it is destroyed. Always call Close() when done with the agent.
func (*Agent) Memory ¶
func (a *Agent) Memory() *memory.MemoryManager
Memory returns the agent's memory manager. Used by the CLI layer to append buffer entries after each turn and signal session end. Returns nil if memory is disabled.
func (*Agent) RunWithMessages ¶
func (a *Agent) RunWithMessages(ctx context.Context, messages []llm.Message) (string, []llm.Message, error)
RunWithMessages executes the agent loop starting from a pre-built message history. Use this for multi-turn conversations where the full conversation context (system prompt, prior turns) has been loaded from a session file and the new user message appended.
Returns the final answer plus the complete updated message history. The caller should persist the history (e.g. to a session file) so the conversation can be continued in a future call.
func (*Agent) SkillManager ¶
func (a *Agent) SkillManager() *skills.SkillManager
SkillManager returns the agent's skill manager. Used by the CLI, WebUI, and Telegram layers to run learning heuristics after agent completion. Returns nil if skills are disabled.
func (*Agent) SwitchModel ¶
SwitchModel updates the LLM model used by this agent at runtime. The model string must be a valid OpenAI-compatible model identifier. This is safe to call between RunWithMessages calls to switch models mid-session. Empty strings are silently ignored.
func (*Agent) SwitchThinking ¶ added in v1.0.0
SwitchThinking updates the reasoning/thinking mode used by this agent at runtime. Accepts the same values as Config.Thinking: "enabled", "disabled", "low", "medium", "high", or "" (provider default / off). Safe to call between RunWithMessages calls to toggle thinking per-query.
func (*Agent) TotalCacheCreationTokens ¶
TotalCacheCreationTokens returns the cumulative Anthropic cache creation tokens across all iterations of the most recent run.
func (*Agent) TotalCacheReadTokens ¶
TotalCacheReadTokens returns the cumulative Anthropic cache read tokens across all iterations of the most recent run.
func (*Agent) TotalCachedTokens ¶
TotalCachedTokens returns the cumulative OpenAI cached prompt tokens across all iterations of the most recent run.
func (*Agent) TotalInputTokens ¶
TotalInputTokens returns the cumulative prompt tokens consumed across all iterations of the most recent RunWithMessages call.
func (*Agent) TotalOutputTokens ¶
TotalOutputTokens returns the cumulative completion tokens generated across all iterations of the most recent RunWithMessages call.
type Config ¶
type Config struct {
// Model is the LLM model identifier (e.g., "deepseek-v4-flash").
Model string
// BaseURL is the OpenAI-compatible API endpoint.
// Default: "https://api.deepseek.com/v1"
BaseURL string
// APIKey authenticates with the LLM provider.
// Falls back to DEEPSEEK_API_KEY, then OPENAI_API_KEY env vars.
APIKey string
// Thinking controls the model's reasoning depth. Provider-specific:
//
// Deepseek: "enabled" or "disabled" → {"type": "enabled"}
// OpenAI o-series: "low", "medium", "high" → {"reasoning_effort": "low"}
//
// When empty, the model's profile default is used. If the profile also
// has no default, the field is not sent (provider default behavior).
Thinking string
// Temperature controls LLM output randomness (0.0–2.0).
// Negative = omit from request (use provider default).
// 0.0 = deterministic, 1.0 = creative. Default: 0.0 for benchmark
// stability; set to -1 to use provider defaults.
Temperature float64
// ThinkingBudget is the maximum thinking tokens for Anthropic extended thinking (default 5000).
ThinkingBudget int
// Tools available to the agent.
Tools []Tool
// MaxIterations caps the number of think→act cycles (default: 90).
MaxIterations int
// SystemMessage is the system prompt injected at the start of every run.
// Runtime context (OS, hostname, cwd, date, platform) is automatically
// prepended to this message before it reaches the LLM.
// If AGENTS.md exists in the working directory, its content is appended
// automatically. Set NoProjectFile to true to skip this.
SystemMessage string
// RuntimeContext, when set, prepends environment awareness to the system
// message: OS, hostname, working directory, current date/time, and
// platform-specific formatting rules. Each entry point (CLI, Telegram,
// WebUI) sets this automatically. When empty, BuildRuntimeContext("")
// provides generic terminal context.
RuntimeContext string
// NoProjectFile disables automatic loading of AGENTS.md from the
// working directory. By default, odek reads AGENTS.md and appends
// its content to the system message with a "Project Instructions" header.
NoProjectFile bool
// SandboxCleanup, if set, is called by Agent.Close() to destroy the
// Docker sandbox container. Set by the CLI when --sandbox is active.
// Programmatic API users can set this to their own cleanup logic
// (e.g., remove a container, delete a VM, tear down a network).
// When nil, Close() is a no-op.
SandboxCleanup func() error
// Renderer, if set, produces colored terminal output for each phase
// of the agent loop. When nil, the agent runs silently (programmatic API).
Renderer *render.Renderer
// ToolEventHandler, if set, is invoked for each tool call and result
// during the agent loop. Fires "tool_call" before and "tool_result"
// after each tool invocation. Used by the WebUI for live streaming.
ToolEventHandler func(event string, name string, data string)
// InteractionMode controls tool-call rendering: "engaging" (default), "enhance", "verbose", or "off".
InteractionMode string
// IterationCallback, if set, is invoked after each iteration of the
// agent loop with progress info (turn number, tokens, tools called).
// Used by the Telegram handler for periodic progress updates.
IterationCallback loop.IterationCallback
// Skills configures the skill system. When nil, skills are disabled.
Skills *skills.SkillsConfig
// SkillManager holds the loaded skill state. Passed by the CLI layer;
// when nil, New() auto-loads from default directories.
SkillManager *skills.SkillManager
// MemoryDir sets the directory for persistent memory storage.
// Default: ~/.odek/memory/
MemoryDir string
// MemoryConfig controls the memory system (facts, buffer, episodes).
// Default: memory.DefaultMemoryConfig()
MemoryConfig memory.MemoryConfig
// PromptCaching enables prompt caching markers for supported providers.
// When enabled (default: false), the system prompt and first user message
// are annotated with cache_control markers, and Anthropic-style system
// blocks are used. Supported by:
// - Anthropic (explicit cache_control markers)
// - DeepSeek (automatic — prefix stability helps)
// - OpenAI (automatic — prefix stability helps)
//
// When disabled (default), no cache markers are sent and the system
// prompt stays in the messages array for maximum provider compatibility.
// Enable this when using Anthropic models to get ~90% cost reduction
// on cached tokens and ~60-80% TTFT latency reduction.
PromptCaching bool
// MaxToolParallel controls how many tool calls run concurrently per
// agent iteration. 0 = use default (4). Models that emit multiple
// parallel tool calls benefit from concurrent execution of I/O-bound
// tools like read_file, search_files, and web_search.
MaxToolParallel int
// SkillEventHandler, if set, is invoked when a skill lifecycle event
// occurs (loaded, autoloaded, saved, deleted, etc.). Used by WebUI
// (WebSocket streaming) and Telegram (inline messages).
SkillEventHandler func(event skills.SkillEvent)
// Approver gates dangerous tool operations. When set and the LLM returns
// multiple tool calls in one iteration, a single batch approval prompt
// is shown instead of N individual prompts. If denied, no tools run
// for that iteration. If approved, individual tool-level PromptCommand
// calls are bypassed via SetTrustAll.
Approver danger.Approver
// DangerousConfig holds the user's risk class configuration (Allow/Deny/
// Prompt per risk class). Used by the batch gate to decide whether a
// tool call needs approval before showing the prompt. When nil, the
// batch gate plays safe and shows the prompt for any classified tool.
DangerousConfig *danger.DangerousConfig
}
Config configures an Agent instance.
type ModelProfile ¶
type ModelProfile struct {
// Label is a human-readable name for the model family.
Label string
// DefaultThinking is the thinking value applied when Config.Thinking
// is empty. Empty string means don't send the field (provider default).
DefaultThinking string
// Timeout is the default request timeout in seconds.
// Zero means use the global default (120s). Increased for
// models that take longer to reason (e.g. deepseek-v4-pro).
Timeout int
// MaxContext is the model's maximum context window in tokens.
// The loop engine automatically trims conversation history when
// estimated tokens approach this limit. Zero means no limit
// enforcement (unknown or effectively unlimited models).
MaxContext int
}
ModelProfile holds per-model defaults applied when the user hasn't explicitly provided a value. Zero values leave the system default.
func LookupProfile ¶
func LookupProfile(model string) *ModelProfile
LookupProfile returns the best-matching ModelProfile for a model name, or nil if no profile matches. Matching uses longest prefix — a model named "deepseek-v4-flash-custom" would match "deepseek-v4-flash".
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
odek
command
|
|
|
internal
|
|
|
config
Package config loads and merges odek configuration from multiple sources.
|
Package config loads and merges odek configuration from multiple sources. |
|
danger
Package danger classifies shell commands by risk level and provides a configurable approval system for dangerous operations.
|
Package danger classifies shell commands by risk level and provides a configurable approval system for dangerous operations. |
|
llm
Package llm provides an OpenAI-compatible HTTP client using only stdlib.
|
Package llm provides an OpenAI-compatible HTTP client using only stdlib. |
|
loop
Package loop implements the ReAct (Reasoning + Acting) agent loop.
|
Package loop implements the ReAct (Reasoning + Acting) agent loop. |
|
mcp
Package mcp implements a Model Context Protocol server over stdio.
|
Package mcp implements a Model Context Protocol server over stdio. |
|
mcpclient
Package mcpclient implements an MCP client that connects to external MCP servers over stdio.
|
Package mcpclient implements an MCP client that connects to external MCP servers over stdio. |
|
memory
Package memory provides persistent, agent-managed memory across sessions.
|
Package memory provides persistent, agent-managed memory across sessions. |
|
narrate
Package narrate produces human-friendly, emoji-rich transition messages describing what the agent is doing.
|
Package narrate produces human-friendly, emoji-rich transition messages describing what the agent is doing. |
|
redact
Package redact provides secret detection and redaction for odek output.
|
Package redact provides secret detection and redaction for odek output. |
|
render
Package render provides emoji-driven terminal rendering for the odek agent loop.
|
Package render provides emoji-driven terminal rendering for the odek agent loop. |
|
resource
Package resource implements @-prefixed resource discovery and inline resolution.
|
Package resource implements @-prefixed resource discovery and inline resolution. |
|
sandbox
Package sandbox builds and operates the Docker container that isolates the agent's shell and file-tool execution from the host.
|
Package sandbox builds and operates the Docker container that isolates the agent's shell and file-tool execution from the host. |
|
session
Package session persists agent conversation history across runs.
|
Package session persists agent conversation history across runs. |
|
skills
Package skills — advanced skill matching using scoring-based approach.
|
Package skills — advanced skill matching using scoring-based approach. |
|
telegram
Package telegram provides Telegram bot integration.
|
Package telegram provides Telegram bot integration. |
|
tool
Package tool provides the clarify tool — ask the user a question and wait for a response.
|
Package tool provides the clarify tool — ask the user a question and wait for a response. |
|
transport
Package transport provides tuned HTTP transports for odek's API clients.
|
Package transport provides tuned HTTP transports for odek's API clients. |
|
ws
Package ws provides WebSocket constants used by cmd/odek/serve.go.
|
Package ws provides WebSocket constants used by cmd/odek/serve.go. |