- Go 99.1%
- Shell 0.9%
Project-specific guidance for future instances: build/test/run/deploy commands, the load-bearing agent boot dance (Catalog → MakeRunner → InstallRunner → AttachCrons/AttachWatchers — chicken-and-egg solved by lazy attach), the event model with explicit caused_by edges that the timeline relies on, the per-agent disk layout, the GOPROXY=off / stdlib-only tool builder constraint, the privilege boundary, and the discipline patterns baked into the default system prompt. Skips the obvious (write tests, use logging) and avoids duplicating the architecture plan / SECURITY.md / README; cross-references them where appropriate. Closes with a "where things tend to break" list covering the four traps I hit while building this session — JSON non-marshalable returns, missing event causal links, value-typed strings.Builder in Bubble Tea, and setSize layout math. |
||
|---|---|---|
| cmd | ||
| deploy | ||
| internal | ||
| pkg | ||
| scripts | ||
| .gitignore | ||
| CLAUDE.md | ||
| go.mod | ||
| go.sum | ||
| README.md | ||
Jarvis
A Go daemon that hosts many autonomous AGI-style agents. Each agent can write its own prompts, memory, Go tools (compiled to per-tool binaries), schedules, watchers, task boards, and sub-agents.
What works today
- Daemon (
jarvisd) runs as a systemd service. Boots agentless and stays healthy when no agents are configured. Restores persisted agents on startup. - CLI / TUI (
jarvis) — Bubble Tea dashboard + per-agent attach view. No args opens the TUI. Subcommands:info,ps,spawn,tell,attach,pause,resume,stop. - Reasoning loop — full LLM-driven session runner with streaming, parallel tool fan-out, multi-turn iteration, MaxTurns guard (configurable per agent), per-turn + cumulative token tracking.
- Kernel tools — irreducible tool surface every agent gets:
memory_read/write/list/search,tool_write/list/delete/stats/share,llm_list_models,heartbeat_set. Tools authored by agents compile viago buildand dispatch as JSON-stdin/stdout subprocesses throughpkg/toolio. - LLM providers — Anthropic (streaming + tool use via /v1/messages),
Ollama (local, /api/chat), mock (offline tests). Aliases (
smart,fast,cheap) configurable; agents can callllm_list_modelsand pick their own model per call. - State machine — running ↔ paused ↔ stopped → terminated. Pause freezes the heartbeat without losing state.
- Memory — per-agent sandboxed markdown + JSON store with a single-writer serializer for concurrent reasoning sessions.
- Tool quality stats — per-tool rolling latency histograms, failure-rate tracking, verdict (healthy / watch / degraded).
- Event bus — every action the daemon takes (thoughts, tool calls
with args + results, memory ops, state changes, build outcomes, token
usage) flows through a fan-out bus. Persisted to
agents/<id>/logs/events.jsonlfor forensics + replayed to new attaches from an in-memory ring (last 500 events per agent) so the TUI doesn't open blank. - TUI attach view — single chronological transcript: streamed prose
interleaved with timestamped tool calls, memory ops, and lifecycle
markers. Word-wrap, inline markdown rendering (
**bold**,`code`, headers), syntax-highlighted Go fortool_write.source(chroma / monokai). Tab toggles focus between input and transcript; mouse wheel scrolls the transcript; pgup / pgdn / home / end navigate.
What's planned but not yet built
- Cron scheduler (
cron.add/remove/list) - Task board with cross-agent DAG dependencies
- Watchers (passive monitors that wake the agent only on triggers)
- Self-reflection (
reflectkernel tool) - Agent lifecycle: ttl, self-destruct, graveyard
- Starter tool library (
http_get,web_fetch,summarize, …) - HTTP bridge (transport-agnostic inbox over
/inbox/<agent>) - WebSocket-backed web frontend
- Security audit + hardening pass
Full architecture and design rationale:
/home/wesley/.claude/plans/i-d-like-to-build-mutable-pearl.md
Build
go build -o bin/jarvisd ./cmd/jarvisd
go build -o bin/jarvis ./cmd/jarvis
Install as a service
bash scripts/install-service.sh # asks for sudo once
$EDITOR ~/.jarvis/jarvisd.env # set ANTHROPIC_API_KEY etc.
sudo systemctl restart jarvisd
The install script copies deploy/jarvisd.service into
/etc/systemd/system/, symlinks bin/jarvis(d) into /usr/local/bin,
seeds ~/.jarvis/jarvisd.env (gitignored), and enables the unit.
For ongoing updates after a git pull:
bash scripts/update-service.sh
The update path needs only a small NOPASSWD entry for
systemctl restart|reload|status jarvisd.service — see the install
script for details.
Run
jarvis # TUI dashboard
jarvis ps # list agents
jarvis spawn alice # create + start an agent
jarvis tell alice "hello" # send a message
jarvis attach alice # tail the event stream
jarvis pause alice # suspend heartbeat
jarvis resume alice
Runtime requirements
- Go toolchain on
$PATH(the daemon shells out togo buildto compile agent-written tools). - Anthropic API key in
~/.jarvis/jarvisd.envfor real model use. - Optional: a local Ollama server for local models.
- Optional: Valkey or Redis on
127.0.0.1:6379(currently unused; the working-memory backend ships separately and is not yet wired into the kernel tools).
The daemon runs cleanly with only a mock provider — useful for offline development. Aliases auto-remap to mock when the configured target is unreachable, so a fresh install Just Works without keys.
Repository layout
cmd/
jarvisd/ daemon entrypoint
jarvis/ CLI + TUI client
internal/
agent/ per-agent supervisor + state machine
config/ TOML loader
events/ event bus + per-agent ring buffer
eventlog/ on-disk JSONL persistor
ipc/ Unix-socket framed JSON-RPC
kernel/ irreducible tool catalog + LLM-driven session runner
llm/ provider abstraction (Anthropic, Ollama, mock)
memory/ per-agent sandboxed markdown / JSON store
sandbox/ path-escape enforcement
tools/ go build driver + invoker + rolling stats
tui/ Bubble Tea views (dashboard, attach)
valkey/ client + key namespacing (ready, not yet wired)
pkg/
toolio/ shared ABI for agent-written tools
watchio/ shared ABI for agent-written watchers
deploy/ systemd unit + env template
scripts/ install + update helpers
Tests
go test ./...
Integration smoke tests gated on ANTHROPIC_API_KEY (e.g.
TestAnthropicSmoke) skip when the env var is unset, so plain
go test ./... stays offline.