Documentation
¶
Overview ¶
agent subcommand dispatcher for `aura agent {dry-run}`. Lives in package main alongside cmd/aura/main.go's switch case "agent", mirroring db.go/neo4j.go.
`aura agent dry-run` is the operator-facing cornerstone proof (SC#4): it drives a mock LoopAgent over agenttest.InfiniteToolCallAgent through the real Budget tree and prints one Event per JSON line. Every line carries the same UUIDv7 request_id (OTel-compatible run correlation). Flag precedence is CLI > env > builtin default (D-06): a numeric flag left at the -1 sentinel falls through to env/default inside agent.NewBudget, a non--1 flag overrides it. The flag values are passed through agent.BudgetOptions, never injected into the process env.
W7: Events are serialized via json.NewEncoder(w).SetEscapeHTML(false), honoring Event.MarshalJSON — the SINGLE user-facing serialization path. canonicaljson is for hashing/dedup only and never appears on this print path.
cache subcommands for `aura cache-stats` (operator-facing, advertised) and the hidden `aura cache-audit` (the runtime-faithful KV-prefix invariant gate, D-06). Both are top-level main.go switch cases mirroring `aura db`; cache-audit is NOT listed in usage() — it is a CI/maintenance gate, not a user command.
The two implementations live in cache_stats.go and cache_audit.go so neither file approaches the 600-LOC ceiling. Each exposes a pure core that returns an exit code; the os.Exit boundary stays in these thin wrappers so the cores are table-testable without a re-exec subprocess.
Hidden `aura cache-audit` — the runtime-faithful KV-cache prefix invariant gate (D-04/D-05/D-06, SC#1/SC#5). It replays 20 deterministic fixtures through the REAL runner.Turn -> LlmAgent.Run -> PromptBuilder.Build path against an agenttest.FakeClient (no synthetic Build() shortcut), reads each captured Requests[n].Messages[0], hashes it with prompt.PrefixHash({0}), prints `request NN: <hex>` to stdout, and asserts every hash is identical. The whole audit runs against in-memory fake Stores, so it needs NO Postgres.
Exit codes (PRD amendment #16): 0 pass / 1 messages[0] mutation / 2 fixture corrupt.
`aura cache-stats --since=<dur>` — a real time-windowed read of aura.cache_metrics (D-06, SC#4). It parses the duration with time.ParseDuration (rejecting unparseable input BEFORE any query, T-06-02), windows the metrics on ts >= now-d via the 06-03 sqlc queries, and prints a tabwriter table of per-turn rows plus a summary line. The cache hit-rate ratio is computed client-side from the summed integers with a total_prompt==0 guard (never a SQL float divide); the 80% target is displayed, never gated (provider-dependent, PRD OQ4).
In-memory, Postgres-free Store fakes for the hidden `aura cache-audit` replay (D-05 / Open Question 2). They live in package main (NON-test) because the shipped subcommand imports them — runner/fakes_test.go is unreachable from a binary. Each fake satisfies the narrow runner consumer interface (ConversationStore / PauseStore / IdentityStore / CacheMetricStore) with just enough behaviour for Runner.Turn to drive a real round in-process: the conversation store round-trips an in-memory turn history, the pause/identity stores answer the lookups the loop makes, and the cache-metric store is a no-op (the audit asserts the prefix invariant, not the metrics).
chat subcommand for `aura chat`: the multi-thread, PERSISTED conversation REPL (Phase 4 / Slice 1.8). It is a hand-rolled switch group {list|new|resume|archive|unarchive|delete|rename|search} mirroring runDB (NOT cobra — the OQ1 deviation recorded for identity/paused-states). Bare `aura chat` starts a NEW persisted conversation REPL; `aura chat resume` (no id) resumes the most-recent active conversation.
The REPL drives runner.Runner (D-A3-06): the Phase-3 streaming + dim tool-activity + per-turn cost footer + two-stage Ctrl+C UX is preserved, now sourced from the Runner's Event stream. On an Actions.AwaitingInput Event it renders the ask_user prompt inline per kind (D-A3-02) and resumes via SubmitAnswer + Turn(convID,nil).
The composition root (D-A2-05) lives in bootChat: config.Load -> db.Open -> identity/askuser/conversations Stores + ScanOrphans (boot reconciliation, Req#12) + tiktoken InitEncoder -> runner.Runner.
chat_reasoning.go holds the REPL's bounded live chain-of-thought renderer, split out of chat_render.go (refactor-on-touch, ≤600 LOC). It replaces the original append-only dim stream (amendment #57e), which grew unbounded and never cleared, with a rune-capped in-place rolling line that mirrors the Telegram status pane's window for cross-surface parity (.planning/spikes/cot-reasoning-fifo.md).
chat_render.go holds the cmd/aura chat rendering helpers split out of chat.go (refactor-on-touch, ≤600 LOC): the streamed-prose renderer, the per-turn cost footer (D-11), and the dim tool-activity one-liner (D-12).
The LlmAgent decodes the terminal text_response itself (D-13) and surfaces the decoded answer as clean prose — on chunk Events for the content-stop fallback, or on the final Event (Content+FinishReason) for the text_response path — so the REPL never has to parse raw tool-call JSON. renderTurn therefore streams whatever prose the agent yields and never shows JSON (Req#11). The incremental JSON-string extractor AI-SPEC §4b sketched is unnecessary here because the agent owns that decode; were the agent to ever stream raw tool-call args instead, the extractor would move here.
chat_repl.go holds the Runner-driven REPL loop, the inline kind-specific pause rendering (D-A3-02), the two-stage Ctrl+C wiring, and the list/search printers split out of chat.go (refactor-on-touch, ≤600 LOC). The loop drives runner.Runner.Turn and resumes a pause via SubmitAnswer + Turn(convID,nil).
config subcommand dispatcher for `aura config {show|get|set}`. Lives in package main alongside cmd/aura/main.go's switch case "config", mirroring db.go:19-44. show: print the effective llm.Config with APIKey shown as REDACTED (D-24/D-28 — the real key value NEVER reaches stdout). get: read a dotted file-tier key (llm.model, llm.base_url, ...) from the effective config. set: read-modify-write ~/.aura/llm.json (creating the dir+file if absent), persisting only file-tier keys. Unknown key / bad usage -> stderr + os.Exit(1). The set path never persists the API key (it normally comes from .env/the environment, not this file).
db subcommand dispatcher for `aura db {migrate|ping|status|reset}`. Lives in package main alongside cmd/aura/main.go's switch case "db". Each branch loads config, opens what it needs (pool for ping/status; migrate URL for migrate/reset), prints a human-readable status line, exits non-zero on error. Per D-07: migrate/reset require AURA_DB_MIGRATE_URL.
identity subcommand dispatcher for `aura identity {list|get|grant|revoke}` (CORE-03 Req#6). Hand-rolled switch tree mirroring runDB (cmd/aura/db.go), NOT cobra.
DEVIATION (04-RESEARCH OPEN QUESTION 1 / Assumption A2): CONTEXT D-A3-05 says "cobra command group", but go.mod has no spf13/cobra and the codebase uses nested switch dispatchers (runDB). CLAUDE.md mandates following existing patterns; SPEC never requires cobra. Implemented as a switch subcommand tree. This is a HOW deviation from CONTEXT wording, not a SPEC requirement change.
Each branch loads config, opens the aura_app pool, constructs identity.Store, runs one operation, prints a human-readable line, exits non-zero on error. grant/revoke of '*' surface the Store's system-managed error to stderr with a non-zero exit (the wildcard is seeded, never CLI-managed).
integrations_proxy.go is the Aura-backend reverse proxy the operator cockpit calls to drive a managed sidecar's admin/management REST API WITHOUT ever holding the sidecar's admin token: the cockpit makes a same-origin call to /api/integrations/<name>/<subpath>, and Aura forwards it to the sidecar on loopback, injecting the token server-side. This is the connect/account-management data plane the v1.0.0 cockpit "Integrations" surface (Phase 28/29) wires onto.
Posture: the route mounts on the SAME loopback, auth-deferred daemon mux as the AG-UI gateway (amendment #35) — it inherits that posture and is gated together with the rest of the mux when serve auth lands (Phase 24).
Registry-driven (builtinIntegrations): two legs are wired.
- calendar: the PIM sidecar's token-gated /admin REST API (accounts CRUD + device-code auth), token injected server-side.
- whatsapp: the forked whatsmeow bridge's management REST (/api/{status,qr,logout}) the cockpit drives to link a device — loopback + unauthenticated, no token.
Aura entry point. Sub-commands:
aura serve — run the long-lived agent runtime (default in production) aura shell — interactive REPL against the agent loop aura agent dry-run — drive a mock LoopAgent through the Budget tree, one Event per JSON line (SC#4) aura web <sub> — web tooling: doctor (SearXNG reachability) | tool web_search/web_fetch '<json>' aura doctor - aggregate runtime dependency health check aura tools — print the tool manifest (active + deferred) aura db <sub> — Postgres lifecycle (migrate|ping|status|reset) aura neo4j <sub> — Neo4j lifecycle aura identity <sub> — identity + capability_grants (list|get|grant|revoke) aura profile <sub> — filesystem Agent.md profile (show|add-fact) aura paused-states <sub>— HITL pause escape hatch (list|purge --before <ISO> --confirm) aura chat <sub> — multi-thread conversation REPL (list|new|resume|archive|unarchive|delete|rename|search) aura version — print build metadata (version, commit, build date)
Tabula-rasa scaffold: `tools`, `agent`, `db`, `neo4j`, `chat`, `shell`, and `serve` are wired through the real runtime composition roots. The Phase-1 `aura chat` stub + concrete Loop were removed in Slice 0.9 (Plan 02-07); `aura chat` returned in Phase 3 wired to a real LlmAgent.
neo4j subcommand dispatcher for `aura neo4j {migrate|ping|status|reset|cypher}`. Lives in package main alongside main.go's switch case "neo4j". Each branch loads config, opens what it needs (Postgres pool for the audit trail; an MCP subprocess client for Cypher), prints a human-readable line, exits non-zero on error. The `cypher` branch is the raw escape hatch the smoke harness drives.
paused-states subcommand dispatcher for `aura paused-states {list|purge}` (CORE-02 Req#11 escape hatch). Hand-rolled switch tree mirroring runDB (cmd/aura/db.go), NOT cobra — go.mod has no spf13/cobra and the codebase uses nested switch dispatchers (the same OQ1 deviation recorded for identity/chat).
`list` shows the most-recent paused_states rows (pending + auto-resolved) with their persisted answer; `purge --before <ISO> --confirm` deletes resolved rows older than the cutoff, gated by --confirm like dbReset's destructive guard.
profile subcommand dispatcher for `aura profile {show|add-fact}`.
serve subcommand for `aura serve`: the first long-lived Aura daemon (D-15). It hosts the scheduler tick loop on the shared composition root (bootChatEnv, the error-returning boot also used by `aura chat`), wires the real per-TaskKind handlers + the composite Notifier + the live cron store into the cron Dispatcher seam (the wiring 10-05 deferred to the composition root), and runs until SIGINT/ SIGTERM. Shutdown is graceful: cancelling the root ctx stops new ticks, the in-flight tick finishes + joins its workers (Scheduler.Start returns), then the MCP closers are reverse-closed and the pool released — goleak-clean (Pitfall 6: the shared boot has no os.Exit, so this shutdown path always runs).
The serve daemon owns the LIVE store wiring three downstream seams need:
- the cron.Dispatch handler map (reminder/agent_job/backup_*) — handlers import internal/agent/tools, and tools imports cron, so cron cannot import handlers; the map is adapted here (the composition root imports both, 10-05 deviation #1);
- a *tools.Registry → cron.SelfSendResolver adapter for the MCP self-send Notifier;
- a cron.Store → tools.taskStore adapter injected into the live `task` tool so the LLM-facing scheduler verb persists against the real DB (10-05 deviation #3).
serve_adapters.go holds the composition-root adapters that bridge the cron-local consumer-declared interfaces (10-05 deviation #1/#3) onto the live runtime types, keeping package cron free of an internal/agent/tools import and the tools package free of an internal/cron import. Two adapters live here:
- selfSendResolver: a *tools.Registry → cron.SelfSendResolver over the mounted MCP self-send tools (send_message / send_email), namespaced <server>__<tool>;
- cronTaskStore: a cron.Store → tools.taskStore so the live LLM-facing `task` tool persists against the real Postgres (the status-aware INSERT + approve/run_now UPDATEs the cron.Store does not expose are run as raw parameterized SQL over the pool, mirroring cmd/aura/task.go — never string-concatenated, T-10-01).
serve_auth.go builds the WEB-03 web-auth dependency bundle (agui.AuthDeps) the `aura serve` daemon threads into newServeHandler. It is split out of serve.go (refactor-on-touch, CLAUDE.md ≤600 LOC) and owns three things:
- identityCheckerAdapter: a thin bridge so *identity.Store satisfies the agui consumer-side identityChecker seam. identity.Store returns identity.Identity; agui declares its own narrow Identity projection so the agui package does not import internal/identity. The adapter does the trivial field copy.
- buildAuthDeps: derives the HMAC signing key from AURA_WEB_AUTH_SECRET (one operator secret governs both login and signing — RESEARCH A2), binds the session to the seeded `local` identity, and sets SecretConfigured from the non-empty secret so RequireAuth no-ops on loopback dev (where the Plan-01 boot guard permits an unconfigured secret).
- the Authula provider seam (docs/cockpit-overhaul/05-authula-auth-SPEC.md, Option A2): when AURA_WEB_AUTH_PROVIDER=authula, it constructs the embedded Authula framework on the isolated authula schema, binds the operator user ⇄ `local` identity, and injects a SessionValidator into AuthDeps so RequireAuth's cookie core validates the Authula session instead of the HMAC cookie. Default (passphrase) leaves everything byte-identical to before.
serve_channels.go is the channels-Registry + setup-server wiring for the `aura serve` daemon (Phase 13 / Slice 9, UX-02/03). It is split out of serve.go (refactor-on-touch, CLAUDE.md ≤600 LOC) and owns three things:
- bootChannelsAndSetup: builds the telegram channel (over the shared composition root's Runner + pool), registers it in a channels.Registry, and builds the loopback setup-wizard HTTP server (:9081) with a telebot getMe BotProbe closure;
- startChannelSubsystems / stopChannelSubsystems: the fail-soft daemon lifecycle, mirroring serve.go's AG-UI mount — StartAll the registry + run the setup server in a log-but-never-exit goroutine; StopAll + Shutdown on teardown, both BEFORE env.close();
- serveTelegramOverride: the --no-telegram / --only=cli flag parsing that overrides the AURA_CHANNEL_TELEGRAM_ENABLED env gate (PRD Punto 1).
Every subsystem is fail-soft: a failed channel Start or a taken setup port is logged and aggregated, never aborts the daemon (T-13-09-DaemonAbort).
serve_drain.go is the bounded in-flight turn drain for `aura serve` shutdown (audit O-06 / mitigation AP-17). It is split out of serve.go (refactor-on-touch, CLAUDE.md ≤600 LOC) and owns ONE small, testable primitive: drainWithGrace.
The problem it solves: on a SIGTERM the daemon's work ctx must NOT be cancelled the instant the signal fires, or an in-flight turn is hard-killed mid-stream instead of reaching its terminal Event/frame (the channels path derives its per-turn ctx from the daemon ctx; the HTTP/SSE path drains via http.Server. Shutdown). drainWithGrace runs the teardown that joins in-flight turns under a BOUNDED grace window: if the turns finish in time it reports Drained, otherwise it reports TimedOut so the caller proceeds to the hard-cancel backstop. It never blocks past the grace on an overrunning turn — bounded shutdown, never a wedge.
serve_webui.go wires the embedded operator SPA into the running daemon (FND-02 + WEB-01). The single-binary host serves the committed Vite build (internal/webui) at "/", additively, on the SAME loopback http.Server that already carries the AG-UI gateway — the embed mount adds no new listener and no new bind (T-23-06).
Precedence is the whole design: a Go 1.22 http.ServeMux gives a longer/more specific registered pattern priority over the catch-all "/", so registering the explicit AG-UI route prefixes (/healthz, /readyz, /debug/vars, /metrics, /agent/run, /threads/) to the AG-UI handler and the integrations proxy subtree (/api/integrations/) ahead of "/" keeps those routes authoritative while everything else falls through to the embed.
WEB-01: the "/" catch-all is an SPA-fallback, not a bare static tree. An unknown CLIENT route returns index.html (React Router resolves deep links); an excluded API/agent/health prefix returns a real 404 so the SPA shell never leaks to an API client (SC1). The exclusion set is SINGLE-SOURCED here — fallbackExcludedPrefixes() derives it from the AG-UI namespaces + the integrations subtree + the forward-compat "/api/" carve-out and passes a copy into webui.Handler, so the parent-mux registration and the fallback exclusion cannot drift (Pitfall 6 / T-24-08).
The "/api/" carve-out is an EXCLUSION prefix ONLY — it is NOT registered on the mux. "/api/integrations/" is already mounted; a second mux.Handle("/api/", ...) would collide with / shadow that subtree (T-24-07). Adding "/api/" only to the fallback exclusion makes "/api/anything" 404 today and lets a real /api/* route register tomorrow without touching the fallback.
internal/webui stays leaf-level (it imports no other internal/* package), an invariant scripts/agui_boundary_check.sh enforces via a dependency-closure assertion on the webui package.
skills subcommand dispatcher for `aura skills {list|info|create|update|delete| approve|always|snippet|audit}` (Slice 7c governance, plan 11-05). Hand-rolled switch tree mirroring runIdentity/runTask (cmd/aura/identity.go), NOT cobra — go.mod has no spf13/cobra and the codebase uses nested switch dispatchers; CLAUDE.md mandates following existing patterns. The install/catalog legs were removed in plan 11-09 (amendment #51 / D-40): discovery+install is the find-skills always-on skill driving `npx skills` in the sandbox, not a CLI/tool leg.
This is the operator-side governance channel (D-03): `approve <name>` activates a pending skill via Writer.Activate with the CLI approval source (the human-only activation path the MODEL can never reach); `audit` lists the append-only aura.skill_audit rows. create/update/delete stage a pending mutation (operator can then `approve` it); delete is the Destructive removal. The audit purge path SURFACES the role error (aura_app holds SELECT+INSERT only — no DELETE/TRUNCATE), proving the append-only ledger at the CLI boundary (SC#2).
`aura skills snippet {save|exec}` (Slice 7e, plan 11-07). save stages an executable snippet (a type:snippet skill) as pending, gated like any mutation (the operator then `approve`s it). exec is the DETERMINISTIC operator path that runs an ACTIVE snippet BY PATH on the HOST (the materialized export-dir file) via os/exec AND stamps the usage sidecar (D-04/D-19). exec resolves the host path + interpreter via Writer.UseSnippet — the same materialized file the model runs through shell_exec.
swarm-demo subcommand for `aura swarm-demo`. Lives in package main alongside main.go's switch case "swarm-demo", mirroring agent.go's `aura agent dry-run`.
`aura swarm-demo` is the operator-facing proof that the swarm engine fans goals out and collects an ordered report array end-to-end — WITHOUT a live LLM or any network call (D-16 convenience deliverable). It drives the real internal/swarm.Run engine over an agenttest.FakeClient mock-LLM fixture: every worker pops one content-stop turn returning a deterministic answer, so each goal yields a {status:ok} report in goal order. The result is printed as a JSON array, one run, deterministic, no OPENROUTER key required.
task subcommand dispatcher for `aura task {schedule|list|cancel|run_now|approve|runs|doctor}` (D-14 full triad CLI parity + D-17 doctor verb). Lives in package main alongside cmd/aura/main.go's switch case "task". It mirrors runWeb/runWebDoctor: a hand-parsed switch (no cobra — repo convention), config.LoadDB so no OPENROUTER_API_KEY is needed, human-readable output, sysexits codes. It makes EVERY scheduler grammar path operator-testable without an LLM (SC#1 + the chaos/smoke scripts).
The schedule path uses the shipped cron.ParseSchedule (gronx.IsValid before persist, T-10-14) + DST-safe cron.NextRunAt, and scoring.ComputeTaskTier to route a destructive payload to pending_approval (D-27). Persistence is raw parameterized SQL over the pool (the status-aware INSERT + approve/runs/doctor aggregates that the 10-02 store does not yet expose) — never string-concatenated, T-10-01.
Source Files
¶
- agent.go
- assets.go
- cache.go
- cache_audit.go
- cache_stats.go
- cachefakes.go
- chat.go
- chat_reasoning.go
- chat_render.go
- chat_repl.go
- config.go
- db.go
- docs.go
- doctor.go
- document_processor_wiring.go
- exit_codes.go
- identity.go
- integrations_console.go
- integrations_proxy.go
- llm_client.go
- main.go
- mcp.go
- mcp_profile.go
- mcp_status.go
- mcp_tools.go
- memory.go
- neo4j.go
- objectstore.go
- paused_states.go
- profile.go
- serve.go
- serve_adapters.go
- serve_auth.go
- serve_channels.go
- serve_drain.go
- serve_webui.go
- shell.go
- skills.go
- skills_snippet.go
- swarm_demo.go
- task.go
- toolpipe.go
- version.go
- web.go