car-server-core 0.53.0

Transport-neutral library for the CAR daemon JSON-RPC dispatcher (used by car-server and tokhn-daemon)
# car-server-core

Transport-neutral library for the [Common Agent Runtime](https://github.com/Parslee-ai/car) JSON-RPC dispatcher.

## What it does

Holds the JSON-RPC dispatcher, per-client session state, and the WebSocket channel plumbing. Used by `car-server` (the standalone WebSocket binary) and by any future embedder that wants to expose CAR's protocol over a different transport (e.g. `tokhn-daemon`).

The standalone `car-server` binary is a thin wrapper that loads `~/.car/env`, initializes telemetry, spawns the dream loop, binds a TCP listener, and on each connection calls `run_dispatch`.

## Library boundary contract

This library MUST NOT:
- spawn the dream loop (caller decides),
- initialize telemetry (caller decides),
- load `~/.car/env` (caller decides).

Those bootstraps stay in the embedder's `main`. The contract prevents the dual-memgine bug: if the library silently spawned its own dream loop, embedded users would end up with two memgine engines (the embedder's plus the library's).

The same boundary applies to watch-only self-healing detection: `ServerState`
owns the shared detector/ledger service and exposes `spawn_selfheal_cadence`,
but only the standalone `car-server` starts that task. Embedders may start it
explicitly or call `selfheal.run`. The default ledger is
`<CAR_HOME>/selfheal/detections.jsonl`; it is private and append-only. Each tick
also checks only its configured/bounded source candidates for a `.git` origin
matching `Parslee-ai/car`. A match creates or updates a trusted-tier handoff at
`<CAR_HOME>/selfheal/issues/<dedup-key>.md`; absent or refused candidates stay
ledger-only, and no path writes into the checkout. This subsystem has no
network, off-machine filing, proposal execution, restart, or remediation path.

## Lock primitive

`ClientSession.memgine` uses `Arc<tokio::sync::Mutex<MemgineEngine>>` per the "one-wrapper rule" — dispatcher handlers can hold the lock across `.await` points without risking poisoning, and tokio's `Mutex` does not poison so a panicking handler does not poison the engine for sibling connections.

## Embedder usage

```rust,ignore
use car_server_core::{ServerState, ServerStateConfig, run_dispatch};

// Build the shared server state.
let state = ServerState::embedded(ServerStateConfig::default()).await?;

// On each accepted WebSocket connection:
tokio::spawn(async move {
    run_dispatch(ws, state.clone()).await;
});
```

## The assistant (`car-server-core::assistant`)

The flagship batteries-included general agent behind `car do` — one-shot, REPL,
or `--serve` conversational.

- A `GeneralExecutor` (generalizes `coder::WorktreeExecutor` to any bound
  `Substrate`, sharing one bounded `run_shell_on`) exposing files, a real shell,
  `http_request` / `web_search`, `calculate`, and durable `remember` / `recall`
  (memgine, `~/.car/memory/assistant.json`).
- Driven through a full `Runtime` — validator, policy, permission tiers, event
  log. Sandbox-first (`car-sandbox`, `--network none`), with `--local` /
  `--full-access` to widen it.
- Conversational via the native `agent.chat` harness (`chat::AssistantService`,
  the first native Rust agent-side). Auto-registered in CarHost as
  `parslee-core` (`car-assistant` remains a server-side alias for
  already-registered installs and raw WS integrators — see
  `assistant::register`).
- Tool *descriptions* are the single source of truth for what a tool does; the
  system prompt (`assistant::prompt`) carries only posture and the rules no def
  or policy expresses, and never re-enumerates the toolset.
- A per-turn **`<runtime-state>` block** carries live run state — the
  `todo_write` task list and the subjects of facts written this run — so state
  reaches the model whether or not it thinks to `recall`. It is appended to the
  *last* message's content rather than added as a trailing `Message::System`,
  because the Anthropic and Gemini handlers fold System messages into the
  top-level system field and a trailing one would land in the cached prefix and
  be rewritten every turn. It never enters durable history, and renders nothing
  when there is nothing to say. Run *constants* — working directory, approval
  tier — belong in the system prompt's `Environment:` sentence instead, which
  compaction pins; putting them here would re-send them every turn and invite
  drift from the substrate's own description.

### What the assistant learns

Three mechanisms, and they are not the same thing — conflating them is how an
agent gets described as "learning" when it only remembers.

1. **Durable facts about the user**`remember` / `recall` (`assistant::memory`),
   model-initiated, persisted to `~/.car/memory/assistant.json` and re-ingested
   at startup. Recall runs memgine's **full** retrieval path, not Fast: skill
   lookup, PPR fact scoring, inline repair, known-unknowns. On `car do --serve`
   it mirrors into the synced Knowledge oplog so facts converge across devices.
2. **Reactive trajectory pressure** — the per-turn proactive-memory pass
   (`agent_loop::maybe_apply_assistant_proactive_memory`), host-side and
   deterministic. It folds the last 32 events of the durable receipt journal
   into proactive facts and may inject one reminder before the next model turn,
   so high-value memory reaches the model whether or not it thinks to `recall`.
   Its derived facts live in the in-memory graph only — they are recomputed from
   the journal each process rather than accumulating, which is why the journal
   being durable is what makes this cross-session at all.
3. **Learned tool repairs**`assistant::tool_memory`, the "gets better over
   time" half and the one that was missing. A tool fails, the same tool succeeds
   within `RECOVERY_WINDOW_TURNS`, and the call that recovered is captured
   against a coarse failure signature (`shell::missing_target`). A later run
   hitting that signature gets it back as a `## Learned Repairs` hint on the
   turn after the failure. Repairs are memgine skills, so a lead that keeps not
   working auto-degrades and stops being offered; they persist in their own file
   (`~/.car/memory/assistant-repairs.json`), deliberately NOT in the note store,
   whose format is shared with the MCP server and whose contents the user's own
   `recall` reads. Off unless a surface sets `AssistantConfig::tool_memory`;
   `car-bench` leaves it unset on purpose.

What is still *not* wired on this path, so nobody has to re-derive it:
`MemgineEngine::reflect()` (user corrections → high-confidence facts) has no
assistant call site — it needs inference attached to an engine that deliberately
has none — and the self-evolution governor operates on the daemon's shared
session engine, not this one.

### Verified goals

Live surfaces: `car do --until`, `car do --infer-until`, `agents.chat { goal }`,
`goal.suggest`, `goal.set` / `status` / `clear`, declarative-agent
`goal { check, max_iterations }`, and `goal_evaluated` events. (The macOS Chat
verified-goal strip and iOS push-to-talk both ride this path for chat-capable
agents.)

The goal loop requires `met && grounded`. Final-summary operational claims like
"tests passed" are cross-checked against same-run tool receipts: an unmatched
claim fails a model-judge-dependent verdict closed, while on a deterministic
pass it only annotates the reply with a `[claim check]` note — **completion is
decided by the receipts, not the prose.**

### Declarative agents

They advertise `capabilities: ["chat"]` and are reachable through `agents.chat`
in-daemon, with no spawned child process. Host `agents.chat*` is WS-only;
agent-side FFI exists via `registerChatHandler` / `register_chat_handler` and
`chatEvent` / `chat_event`.

## Provisioned coder benchmark

The deterministic native scorecard runs in ordinary tests. Comparing it with a
real external foreman spends CLI quota and therefore requires both an exact
opt-in and a pinned ready engine:

```bash
CAR_CODER_BENCH_LIVE=1 CAR_CODER_BENCH_ENGINE=codex \
  cargo test -p car-server-core coder::bench::foreman_vs_native_live \
  -- --ignored --exact --nocapture
```

The task table and thresholds are fixed in source. The lane fails when the
pinned engine is unavailable or the threshold is missed and emits a
`car.coder.foreman-bench.v1` JSON receipt.

## Where it fits

The full method reference is in [`docs/websocket-protocol.md`](../../../docs/websocket-protocol.md) — that document describes the wire format produced by this crate's dispatcher.