apogee

package module
v1.7.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 21, 2026 License: MIT Imports: 3 Imported by: 0

README

Apogee

A terminal-based coding agent built for small, locally-run LLMs (~4B–35B).

Apogee is a single, cross-platform tool that drops into any IDE's integrated terminal — or any standalone terminal — on Windows, macOS, and Linux. It runs against any OpenAI-compatible LLM server (llama.cpp, Ollama, LM Studio, vLLM), so your code stays on your machine, no API key is required for local models, and you get a full agentic tool-use loop with sensible guardrails.

What this repo is

Apogee brings together two things most coding agents keep separate:

  • A complete agentic coding assistant — the agent loop, with provider abstraction, a ~21-tool suite (file ops, grep/glob, git, terminal, web, sub-agents, showing you a finished document), an MCP client, sessions, four autonomy modes (Plan / Ask-Before / Allow-Edits / Auto), and security guardrails.
  • Self-regulating mechanisms for small models — features that make small, locally-run models measurably better at sustained agentic coding: context compression, tool-call validation + auto-retry, behavioural nudges, and a cross-session learning Library. Each is gated so it only fires when the model needs it.

These mechanisms run inside the agent loop, where they have the most leverage — not in a separate proxy. And nothing is carried forward on faith: every mechanism is measured and A/B-tested against real local models with an eval/simulation harness before it earns a place in the loop.

Why Go

Portability is the primary goal. Go cross-compiles to a single static binary with no runtime — the gold standard for "drop into any terminal on any OS." It also lets us use one language for both the agent and the bench that evaluates it. The TUI is built on the Charm stack (Bubble Tea + Lipgloss + Bubbles) with Cobra for the CLI.

Status

v1.7.0 shipped (2026-07-21). The embeddable agent core is stable — the public Go API follows semver from v1.0.0 — with the full tool suite, MCP client, sub-agents, and OS-confined Auto mode (Linux landlock / macOS seatbelt; Windows confinement comes in a later phase, so Auto there still runs — it just falls back to asking before each shell/subprocess call instead of confining it). The same fallback applies wherever the facility is missing rather than unimplemented — most containers, where landlock reports ENOSYS whatever the kernel version. Apogee says so at startup and offers /confine as the way out (see Auto mode's blast radius). Current work is per-model bench validation of the mechanism catalogue: the full catalogue is ported, and the first Validated set (gemma-4-E4B) ships with the binary. See docs/plans/ and the CHANGELOG for what's next.

Key capabilities

  • Model-agnostic, local-first — any OpenAI-compatible endpoint; zero data leaves your machine with a local model.
  • Agentic tool use — multi-step loop with file edits, shell, search, git, web, and sub-agents.
  • Deliverables you actually seepresent_document ends a report-producing task by showing the file: opened on your desktop when apogee runs locally, served over a one-off link when it runs on a remote box, and always printed as a clickable path in the transcript. See Showing a finished document.
  • Four autonomy modes — Plan (read-only), Ask-Before (writes need approval), Allow-Edits (workspace-scoped writes auto-approved), Auto (autonomous, confined at the OS level via Linux landlock / macOS seatbelt; where the OS cannot fence a command, Auto asks before it rather than running it unbounded).
  • MCP support — connect external tool servers over stdio / SSE / streamable-http.
  • Model profiles — adapt to models that don't speak native tool-calls: the tool menu and format instructions are injected as text on the request side, markdown-fenced or custom-regex tool calls are parsed back out of the reply, and inline thinking / harmony channels are stripped — all driven by a per-model profile (native models stay byte-identical on the wire).
  • Small-model mechanisms — context compaction is built in; tool-call validation/auto-retry, syntax + autofix, behavioural nudges, and the cross-session Library are all catalogued — each default-off, gated so it only fires when the model needs it, and enabled per model via Validated sets backed by bench evidence.
  • Validated, not assumed — every mechanism is A/B-tested against real local models via an eval/simulation bench (which imports Apogee as a Go library and drives the real loop in-process) before it earns a place in the loop.

In-chat commands

Typing / in the prompt opens the command menu; @ completes a workspace file path, and an @path in a message hands that file to the model.

Command Does
/clear (or /new) Reset the model's memory of this session
/compact Summarise the conversation to reclaim context
/continue Ask the model to keep going
/confine Report or change Auto's blast radius — see below
/skill Attach a skill to your next message

Configuration

Settings resolve by precedence, highest first: a command-line flag overrides an APOGEE_* environment variable, which overrides ~/.apogee/config.yaml, which overrides the built-in default. A fully-commented starter config.yaml is written to ~/.apogee on first run (your edits are never overwritten). Some settings are file-only (no flag or env) — the model profile, MCP servers, web-search endpoint, and the small-model mechanisms.

Catalogued mechanisms are opt-in by canonical ID. Every mechanism ships off until its A/B bench run proves it a win, so enabling one is a deliberate config choice:

# ~/.apogee/config.yaml
mechanisms:
  validate: true   # tool-call validation + auto-retry
  syntax: true     # write-content syntax check
  autofix: true    # formatter pass on tool-call payloads

An unknown ID is a startup error that lists the IDs this build knows; --bypass still wins (an enabled non-off-ramp mechanism does not fire under bypass). The same catalogued mechanisms are enabled by ID from the Go API through Config.EnableMechanisms (with apogee.CataloguedMechanisms() to enumerate them), so a library embedder arms the identical stack without the config file. The catalogue fills in as the port waves land — see docs/design/mechanism-catalogue.md.

Automatic context Compaction keeps a long session from overflowing the model's window: when the conversation history outgrows its budgeted share, apogee folds the older turns into a summary (the same reducer as the /compact command) before the next request. The same fold is also apogee's overflow recovery: when a request does not fit the window after all — or the estimate already says it cannot — the history is folded mid-task and the turn is re-sent once, so a long task survives instead of dying on "context window exceeded". It is structural and load-bearing — it stays on even under --bypass — so it is on by default; set auto-compact: false (a file-only key) to manage the window yourself with /compact instead, which opts out of the recovery too.

The context window these budgets are measured against is discovered from the server at startup — for a pinned model: too. Set context-window: (a file-only key, in tokens) only when your server does not advertise a window, or to start a pinned model offline; with no window known, the Budget and automatic compaction stay inactive and apogee says so once at startup.

Showing a finished document

When the model finishes a deliverable — a report, a review, an HTML summary — it calls present_document and hands apogee nothing but the path. Apogee decides how to show it; the model never reasons about your platform. Whatever it decides, the document's workspace-relative path is always printed in the transcript, which most terminals (Zed, VS Code, iTerm2, WezTerm, kitty) make cmd/ctrl+clickable. Above that baseline: on your own desktop the file is opened in its associated application (HTML in your default browser); over SSH — a devbox, a VM, a container — browser-renderable documents (.html, .htm, .svg, .pdf) are served from a small built-in server and the URL is printed beside the path, so one cmd+click opens the document in the browser on your machine. Apogee never auto-opens on the remote box: there is no display there to open into. If a rung fails, the transcript says so and falls back to the path.

The built-in server hands out one random-token URL per presented document — no directory listing, no other file reachable — re-reads the file per request, starts only when a document is actually served, and stops when apogee exits. Four file-only keys tune all of this:

# ~/.apogee/config.yaml
present:
  auto-open: true        # open documents on a LOCAL desktop run; false = only print the path
  command: "zed {path}"  # open with THIS application instead of the OS default
  port: 0                # the built-in server's port; 0 (default) picks a free one per session
  host: ""               # address the printed URL advertises; empty = detected

host is a fallback, not an override: over SSH the address you connected to this box on is used, because it is known-routable. If a printed URL is unreachable on macOS Sequoia or later, the first browser connection to a local-network address needs Local Network permission — Chrome fails with a generic "this site can't be reached" until you allow it in System Settings → Privacy & Security → Local Network, while Safari tends to work straight away. The path line works regardless.

Auto mode's blast radius

Auto is the one unsupervised mode, so it is fenced: filesystem writes are confined to the workspace at the OS level, the network is open, and MCP still asks. Where the OS cannot fence a command — Windows today, and most containers, where landlock reports ENOSYS regardless of kernel version — Auto keeps the promise the honest way and asks before each shell call instead of running it unbounded ("confine if you can, gate if you can't"). That is not a fault, so Apogee says so at startup rather than letting Auto look broken.

If the machine is disposable and you would rather have Auto unfenced there, /confine is the route. /confine (or /confine status) reports the backend, what it can actually enforce here, this host's id, and the effective setting. /confine off runs Auto unconfined for this session and writes nothing; /confine off --save also records this machine in ~/.apogee/config.yaml, comments and formatting intact:

# ~/.apogee/config.yaml
unconfined-hosts:
  - id: "devbox-a1b2c3"                # this machine's id — /confine reports it
    acknowledged: "2026-07-21"
    note: "disposable container, landlock unavailable"

The acknowledgement is host-scoped on purpose: "this machine is disposable" is a claim about one machine, so it must not travel with your config file onto a laptop. The id is a safety interlock, not authentication — it fails closed, so an unrecognised machine is simply confined again. Delete the entry to re-confine a host; /confine on does the same for the running session.

confine-to-workspace: false remains the global blanket loosen and still means every host. Both keys are global-config-file-only — no flag, no environment variable, and no project config — because editing that file is the deliberate acknowledgement, and a repo you cloned must never be able to make that claim for you.

Building from source

Prerequisites: Go 1.26+ (the toolchain version pinned in go.mod).

git clone https://github.com/airiclenz/apogee.git
cd apogee
make build      # compiles ./apogee
./apogee --help

A Makefile wraps the common Go invocations:

Command Does
make build Compile the binary to ./apogee
make run ARGS="--help" Build-and-run, passing flags via ARGS
make test Run the test suite with the race detector
make cross Cross-build all six release targets (Linux/macOS/Windows × amd64/arm64)
make check The full acceptance gate — gofmt, vet, build, race tests, cross-build
make help List every target

Prefer the raw toolchain? go build -o apogee ./cmd/apogee does the same thing — the Makefile just gives the common commands one-word names.

Note: launch the TUI with apogee --endpoint <openai-compatible-url> --model <name> to hold a real coding conversation with a local model. All four autonomy modes, the full tool suite, MCP, sub-agents, and skills are live; Auto mode runs fully unattended where OS confinement is actually available (Linux landlock and macOS seatbelt today — Windows lands in a later phase, and a container may have neither), and where it is not, Auto gates each shell/subprocess call through approval and tells you why. See Auto mode's blast radius.

License

MIT — see LICENSE.

Documentation

Overview

Package apogee is the public, embeddable surface of the Apogee coding agent.

Apogee is a terminal coding agent for small local LLMs that owns the full agentic loop — build request, call the Upstream, parse the response, dispatch tools, apply Mechanisms — and ships as both a product (the cmd/apogee TUI/CLI) and this reusable library. The TUI, the optional `apogee headless` CLI, and the external bench (apogee-sim) are all consumers of this one package over the same engine. Everything not in this package (and its sibling public subpackages) is internal and carries no stability promise.

Layout (ADR 0010): this package is a THIN FACADE. The public types, interfaces, enums, and sentinel errors live in internal/domain (the ubiquitous language as Go); the engine lives in internal/agent; the provider seam lives in internal/provider. The root re-exports the public surface as type aliases, re-exported consts/errors, and forwarding constructors, and holds no engine logic. The invariant "internal/* never imports root" makes the dependency graph flow strictly downward toward internal/domain. example_test.go is a compile-time completeness guard: it names the full public surface so a forgotten alias fails the build.

It is grounded in:

ADR 0001  embeddable, steppable, no ambient state; snapshot/resume + hygiene
          (forking is the bench's, composed from these primitives — not exposed)
ADR 0002  Tools are an open extension point; the Mechanism catalogue is curated
ADR 0003  Mechanisms are a constraint-declared registry → deterministic total order
ADR 0004  Auto mode requires Confinement, reported as a capability matrix
ADR 0005  sub-agent privileges are always ≤ the parent's
ADR 0006  Bypass mode — the honest "Mechanisms-off" floor
ADR 0007  Step / Turn / quiescent boundary; cancellation; recover-at-boundary
ADR 0008  Tools are stateless across Turns; external effects are non-forkable
ADR 0010  package layout: a domain core, an engine, and this thin root facade

Stability: v0.x, no stability promise through Phase 3; v1.0.0 is cut at the end of Phase 3. Events and hook points are additively extensible — a new variant is a minor bump (so consumers must treat the Event set and enums as open).

Example (CataloguedMechanisms)

Example_cataloguedMechanisms plans a leave-one-out arm from CataloguedMechanisms() — the bench's idiom: to leave a Mechanism out, also drop every Mechanism that Requires it, so no half-armed stack reaches New (which would refuse with ErrMissingRequirement). Dropping tool_result_cap therefore also drops guided_decomposition, which Requires it.

package main

import (
	"fmt"
	"slices"

	"github.com/airiclenz/apogee"
)

func main() {
	const leaveOut = apogee.MechanismID("tool_result_cap")

	var arm []apogee.MechanismID
	for _, d := range apogee.CataloguedMechanisms() {
		if d.ID == leaveOut || slices.Contains(d.Requires, leaveOut) {
			continue // the left-out Mechanism, and any stack that Requires it
		}
		arm = append(arm, d.ID)
	}

	fmt.Println("left out:", leaveOut)
	fmt.Println("guided_decomposition still armed:", slices.Contains(arm, "guided_decomposition"))
}
Output:
left out: tool_result_cap
guided_decomposition still armed: false
Example (EnableMechanismStack)

Example_enableMechanismStack arms the guided_decomposition + tool_result_cap stack by ID through Config.EnableMechanisms. guided_decomposition Requires tool_result_cap (ADR 0014), so both must be enabled together — enabling one alone fails New with ErrMissingRequirement.

package main

import (
	"fmt"

	"github.com/airiclenz/apogee"
)

// discardSink is a no-op EventSink so the Examples construct an Agent hermetically — construction
// emits nothing and never dials the Endpoint.
type discardSink struct{}

func (discardSink) Emit(apogee.Event) {}

func main() {
	cfg := apogee.Config{
		Endpoint:         "http://localhost:11434",
		Model:            "local-model",
		Events:           discardSink{},
		EnableMechanisms: []apogee.MechanismID{"guided_decomposition", "tool_result_cap"},
	}
	ag, err := apogee.New(cfg)
	if err != nil {
		fmt.Println("construct:", err)
		return
	}
	defer ag.Close()

	fmt.Println("armed:", cfg.EnableMechanisms)
}
Output:
armed: [guided_decomposition tool_result_cap]

Index

Examples

Constants

View Source
const (
	FormatNative         = domain.FormatNative
	FormatMarkdownFenced = domain.FormatMarkdownFenced
	FormatCustomRegex    = domain.FormatCustomRegex
)
View Source
const (
	ThinkingNone      = domain.ThinkingNone
	ThinkingDelimited = domain.ThinkingDelimited
	ThinkingHarmony   = domain.ThinkingHarmony
)
View Source
const (
	ModePlan       = domain.ModePlan
	ModeAskBefore  = domain.ModeAskBefore
	ModeAllowEdits = domain.ModeAllowEdits
	ModeAuto       = domain.ModeAuto
)
View Source
const (
	StatusTurnComplete     = domain.StatusTurnComplete
	StatusExchangeComplete = domain.StatusExchangeComplete
	StatusCancelled        = domain.StatusCancelled
)
View Source
const (
	ApprovalAllow           = domain.ApprovalAllow
	ApprovalDeny            = domain.ApprovalDeny
	ApprovalAllowForSession = domain.ApprovalAllowForSession
)
View Source
const (
	PresentOpened = domain.PresentOpened
	PresentServed = domain.PresentServed
	PresentShown  = domain.PresentShown
)
View Source
const (
	EffectNetwork = domain.EffectNetwork
	EffectMCP     = domain.EffectMCP
)
View Source
const (
	HookPreRequest     = domain.HookPreRequest
	HookPostResponse   = domain.HookPostResponse
	HookPreToolExec    = domain.HookPreToolExec
	HookPostToolResult = domain.HookPostToolResult
	HookHistoryRewrite = domain.HookHistoryRewrite
)
View Source
const (
	ActionRetry     = domain.ActionRetry
	ActionIntercept = domain.ActionIntercept
	ActionDefer     = domain.ActionDefer
)
View Source
const (
	CapOffRamp        = domain.CapOffRamp
	CapProactiveNudge = domain.CapProactiveNudge
	CapResponseRepair = domain.CapResponseRepair
)
View Source
const (
	SuppressStrikesThree = domain.SuppressStrikesThree
	SuppressExempt       = domain.SuppressExempt
)
View Source
const (
	RoleSystem    = domain.RoleSystem
	RoleUser      = domain.RoleUser
	RoleAssistant = domain.RoleAssistant
	RoleTool      = domain.RoleTool
)
View Source
const (
	FinishStop      = domain.FinishStop
	FinishLength    = domain.FinishLength
	FinishToolCalls = domain.FinishToolCalls
)

Variables

View Source
var (
	// ErrAutoUnavailable is returned by New when Mode==Auto but the Confiner cannot
	// satisfy the Auto gate (missing or insufficient capabilities).
	ErrAutoUnavailable = domain.ErrAutoUnavailable

	// ErrConfinementUnavailable is the runtime safety net a Confiner backend returns
	// when it cannot establish a confinement box for a subprocess, so dispatch gates
	// the call through Approval instead of running it unconfined (ADR 0012).
	ErrConfinementUnavailable = domain.ErrConfinementUnavailable

	// ErrOrderingCycle is returned by New / registry Add when Mechanism ordering
	// constraints form a cycle.
	ErrOrderingCycle = domain.ErrOrderingCycle

	// ErrIncompatibleMechanisms is returned by New when two registered Mechanisms
	// declare each other incompatible (IncompatibleWith) — they must never co-fire.
	ErrIncompatibleMechanisms = domain.ErrIncompatibleMechanisms

	// ErrMissingRequirement is returned by New / Resume when a registered Mechanism declares a
	// required peer (MechanismDescriptor.Requires) that is not itself registered — the dual of
	// ErrIncompatibleMechanisms: where that refuses two Mechanisms that must never co-fire, this
	// refuses one half of a benched stack (enable both or neither, ADR 0014 §4). Match with errors.Is.
	ErrMissingRequirement = domain.ErrMissingRequirement

	// ErrUnknownMechanism is returned by New / Resume when Config.EnableMechanisms names an ID that
	// is not in the catalogue — a typo'd or deferred ID fails construction loudly rather than
	// silently disabling a Mechanism (ADR 0015 §4). The wrapping error still names the known IDs;
	// match the sentinel with errors.Is.
	ErrUnknownMechanism = domain.ErrUnknownMechanism

	// ErrSessionVersion is returned by Resume / DecodeSession for a snapshot whose
	// schema version this build does not understand.
	ErrSessionVersion = domain.ErrSessionVersion

	// ErrInputPending is returned by Submit when an Exchange is already in progress.
	ErrInputPending = domain.ErrInputPending

	// ErrDuplicateTool is returned by ToolRegistry.Register on a duplicate tool name.
	ErrDuplicateTool = domain.ErrDuplicateTool

	// ErrInvalidTool is returned by ToolRegistry.Register for an unaddressable tool
	// (currently an empty Name).
	ErrInvalidTool = domain.ErrInvalidTool
)

Functions

func IsReadOnly

func IsReadOnly(t Tool) bool

IsReadOnly reports whether a Tool has declared itself read-only; an undeclared tool is treated as write-capable.

Types

type Agent

type Agent = agent.Agent

Agent is a single embeddable Apogee agent instance — the engine handle. Its methods (Submit / Step / Run / Mode / Snapshot / Close) are the public stepping surface; construct one with New or Resume. See internal/agent for the contract.

func New

func New(cfg Config) (*Agent, error)

New constructs an Agent from cfg, validating the Auto/Confinement gate (ADR 0004) and the Mechanism ordering graph (ADR 0003) before returning a ready-to-Step Agent.

func Resume

func Resume(cfg Config, snap Session) (*Agent, error)

Resume reconstructs an Agent from a prior Session snapshot; cfg re-supplies the live delegates and state roots, snap supplies the serializable conversation state.

type ApprovalDecision

type ApprovalDecision = domain.ApprovalDecision

ApprovalDecision is the Approver's verdict.

type ApprovalEvent

type ApprovalEvent = domain.ApprovalEvent

The Event variants. The set is additively extensible (a new variant is a minor bump).

type ApprovalRequest

type ApprovalRequest = domain.ApprovalRequest

ApprovalRequest describes the pending tool call the human is asked to allow.

type Approver

type Approver = domain.Approver

Approver is the host-supplied human-in-the-loop gate on a single tool call.

type AskAnswer

type AskAnswer = domain.AskAnswer

AskAnswer is the human's free-text reply (a struct for freeze-safety).

type AskRequest

type AskRequest = domain.AskRequest

AskRequest is the free-text question put to the human (a struct for freeze-safety).

type Asker

type Asker = domain.Asker

Asker is the host-supplied free-text Q&A delegate the ask_user tool routes a question to. It is distinct from Approver (free-text, not a safety gate); a nil Asker means ask_user is not registered. A headless host must supply an Asker that fails safe (no hang).

type AuditEvent

type AuditEvent = domain.AuditEvent

The Event variants. The set is additively extensible (a new variant is a minor bump).

type Budget

type Budget = domain.Budget

Budget is the read-only context-budget view a hook reads.

type Capability

type Capability = domain.Capability

Capability is what a Mechanism does — and what Bypass switches on.

type Config

type Config = domain.Config

Config is the full construction surface (Upstream target, autonomy, delegates, registries, injected state roots). See domain.Config for the field contract.

type ConfinementBox

type ConfinementBox = domain.ConfinementBox

ConfinementBox is the confinement policy for a run.

type ConfinementCaps

type ConfinementCaps = domain.ConfinementCaps

ConfinementCaps is the capability matrix a Confiner reports.

type Confiner

type Confiner = domain.Confiner

Confiner is the OS-level confinement facility required for Auto mode (ADR 0004). The interface is public (the host injects it via Config); the backends live in internal/platform.

type ContextConfig

type ContextConfig = domain.ContextConfig

ContextConfig governs the structural context reducers (Budget, Compaction).

type Conversation

type Conversation = domain.Conversation

Conversation is the serializable conversation state a history-rewrite hook edits.

type ConversationView

type ConversationView = domain.ConversationView

ConversationView is read-only history with tool-call/result pairing helpers.

type ErrorEvent

type ErrorEvent = domain.ErrorEvent

The Event variants. The set is additively extensible (a new variant is a minor bump).

type Event

type Event = domain.Event

Event is the sealed sum type of everything the loop reports. The seal (an unexported method) lives in internal/domain and is intentionally not re-exported, so external code switches on the variants but cannot add new ones.

type EventSink

type EventSink = domain.EventSink

EventSink receives typed Events as the loop produces them, including inside a Step.

type ExternalEffectKind

type ExternalEffectKind = domain.ExternalEffectKind

ExternalEffectKind classifies a non-forkable external effect.

type ExternalEffectTool

type ExternalEffectTool = domain.ExternalEffectTool

ExternalEffectTool is an optional interface a Tool implements when it reaches state Apogee does not own (network, MCP).

type ExternalEffects

type ExternalEffects = domain.ExternalEffects

ExternalEffects is the single injectable boundary for non-forkable external effects.

type FinishReason

type FinishReason = domain.FinishReason

FinishReason is the model's stop reason (open set).

type HistoryRewriter

type HistoryRewriter = domain.HistoryRewriter

The five hook interfaces a Mechanism (or bench experimental hook) may implement.

type HookPoint

type HookPoint = domain.HookPoint

HookPoint is where in the loop a Mechanism fires.

type LoopView

type LoopView = domain.LoopView

LoopView is the read-only window every hook has onto loop state.

type Mechanism

type Mechanism = domain.Mechanism

Mechanism is a catalogued unit of gated, self-regulating behaviour.

type MechanismDescriptor

type MechanismDescriptor = domain.MechanismDescriptor

MechanismDescriptor is per-Mechanism metadata orthogonal to its hook point.

func CataloguedMechanisms

func CataloguedMechanisms() []MechanismDescriptor

CataloguedMechanisms returns a descriptor for every catalogued Mechanism, sorted by ID and duplicate-free — the metadata needed to plan a Config.EnableMechanisms arm (each Mechanism's Capability, SuppressionPolicy, and its IncompatibleWith / Requires stacking relations) WITHOUT building any Mechanism. Each descriptor is a copy with its slice fields cloned, so a caller may traverse and mutate the result freely (e.g. compute a leave-one-out arm by dropping an ID and everything that Requires it). The catalogue's CONTENTS are data, not v1 contract — an ID may change in a minor with a CHANGELOG notice — while this query and the descriptor shape are the stable surface (ADR 0015 §3, locked decision 4).

type MechanismFiredEvent

type MechanismFiredEvent = domain.MechanismFiredEvent

The Event variants. The set is additively extensible (a new variant is a minor bump).

type MechanismID

type MechanismID = domain.MechanismID

MechanismID is the canonical, stable identifier of a Mechanism.

type MechanismRegistry

type MechanismRegistry = domain.MechanismRegistry

MechanismRegistry is the injectable catalogue plus the bench's experimental slots.

func NewMechanismRegistry

func NewMechanismRegistry() *MechanismRegistry

NewMechanismRegistry returns a registry seeded with the built-in catalogue.

type Message

type Message = domain.Message

Message is a read-only snapshot of one conversation message handed to hooks.

type MessageEvent

type MessageEvent = domain.MessageEvent

The Event variants. The set is additively extensible (a new variant is a minor bump).

type Mode

type Mode = domain.Mode

Mode is the autonomy level governing whether tool calls need human approval.

type ModelProfile

type ModelProfile = domain.ModelProfile

ModelProfile describes how the configured model speaks the wire — its tool-call format and inline thinking-channel style; the host sets it via Config.Profile (a zero profile is native tool calls with no inline thinking).

type OrderingConstraints

type OrderingConstraints = domain.OrderingConstraints

OrderingConstraints declares a Mechanism's position relative to others.

type PostResponseAction

type PostResponseAction = domain.PostResponseAction

PostResponseAction enumerates the post-response decisions.

type PostResponseDecision

type PostResponseDecision = domain.PostResponseDecision

PostResponseDecision is the action a post-response Mechanism chooses.

type PostResponseHook

type PostResponseHook = domain.PostResponseHook

The five hook interfaces a Mechanism (or bench experimental hook) may implement.

type PostToolResultHook

type PostToolResultHook = domain.PostToolResultHook

The five hook interfaces a Mechanism (or bench experimental hook) may implement.

type PreRequestHook

type PreRequestHook = domain.PreRequestHook

The five hook interfaces a Mechanism (or bench experimental hook) may implement.

type PreToolExecHook

type PreToolExecHook = domain.PreToolExecHook

The five hook interfaces a Mechanism (or bench experimental hook) may implement.

type PresentMethod

type PresentMethod = domain.PresentMethod

PresentMethod names the presentation-ladder rung that ran.

type PresentOutcome

type PresentOutcome = domain.PresentOutcome

PresentOutcome reports which rung of the presentation ladder carried the document to the user (a struct for freeze-safety).

type PresentRequest

type PresentRequest = domain.PresentRequest

PresentRequest is the document put in front of the user (a struct for freeze-safety).

type Presenter

type Presenter = domain.Presenter

Presenter is the host-supplied delegate the present_document tool routes a finished deliverable to; the host picks the mechanism (the presentation ladder), the model supplies only a path. Like Asker it is mode-independent and not a safety gate, and a nil Presenter means present_document is not registered.

type ReadOnlyTool

type ReadOnlyTool = domain.ReadOnlyTool

ReadOnlyTool is an optional interface a Tool implements to declare it performs no writes — the signal Plan mode and Ask-Before Approval gate on.

type ReasoningEvent

type ReasoningEvent = domain.ReasoningEvent

The Event variants. The set is additively extensible (a new variant is a minor bump).

type Request

type Request = domain.Request

Request is the outgoing Upstream request a pre-request hook may shape.

type ResolvedSkill

type ResolvedSkill = domain.ResolvedSkill

ResolvedSkill is one attached skill reduced to the fields the loop injects (ID, DisplayName, Body) — the return shape a SkillResolver produces.

type Response

type Response = domain.Response

Response is the model response a post-response hook inspects and may intercept.

type Role

type Role = domain.Role

Role is a conversation message's role.

type SamplingParams

type SamplingParams = domain.SamplingParams

SamplingParams are the optional sampling overrides a pre-request hook may set.

type Session

type Session = domain.Session

Session is the serializable, copyable conversation state (no live handles).

func DecodeSession

func DecodeSession(data []byte) (Session, error)

DecodeSession deserializes a session, returning ErrSessionVersion if the schema version is newer than this build understands.

type SkillResolver

type SkillResolver = domain.SkillResolver

SkillResolver maps a user's attached skill IDs (UserInput.SkillIDs) to their injectable bodies; the host injects it via Config.Skills (the binary loads a disk-backed catalog, but an embedder may supply any implementation). A nil resolver means an attached ID is reported and dropped. The concrete catalog lives in internal/skills, off the public surface.

type StepResult

type StepResult = domain.StepResult

StepResult reports the outcome of one Step at the quiescent boundary.

type StepStatus

type StepStatus = domain.StepStatus

StepStatus is the disposition of a completed Step (open set).

type StreamResetEvent

type StreamResetEvent = domain.StreamResetEvent

The Event variants. The set is additively extensible (a new variant is a minor bump).

type SuppressionPolicy

type SuppressionPolicy = domain.SuppressionPolicy

SuppressionPolicy is how a Mechanism participates in self-regulation.

type ThinkingProfile

type ThinkingProfile = domain.ThinkingProfile

ThinkingProfile selects a model's inline thinking-channel style (none / delimited / harmony).

type ThinkingStyle

type ThinkingStyle = domain.ThinkingStyle

ThinkingStyle names a model's inline reasoning-channel format.

type TokenEvent

type TokenEvent = domain.TokenEvent

The Event variants. The set is additively extensible (a new variant is a minor bump).

type Tool

type Tool = domain.Tool

Tool is the public, open extension point: embedders may register their own.

type ToolCall

type ToolCall = domain.ToolCall

ToolCall is a parsed request from the model to run a tool.

type ToolCallEvent

type ToolCallEvent = domain.ToolCallEvent

The Event variants. The set is additively extensible (a new variant is a minor bump).

type ToolCallFormat

type ToolCallFormat = domain.ToolCallFormat

ToolCallFormat selects how a model emits tool calls (native / markdown-fenced / custom-regex).

type ToolDef

type ToolDef = domain.ToolDef

ToolDef is one entry of the tool menu the model sees.

type ToolRegistry

type ToolRegistry = domain.ToolRegistry

ToolRegistry is the injectable set of available tools.

func NewToolRegistry

func NewToolRegistry() *ToolRegistry

NewToolRegistry returns an empty registry.

type ToolResult

type ToolResult = domain.ToolResult

ToolResult is what a tool returns to the loop (pre tool-result-capping).

type ToolResultEvent

type ToolResultEvent = domain.ToolResultEvent

The Event variants. The set is additively extensible (a new variant is a minor bump).

type UsageEvent

type UsageEvent = domain.UsageEvent

The Event variants. The set is additively extensible (a new variant is a minor bump).

type UserInput

type UserInput = domain.UserInput

UserInput is one user message into an Exchange (text plus optional file refs).

Directories

Path Synopsis
cmd
apogee command
Command apogee is the terminal coding agent for small local LLMs.
Command apogee is the terminal coding agent for small local LLMs.
internal
agent
Package agent is the embeddable agent loop: it builds requests, calls the Upstream, parses responses, dispatches tools, and applies Mechanisms at the loop's hook points.
Package agent is the embeddable agent loop: it builds requests, calls the Upstream, parses responses, dispatches tools, and applies Mechanisms at the loop's hook points.
context
Package context manages the model's working context: Budget allocation, the context builder, generative Compaction (the default reducer), and tool-result capping.
Package context manages the model's working context: Budget allocation, the context builder, generative Compaction (the default reducer), and tool-result capping.
domain
Package domain is the ubiquitous language (CONTEXT.md) rendered as Go: every type, interface, enum, sentinel error, and hook working-value in Apogee's public surface, plus the pure logic intrinsic to those types (the Mechanism registry's ordering-cycle detection, ConfinementCaps.AutoEligible, the Session envelope and its versioning).
Package domain is the ubiquitous language (CONTEXT.md) rendered as Go: every type, interface, enum, sentinel error, and hook working-value in Apogee's public surface, plus the pure logic intrinsic to those types (the Mechanism registry's ordering-cycle detection, ConfinementCaps.AutoEligible, the Session envelope and its versioning).
domain/domaintest
Package domaintest is the hook seam's shared test adapter (the internal/platform/confinetest precedent): conversation fixtures and a settable LoopView fake, so a Mechanism or engine test builds history and loop state through one vocabulary instead of hand-rolled per-file literals.
Package domaintest is the hook seam's shared test adapter (the internal/platform/confinetest precedent): conversation fixtures and a settable LoopView fake, so a Mechanism or engine test builds history and loop state through one vocabulary instead of hand-rolled per-file literals.
library
Package library is Apogee's cross-session, per-model learning substrate (CONTEXT "Library").
Package library is Apogee's cross-session, per-model learning substrate (CONTEXT "Library").
mcp
Package mcp is Apogee's Model Context Protocol client, built on the official Go SDK (github.com/modelcontextprotocol/go-sdk) over stdio / SSE / streamable-http.
Package mcp is Apogee's Model Context Protocol client, built on the official Go SDK (github.com/modelcontextprotocol/go-sdk) over stdio / SSE / streamable-http.
mechanisms
Package mechanisms is the curated Mechanism catalogue: a constraint-declared registry that the loop resolves into a deterministic total order (topo-sort with a stable canonical-ID tiebreak — ADR 0003).
Package mechanisms is the curated Mechanism catalogue: a constraint-declared registry that the loop resolves into a deterministic total order (topo-sort with a stable canonical-ID tiebreak — ADR 0003).
platform
Package platform abstracts shell execution and path handling across POSIX and Windows, and hosts the Confiner backends (seatbelt / landlock / AppContainer) that gate Auto mode as a capability matrix (ADR 0004).
Package platform abstracts shell execution and path handling across POSIX and Windows, and hosts the Confiner backends (seatbelt / landlock / AppContainer) that gate Auto mode as a capability matrix (ADR 0004).
platform/confinetest
Package confinetest is the shared escape-probe harness both Confiner backends' acceptance tests call, so "confined" means the same thing on Linux landlock and macOS seatbelt (confinement-execution-contract §6).
Package confinetest is the shared escape-probe harness both Confiner backends' acceptance tests call, so "confined" means the same thing on Linux landlock and macOS seatbelt (confinement-execution-contract §6).
present
Package present holds the HOST-SIDE mechanisms of the presentation ladder (ADR 0019): the locality/desktop detection that decides which rung applies, the OS opener that auto-opens a deliverable on a user's own desktop, and the capability-token doc server that makes one reachable from the user's machine when Apogee runs remotely.
Package present holds the HOST-SIDE mechanisms of the presentation ladder (ADR 0019): the locality/desktop detection that decides which rung applies, the OS opener that auto-opens a deliverable on a user's own desktop, and the capability-token doc server that makes one reachable from the user's machine when Apogee runs remotely.
processing
Package processing turns an Upstream response into the loop's domain values: it parses tool calls into domain.ToolCall and strips inline thinking / harmony channels from the assistant's visible content.
Package processing turns an Upstream response into the loop's domain values: it parses tool calls into domain.ToolCall and strips inline thinking / harmony channels from the assistant's visible content.
provider
Package provider talks to the Upstream: it owns the Responder seam (the interface the engine calls instead of net/http), the provider-local wire types, and the OpenAI-compatible Client that implements the seam — non-streaming Respond plus a streaming Stream, with bounded retries and timeouts, /v1/models discovery, and a local server-process manager.
Package provider talks to the Upstream: it owns the Responder seam (the interface the engine calls instead of net/http), the provider-local wire types, and the OpenAI-compatible Client that implements the seam — non-streaming Respond plus a streaming Stream, with bounded retries and timeouts, /v1/models discovery, and a local server-process manager.
security
Package security holds Apogee's human-in-the-loop safety guardrails — the layer that runs in EVERY mode, distinct from Auto-mode Confinement (the OS-level Confiner in package platform).
Package security holds Apogee's human-in-the-loop safety guardrails — the layer that runs in EVERY mode, distinct from Auto-mode Confinement (the OS-level Confiner in package platform).
session
Package session persists and reloads Agent snapshots — the same primitive the bench composes into forking and counterfactuals, which Apogee itself does not expose (ADR 0001).
Package session persists and reloads Agent snapshots — the same primitive the bench composes into forking and counterfactuals, which Apogee itself does not expose (ADR 0001).
skills
Package skills discovers user-authored skills from disk and serves them as a catalog.
Package skills discovers user-authored skills from disk and serves them as a catalog.
tools
Package tools holds the built-in Tool implementations that sit behind the public domain.Tool interface — an open extension point (ADR 0002).
Package tools holds the built-in Tool implementations that sit behind the public domain.Tool interface — an open extension point (ADR 0002).
tui
Package tui is the Bubble Tea terminal UI: a thin renderer over the agent's typed Events that supplies the Approval delegate.
Package tui is the Bubble Tea terminal UI: a thin renderer over the agent's typed Events that supplies the Approval delegate.
validated
Package validated implements the Validated-set runtime surface (ADR 0016 and its 2026-07-19 runtime-surface realisation): loading per-model Validated-set entries from the two sources (the embedded shipped bundle and the user-local drop-in dir), matching them against the resolved model fingerprint under the confidence-graded rule — auto-apply at ≥ medium confidence, offer at low, alias applies at any — and validating that an entry's enable set is whole and buildable against the current Mechanism catalogue (whole-set-or-nothing: a subset is an unvalidated stack and must not apply).
Package validated implements the Validated-set runtime surface (ADR 0016 and its 2026-07-19 runtime-surface realisation): loading per-model Validated-set entries from the two sources (the embedded shipped bundle and the user-local drop-in dir), matching them against the resolved model fingerprint under the confidence-graded rule — auto-apply at ≥ medium confidence, offer at low, alias applies at any — and validating that an entry's enable set is whole and buildable against the current Mechanism catalogue (whole-set-or-nothing: a subset is an unvalidated stack and must not apply).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL