axagent experiencelive
what's under the hood

What ax knows about your agent.

A tour of the data ax keeps on your laptop - the transcripts it indexes, the graph it builds, the stages it derives, the interventions it proposes, and the hooks it watches in real time.

369,132turns indexed
4,773sessions ingested
5.9msmedian FTS query
8 stagesderived per ingest
01 / Sources

Everything your agent already wrote down.

Your agent leaves a paper trail in five places. ax reads all of them.

Claude transcripts~/.claude/projects/*.jsonl
Codex sessions~/.codex/sessions/*.jsonl
Git historycommits · file changes
Hook firespre-tool · post-tool · stop
Installed skills~/.claude/skills/ · ~/.agents/skills/ · ~/.claude/plugins/cache/
graphSurrealDB · local

A LaunchAgent (com.necmttn.ax-watch) tails your Claude and Codex transcript directories and runs ax ingest --since=1 in the background within seconds of a new turn. A weekly cron does a deep-scan backfill for anything the watcher missed. Nothing is uploaded, queued, or phoned home - every read stays on the same filesystem your agent already writes to.

liveLaunchAgent tails new turns, ingests in ~2s
weeklycron deep-scans for missed sessions + drift
02 / The graph

A typed graph of who did what, where.

Transcripts become nodes and edges. Sessions own turns. Turns invoke tools. Tools touch files. Files end up in commits.

// schema sketch - SurrealDB v3, ns=ax, db=main session ──owns──▶ turn ──invoked──▶ tool_call │ │ │ │ │ ├──touched──▶ file │ │ └──fired────▶ hook_event │ │ │ └──addressed──▶ skill │ └──produced──▶ commit ──changes──▶ file // relations are first-class - every edge is queryable
Nodes - 6 of ~14
sessionone agent conversation, start → end
turna single user-assistant exchange
tool_callRead, Edit, Bash, MCP - one invocation
skillan installed Claude/Agents skill
filea path the agent read or wrote
commitgit commit attributed to a session
Relations - 5
invokedturn → tool_call
editedtool_call → file (Edit/Write)
touchedtool_call → file (Read)
producedsession → commit
addressedturn → skill (mentioned/loaded)
03 / Derive stages

Raw events become findings.

Once the graph is built, ax runs a chain of small derive stages. Each one writes back into the same database, so every later query can ask the typed question instead of the raw one.

closurecommit
Classifies each commit as feature-only or feature-then-fix.
writes commit.closure
skill_candidatepattern
Finds fix-chain patterns worth packaging as a skill.
writes skill_candidate
session_healthsession
Tokens, cache hit-rate, and context pressure per session.
writes session.health
friction_eventsignal
Surfaces what failed, where, and how often.
writes friction_event
command_outcometool
Tags tool calls: success · expected-feedback · guardrail · failure.
writes tool_call.outcome
workflow_epochwindow
Splits your history into eras by toolset and workflow shape.
writes workflow_epoch
harness_doctorinstall
Audits installed skills, hooks, plists, and settings.json for drift, conflicts, dead weight.
writes harness_finding
classifiersharness
Pluggable graph classifiers that read derived rows and emit higher-order labels.
writes classifier_result
04 / Interventions

Proposals, lifecycle, and safety gates.

Patterns that recur in your graph turn into Intervention proposals. Each one moves through a tracked lifecycle, and any change to your harness ships with a written rollback contract.

Six forms
skilla new SKILL.md packaging a repeated pattern
guidancea line of instruction added to a grounded file
subagenta routed sub-agent under ~/.claude/agents/
hooka pre/post-tool gate in settings.json
automationa plist or cron entry that runs on a schedule
harness_checka doctor assertion that proves the change still holds
Intervention safety contract
recovery_pathwritten rollback steps. Required on every hook + automation.
smoke_test_commandone shell command that proves the agent still works after.
disable_commandkill switch you can paste from memory in a panic.
failure_modefail_open (let through if hook errors) or fail_closed.
Lifecycle - eight states, checkpointed against future sessions
openacceptedtask_emittedmarker_landed+3 sessions+10 sessions+30 sessionslocked_verdict
Manual-safe by commitment. ax never edits settings.json, hooks, LaunchAgents, cron, or shell scripts directly. An accepted intervention emits a task brief at .ax/tasks/<id>.md with the safety contract on top. You - or your agent - apply it. ax improve lint reads the marker you left behind to reconcile the proposal back to accepted state.
.ax/tasks/hook_chk_skip_test_runner.mdmd
---
form: hook
experiment: experiment:chk_skip_test_runner
recovery_path: Remove the matching block from ~/.claude/settings.json,
                  restart Claude Code, run smoke test.
smoke_test_command: bun test src/ingest/turns.test.ts
disable_command: jq 'del(.hooks.PreToolUse[] | select(.matcher=="Bash"))' \
                  ~/.claude/settings.json > /tmp/s.json && mv /tmp/s.json ~/.claude/settings.json
failure_mode: fail_open
---

# Hook: block `bun test` without the wrapper

Add to ~/.claude/settings.json hooks.PreToolUse:

{
  "matcher": "Bash",
  "hooks": [{
    "type": "command",
    "command": "echo 'ax:chk_skip_test_runner' && ax-hook check-test-runner"
  }]
}

The echo line is the marker ax improve lint looks for.
05 / Hooks

Watch tool fires in real time.

Claude Code and Codex call hook handlers around every tool invocation. ax ships handlers that record each fire as a row, and lets you backtest a candidate hook against your last 7–30 days before turning it on for real.

~/.claude/hooks/ax-record.tsts
import { recordHook } from "ax/hooks";

export default async function handler(event) {
  // every pre-tool fire becomes one row
  await recordHook({
    phase:   "pre_tool",
    tool:    event.tool_name,
    session: event.session_id,
    inputs:  event.tool_input,
    at:      new Date(),
  });
}
Phase coverage
pre_tool, post_tool, and stop are all captured. Hook rows link back to the turn and tool_call that fired them.
Inspect + backtest
ax hooks shows fire counts, marker reconciliation, and who-installed-what. ax hooks backtest ./my-hook.ts --days 14 replays the last two weeks of tool calls through a candidate before you ship it.
No daemon, no socket
Hooks write directly to the local SurrealDB. If ax isn't running, the handler no-ops in under a millisecond - your agent never blocks on us.
06 / Surfaces

One CLI, seven verbs.

Everything ax knows is reachable from ax. The dashboard and TUI are the same queries with different paint.

ax recall <q>BM25 full-text search across every user + assistant turn. Median 5.9ms.
ax contextBuilds a just-in-time context pack from the graph for the next session.
ax hooksInspect installed hooks, fire counts, marker reconciliation, and backtest candidates.
ax doctorHarness health check - drift, conflicts, dead weight across skills + hooks + plists.
ax improve list / show / accept / reject / lint / verdict / checkpointWalk the intervention queue. Accept emits a task brief; lint reconciles the marker.
ax serveLocal web dashboard at 127.0.0.1:8520 with the same data the TUI sees.
ax tuiInteractive terminal dashboard. Sessions, interventions, harness in one pane.
ax ingest --since=NWhat the LaunchAgent calls on every new transcript. Idempotent; safe to rerun.
07 / Local-first

One process, one database, one laptop.

ax is a single binary that runs as a LaunchAgent, talks only to localhost, and stores everything in a SurrealDB instance you own.

database127.0.0.1:8521SurrealDB v3, schemafull. Namespace ax, db main.
dashboard127.0.0.1:8520Web UI for the graph, findings, and intervention queue. No auth - it's your loopback.
daemonLaunchAgentmacOS + Linux, installed by ax install. Survives reboots.
licenseMIT · single binaryBuilt with Bun. No cloud account, no telemetry egress. brew uninstall ax removes it.

“Your transcripts are already on your laptop. ax just reads them where they sit.”