# neosian

> Async-only Python state layer for LLM agents: durable conversations,
> agent-curated file-shaped memory, and context lifecycle, on storage the
> product owns. Keyless to boot; Python >= 3.12.

A stateless `Agent` core (tools, orchestration, streaming, fallback,
guardrails, structured output) with opt-in `Conversation` and memory
layers. One memory dispatcher serves five transports: the function tool,
Anthropic's native declaration, an MCP stdio server, the shell, and the
state process's HTTP wire. Agents share context by config: a `board`
mount on a task scope, and read-only `ConversationView`s of another
conversation with cross-conversation `recall_turn`. Long URLs, paths and
ids in aged turns become `[link N]` handles, expanded at the tool boundary.
Skills are documents under `skills/` in a mount: versioned, curated by
the mount flag, loaded by `list_skills`/`load_skill`, served as MCP prompts.
`McpServer` consumes any MCP server as agent tools, over stdio or HTTP.

## Learn from the shell

- `neosian docs`: list the shipped topics (they travel in the wheel, so
  they always describe the installed version).
- `neosian docs quickstart | agent | local | tools | memory | skills | cli | mcp | agents | topology | wire`
  prints one page, markdown on stdout, pipe-safe.
- `neosian docs topology`: who runs neosian code x where the bytes live;
  one writer per FileStore root.
- The same pages online, rendered from the wheel at the current release:
  https://docs.neosian.com (this file at https://docs.neosian.com/llms.txt).

## Set up from the shell (the human path)

- `neosian status [--json]`: is this machine set up? The home, which
  providers have a key (never values), this directory's two scopes, per
  client installed / MCP registered / hooks present / level (user,
  project, both) / interpreter resolving, the last recorded session, the
  install shape. Exit 0 always; findings are data.
- `neosian setup [--write] [--level user|project] [--root DIR | --url URL]`:
  wire every installed client (Claude Code, Codex, OpenCode, Muse Code, Cursor) to the home,
  once per machine: MCP + the record hooks in the client's own config, no
  file per project; print first. `--write` runs the client's own CLI for
  a file it owns (`claude mcp add-json`, `codex mcp add`) when on PATH,
  else prints the line and exits 1. `--url URL` moves every client to the
  state process in one run.
- `neosian configure --list | --provider NAME --key - | --env NAME --key - |
  --delete`: keys under `<home>/config.toml`, read from stdin, never argv;
  `--env` names a door by its key's env var (one an agent file registers).
- `neosian chat [PROMPT] [--model M] [--agent FILE] [--resume ID] [--json]`:
  the resident agent that knows neosian (a `docs` tool over these pages)
  on the same memory; a PROMPT or piped stdin is one turn, `--model fake`
  keyless. `[[chat.mcp]]` tables in `<home>/config.toml` (`name`, then
  `command`/`args`/`env` or `url`/`headers`, `prefix`) are MCP servers it
  opens for the session, their tools added. Bare `neosian` on a terminal
  opens it.
- `neosian update [--mode off|notify|auto]`: a PyPI check on the human
  verbs only, never on an agent verb; `auto` applies a uv tool install
  within the major.

## Operate memory from the shell

- `neosian memory view /`: the first command to try; renders the
  memory index. No flags inside a project means this directory's
  layout (`/user`, `/project`); `--scope S` or `NEOSIAN_SCOPE` names a
  scope. The store is the home, `~/.neosian` or `$NEOSIAN_HOME`, unless
  `--root DIR`, `--url` or the DSN names one.
- Six commands: view, create, str_replace, insert, delete, rename.
  `--json` prints the memory tool's result envelope verbatim.
- `neosian memory maintain`, the gardener: keyless dedup + empty-prune;
  `--model MODEL` adds the semantic pass (merge, prune stale, promote).
- A skill is `create /project/skills/<name>` with a frontmatter
  `description` and the instructions as the body (`neosian docs skills`);
  `versions` and `revert` work on it like any document.
- Operator verbs, keyless: `versions PATH` (the audit trail; `--json`
  carries full historical content), `redact PATH [--all]` (the one
  eraser; audit skeleton preserved), `revert PATH --version N` (undo).
- `neosian audit --scope S [--conversation C] [--actor A] [--since T]
  [--json]`, the ledger: what was done, by whom, when, newest first,
  identical on a root, Postgres, or the state process (`--url`).
- `neosian export DIR` / `neosian import DIR`: a store moves whole,
  history included, any substrate to any other; DIR is a FileStore root.
  An import needs every scope and conversation empty in the target.
- Exit tiers: 0 success, 1 ran-and-failed, 2 bad invocation, 130
  interrupt. stdout carries the artifact; stderr carries guidance; with
  `--json` in argv a tier-2 error is also one `{"error": "usage"}` object.
- Postgres arrives only via the NEOSIAN_POSTGRES_DSN environment
  variable (never an argv flag). `python -m neosian.memory` is the
  PATH-free twin.

## Upgrade to MCP

- `neosian mcp install --client claude-code|claude-desktop|cursor|codex|opencode`
  prints the exact registration; `--write` applies it (refused when the
  client is not installed). Once per machine by default: the entry names
  the store and no mount, the server derives each session's layout from
  the directory the client spawns it in (/user alone where that has no
  name); `--level project` writes this directory's file with its layout in
  the line (not for claude-desktop, cursor, codex: one file each). Claude
  Code's user scope and Codex are print-only, their own CLIs write those
  files: apply with the printed `claude mcp add-json --scope user` /
  `codex mcp add` line, or let `neosian setup --write` run it.
- `python -m neosian.mcp --root DIR --scope user:me` serves the same
  store over stdio: the `memory` tool, `list_skills` and
  `load_skill` over the mounts' skills (each skill also an MCP prompt, a
  slash command in Claude Code), and `recall_turn(turn, conversation)` for
  any recorded turn of any agent's session, verbatim.
- The other direction: `async with McpServer.stdio(cmd, args) as s:`
  (or `.http(url, headers=)`, `.in_process(server)`) from `neosian.mcp`
  gives the server's tools as `AgentConfig(tools=[*s.tools])`, schema
  verbatim, `is_error` in-band, `prefix=` for two servers that clash;
  the gate and hooks apply unchanged (`neosian docs mcp`).

## Record a foreign agent

- `neosian record install --client claude-code|codex|opencode|muse-code|cursor` prints
  the client hooks (UserPromptSubmit, PostToolUse, Stop, SessionStart;
  Cursor has five native events below; OpenCode has a plugin file). Once per machine by default: the line names
  the home and no mount, and each session gets `user:<login>` at /user
  and `user:<login>/proj:<slug>` at /project, derived from the client's
  project directory (Claude Code: `--project "$CLAUDE_PROJECT_DIR"`);
  `--write` merges them into ~/.claude/settings.json or
  ~/.codex/hooks.json, every other hook preserved, or writes
  plugins/neosian-record.js in OpenCode's config directory (Codex reviews
  a new hook once in /hooks). `--level project` writes this directory's
  file with its layout in the line; `--root DIR --scope S` override at
  either level. One level per client: clients merge hook sources, so a
  user-level `--write` removes this directory's old entry and a
  project-level install beside user-level hooks is refused.
- The hooks call `python -m neosian.record` with the payload on stdin; a
  prompt-to-stop span lands as one turn by `claude-code:<session_id>` in
  the conversation the session id names, plus a sessions document at
  /memories/sessions/<session_id>. Read it back: `neosian audit --scope S
  --conversation <session_id>`. On SessionStart the verb prints the memory
  index and "where we left off" (the recent sessions, log-projected):
  the client adds a hook's stdout to the model's context.
- Hooks beside an MCP server are two writers: use `--url` (the state
  process; `neosian setup --url URL --write` moves the whole machine) or
  Postgres. `neosian docs agents` carries the client table.

## Reach it over the network

- `NEOSIAN_SERVE_TOKEN=... neosian serve` runs the state process
  on the home (`--root DIR` another root): memory and conversations on a port, MCP
  over streamable HTTP at /mcp when started with mounts. Token is
  env-only (one token, or a per-client table `actor=token,...`, so the
  process records who wrote); unset refuses to start; /health is
  unauthenticated. Shell clients: `--url URL` + NEOSIAN_CLIENT_TOKEN.
- The shipped Dockerfile is the appliance: `docker run -e
  NEOSIAN_SERVE_TOKEN=... -p 6367:6367 -v state:/data neosian`.
- Cursor: `setup --client cursor --write` adds native version-1 hooks at
  ~/.cursor/hooks.json beside user MCP. Interactive CLI 2026.09.10-fd3934a
  records as cursor:<conversation_id>; --print lacks the full event stream.
  Completed turns wait for stop and afterAgentResponse in either order.
  sessionStart returns JSON additional_context. One workspace root selects
  /project; ambiguous roots use /user unless a project or scope is named.
  Imported Claude recorder calls carrying cursor_version are ignored.
  MCP credentials use ${env:NAME} references; hooks inherit the environment.
- Muse Code: `--client muse-code`; user MCP and hooks share
  $XDG_CONFIG_HOME/muse/settings.json (default ~/.config/muse), schema_version
  1; project MCP uses shared .mcp.json, hooks .muse/hooks.json (workspace
  trust required). Muse clears child environments: --url/Postgres hooks
  require user-level managed neosian-hooks.json and pass credential names
  through managed_hooks_env_vars; MCP uses ${NAME} env references. Another
  managed pointer is refused, never replaced. Shared project MCP survives
  user install; mcp_shadowed_by reports its override. Hook print mode is a
  files change map, with no unrelated settings. Actor muse-code:<session>.
- Python clients: `await RemoteStore.connect(url, token=...)` carries both
  storage ABCs over the wire, core install, drops in where FileStore
  does. Listings cross in pages of at most 500 rows, followed for you;
  `Pageable` pages them yourself. `neosian docs topology` carries the
  full shape.
- `neosian docs wire` is the HTTP contract a client in any language
  implements: the eighteen /v1/ routes, the envelope, paging, the JSON
  shapes, WIRE_VERSION.

## Bring an OpenAI-compatible model

- `register_model("acme-large", provider=OpenAICompatible(name="acme",
  api_key_env="ACME_API_KEY", base_url="https://llm.acme.example/v1"),
  ...)` is the day-one door for any model neosian has not shipped: once
  at import; the model prices in µ$ and passes every gate like a shipped
  one. `wire="chat"` (Chat Completions, the default) or `"responses"`
  (the Responses API, stateless: encrypted reasoning carried on the
  message between tool calls, nothing stored at the provider); the chat
  dialect knobs and `thinking_switch` beside it. `neosian docs
  quickstart` carries the snippet.
- A local server is the same door declared keyless:
  `OpenAICompatible(name="local", api_key_env=None,
  base_url="http://127.0.0.1:8080/v1")` reads no variable and sends the
  SDK a placeholder, never a key of yours; price it on a zero card
  (`ModelPricing(input_per_mtok=0, output_per_mtok=0)`, priced at zero
  rather than unknown). `neosian docs local` carries the llama.cpp and
  Ollama recipes (`llama-server -hf ggml-org/gemma-4-E4B-it-GGUF:Q4_0
  --jinja`, `ollama pull gemma4:e4b`) and the measured local row;
  `examples/local_agent.py` is the runnable form.
- Every shipped row is a `Model` member, the door rows too:
  `Model.GROK_4_6` (xAI `XAI_API_KEY`), `Model.GEMINI_3_8_FLASH` (Gemini
  `GEMINI_API_KEY`), `Model.KIMI_K3` (Moonshot `MOONSHOT_API_KEY`),
  `Model.QWEN_3_8_MAX` (Alibaba Model Studio `DASHSCOPE_API_KEY`): first-party, fingerprinted, no client of their own;
  each earned by green dispatched runs of the memory baselines and
  carrying its provider's clock (`spec.retires`, `spec.card_until`).
  OpenAI's rows (`gpt-5.6-sol` the default, `gpt-6-astra`) and xAI's
  speak the Responses API. A config takes the wire id too:
  `AgentConfig(model="gpt-5.6-sol")`.

## Install

- `uv add "neosian==1.0.0rc17"`: one package, the library and its
  provider SDKs, the `neosian` shell, the MCP server and client, the
  state process and OpenTelemetry spans (about 70 MB; nothing loaded
  until used). On PyPI as a pre-release until v1.0.0: pin it explicitly,
  never a default resolve. Keyless to start: `Model.FAKE` and `FileStore`
  need nothing. The one extra is the Postgres driver for `PostgresStore`,
  `neosian[postgres]` (`[all]` its alias).
- `curl -fsS https://neosian.com/install | bash` puts uv and neosian on a
  machine with nothing on it; the script says what it installs first.
- The appliance: `docker run -d -e NEOSIAN_SERVE_TOKEN=… -p 6367:6367
  -v neosian-state:/data ghcr.io/mausa-ai/neosian:1.0.0rc17`, the state
  process on a volume, `/health` the one open route.

## Docs

- https://github.com/mausa-ai/neosian/blob/v1.0.0rc17/README.md: install and quickstart.
- https://github.com/mausa-ai/neosian/blob/v1.0.0rc17/SERVICES.md: every environment key and what turning it off means.
- `neosian docs baselines`: the published per-provider memory numbers.
