# Changelog

All notable changes to `@trymesh/cli` are documented here. This project follows
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## 0.4.104 — 2026-06-12

Static-analysis cleanup pass (DeepSource grade). Removed ~100 lint findings
across 51 files — unused imports / variables / catch params, unnecessary regex
escapes, and small anti-patterns (`new Array(n)`, useless spreads, `indexOf`→
`startsWith`) — via oxlint + biome safe auto-fixes. No behaviour change; the full
test suite stays green. Also fixed a real one: the turn tracer logged every
aborted turn as `completed` and always passed `undefined` for the error message
(`turnErrorMessage` was declared but never assigned) — aborted turns are now
recorded correctly. Added a `.deepsource.toml` (analyzer config, test patterns,
build/vendored excludes); the `no-control-regex` findings are false positives for
a terminal app that legitimately matches ANSI/control characters.

## 0.4.103 — 2026-06-12

A patch storm no longer punts the edit into pasted code. When `patch_surgical` /
`patch_file` keep mismatching their search block by a few characters and the
patch-storm guard trips, mesh used to block ALL edits to that file (write_file
included) and tell the model to "summarize what was blocked" — so it pasted the
diff into chat with "once the lock is lifted we can apply it" and the requested
change never landed. Now the storm STEERS to a whole-file `workspace.write_file`
(the one edit path that doesn't depend on matching a search block; its own
content-loss guard still blocks dangerous rewrites) and keeps editing alive, so
the change actually applies this turn. A true text-summary hard-stop fires only
if write_file itself storms. Also fixed the garbled separator (`?` → `→`) in the
convergence / early-exit / error-storm gate labels.

## 0.4.102 — 2026-06-12

Honest edit diffs + a `.mesh` that stops polluting git.

- **Edit diff display.** A `write_file` to an existing file was always labelled
  "Create" and counted the *entire* new file as additions — so a 5-line tweak to
  a 34-line `package.json` rendered as "create package.json +100". The view now
  reads the file's pre-write content, so it shows the real verb (Update) and a
  real ±N (a true LCS line-diff, e.g. +5 −4), with the hunk showing only the
  changed lines. Local-only; the pre-image is never sent to the model.
- **`.mesh` is now git-ignored.** Mesh drops a self-ignoring `.mesh/.gitignore`
  on init. `.mesh` is a per-machine cache (capsules, embeddings, transcripts,
  dashboards) — its normal churn was surfacing as walls of tracked-file changes
  (one session deleted 132 tracked `.mesh` files). If your repo already tracks
  `.mesh`, run `git rm -r --cached .mesh` once to stop tracking it.

## 0.4.101 — 2026-06-12

The approval card can no longer freeze the session. It now clears the instant
you choose (y / a / n) instead of lingering until the whole turn ends — so if
the next tool stalls, the card no longer looks frozen and y/a no longer appear
to do nothing. And Ctrl+C now escapes a pending approval (deny + abort): the
approval key-handler used to `return` on every key, silently swallowing Ctrl+C,
so a stuck approval was unkillable. Pairs with a proxy-side fix that stopped
`gemini-embedding-001` (the 0.4.99 default) from 500-ing on the Vertex path and
degrading semantic search to 0% — the stall that exposed this in the first place.

## 0.4.100 — 2026-06-12

Autopilot no longer spins. In `auto` mode the auto-continue loop now stops the
moment a turn stops making real progress — no edit landed and no open step
closed, or it merely repeats its previous answer — instead of burning the whole
turn budget re-describing the same plan (the degenerate small-model loop seen on
gemini-3.5-flash). It also detects the user's language so the continued turns no
longer drift from German to English, and the continue-prompt now forces the next
concrete step ("do it now, don't re-plan; reply 'Done' when finished") rather
than inviting another summary.

## 0.4.99 — 2026-06-12

Embedding/indexing reliability. The default embedding model is now
`gemini-embedding-001` (GA) instead of `gemini-embedding-2` (preview) — the
preview model carries a tiny per-minute quota that 429-storms and stalls
`/index` on real repos; the GA model has the headroom to index without choking.
Local embedding throughput is paced and parallelized with bounded concurrency,
with new knobs `MESH_EMBED_CONCURRENCY` (default 8) and
`MESH_EMBED_RECORDS_PER_SLICE` (default 16). Override the model anytime via
`MESH_EMBEDDING_MODEL`.

## 0.4.98 — 2026-06-11

Approval prompts that actually fire, an agent that stops handing work back, and
real autonomy — the autonomy & error-reduction pass (plan 0.4.98). Built on the
forensics of CALOR session `3c46eb0c` (where mesh wrote two files, failed to
patch a third, gave up, and pasted the corrected code into the chat for the user
to copy by hand — with no approval prompt ever shown). Requires `@trymesh/core`
`0.2.3`.

### Fixed
- **Allow-once / always-allow now actually appears.** The Ink TUI showed
  permission mode "ask" while the agent internally defaulted to
  auto-approve-everything (the TUI never synced its mode on mount), so edits
  landed silently and no prompt was ever drawn. The TUI now syncs the agent's
  permission mode; in the default mode every file change / command prompts with
  **Allow once · Always allow (stop asking) · Deny**.
- **Mesh no longer pastes code into the chat for you to copy.** A failed
  `patch_file` / `patch_surgical` used to throw a bare error with no file
  content, so the model retried from a stale view, gave up, and dumped the
  corrected files into its answer. Patch failures now **self-heal**: the tool
  returns the file's exact current bytes (or a symbol outline + read pointer for
  a large file), so the model builds a correct patch — or writes the corrected
  full file — on the very next call. A failed patch no longer draws a phantom
  "changed file" row, and a self-healed failure still feeds the per-file
  patch-storm guard.

### Added
- **Continue-on-announce (A1).** When the final answer PROMISES deferred work
  ("als nächsten Schritt sollten wir…", "das können wir im Anschluss tun",
  "next I'll fix…") on a non-question edit turn with open work, the turn no
  longer ends — mesh is forced to apply the announced change now and verify.
  This is the fix for having to type "ja mach das" three times in a row. Capped
  at 3 nudges/turn; never fires on a question, after a patch-storm stop, or once
  verification has passed.
- **Autopilot across turns (A2/A3), `auto` mode only.** In the hands-off `auto`
  permission mode, mesh keeps continuing the task after a turn ends — working
  the goal's remaining open steps and running the project's tests/typecheck
  (drive-to-green) — until the goal is complete, a real blocker appears, or the
  per-submit budget (`MESH_AUTOPILOT_MAX_TURNS`, default 6) is spent. Each
  auto-continued turn is marked `autopilot ▸ continuing` in the transcript;
  Ctrl+C stops the loop.
- **Safe output redirection.** `run_command` now allows a single trailing
  redirect to a workspace-relative file (`cmd > out.txt`, runs via `sh -c` with
  cwd pinned to the workspace) — it is no more dangerous than `write_file`.
  Input redirection (`<`), append (`>>`), fd/dup redirects (`2>`, `&>`, `>&`),
  and any target that is absolute, uses `~`, contains a space, or escapes via
  `..` stay blocked. (Core `0.2.3`.)

### Changed
- The edit-discipline system prompt now tells the model to finish every item of
  a multi-item request in one turn and never to announce future work instead of
  doing it — reinforcing A1.

## 0.4.97 — 2026-06-10

TUI input/render fixes, a quieter & cheaper Q&A path, autonomous post-edit
verification, and honest dashboard numbers — from a second forensics pass over
the 0.4.96 dogfood sessions.

### Fixed
- **Multi-line paste no longer floods the agent.** Bracketed paste (`?2004h`) is
  enabled; a pasted block is captured between the `\x1b[200~`/`\x1b[201~` markers
  and inserted as ONE editable input instead of submitting a turn per embedded
  newline (which was also un-abortable). Disabled again on exit/suspend.
- **Ctrl+C clears the input when idle.** With typed-but-unsent text and no turn
  running, the first Ctrl+C clears the composer (like any shell); it only arms
  the exit prompt when the composer is already empty. While a turn runs it still
  aborts first.
- **TUI glitch / tearing / jumping.** The full-frame redraw is now wrapped in
  synchronized output (DEC 2026) so the terminal paints each frame atomically,
  identical frames are skipped (de-flicker), and a trailing `\x1b[J` clears
  leftover rows. The thinking spinner animates smoothly again.
- **Live status shows what mesh is doing, not "model call".** The working
  indicator reads `thinking · last: read X · step 3/8` / the running tool / the
  streamed-char count, instead of freezing on the internal "model call" /
  "tool complete" label between hops.
- **Dashboard "Saved" matched reality.** It summed only the tiny artifact-clamp
  estimate (≈hundreds of tokens) while the CLI status line showed thousands. The
  per-turn metric now records the TOTAL saved estimate (prompt-cache + truncation
  + compaction + artifact) and the cached-token count; the dashboard reads it and
  prices cached input at 0.25×. Model bars are clamped to 0–100%.

### Changed
- **Code changes collapse below the final answer.** After a fix, the live
  per-file edit cards are consolidated at turn end into one `✎ changed N files`
  summary (Ctrl+E expands the diffs) placed ABOVE the prose answer — so the
  answer sits at the very bottom instead of under a stack of expanded diffs.
- **Small Q&A is much cheaper.** History is now task-aware: a short question caps
  prior-transcript context (trivial 6K / search 16K tokens) instead of dragging
  the whole accumulated session on every round-trip (the "1-line question costs
  90K tokens, 30s" pathology); edit/reasoning turns keep a large window (96K/160K).
  The session capsule carries older context and the last 2 turns are always
  retained. Override with `MESH_HISTORY_BUDGET`.
- **Autonomous verification after edits.** The verify-before-done gate now fires
  for ANY turn that patched a source file (was limited to six workflow labels, so
  many "fix X" turns finished untested) and names the project's detected verify
  command. Pure docs/markdown edits are exempt.

## 0.4.96 — 2026-06-10

Forensics pass over a real dogfood session (date-fns monorepo): the gate
cascade that killed tasks, indexing that lost all progress at the wall-clock
budget, a grep-rendering bug that blinded Q&A, and the prefix instability that
kept prompt-cache savings near zero.

### Fixed
- **Q&A grounding: grep results rendered EMPTY.** `extractPrimaryContent`
  mapped matches as `{file,text}`, but both grep tools emit `{path,snippet}` —
  the model received lines like `?:31: ` (no file, no content), concluded the
  search returned nothing citable, and refused to answer. Both shapes are
  accepted now, plus an honest `[+N more matches]` footer.
- **Indexing survives the wall-clock budget.** `rebuild()` persisted only after
  the FULL embedding pass, so the 300s `mesh index` budget discarded everything
  on big repos (observed: 10,552-slot repo dead at 88 embeds; every session
  restarted from zero and died identically). Now: the lexical/BM25 index is
  persisted as soon as the file pass completes, the embedding pass checkpoints
  every 25s, and a cooperative deadline (20s before the hard kill) ends the run
  as a usable PARTIAL index. The status is "partial" (not "failed"), and
  session start auto-requeues the background job (10-min cooldown) until
  coverage completes — embeddings converge across sessions.
- **`/start` (readline mode) no longer runs a foreground full index** — it
  queues the background job like the TUI path. Escape hatch: `/start wait`.
- **Whole-file-rewrite economy gate no longer hard-blocks.** The ≥70%-unchanged
  rejection made the model shrink its diff by REMOVING the intended fix to get
  under the threshold — shipping a regression. Writes always land now; the
  gate became a steering note toward `patch_surgical`. The content-loss gate
  (≥2 silently deleted definitions) stays hard, with self-healing grounding.
- **Tool-spam converge gate counted blocked calls as spam.** Safety-blocked and
  failed calls no longer count toward the threshold (the error-storm breaker
  owns that case), the non-edit entry threshold is raised 7 → 10, and the soft
  nudge allows 2 targeted follow-up reads instead of forbidding all further
  tools — investigation turns can finish instead of being forced into an empty
  "nothing verified" answer.

### Changed
- **`run_command` accepts pipelines and sequences** (`rg pattern | head -20`,
  `pnpm build && pnpm test`, `a; b`, `a || b`). Every segment is validated
  against the same allowlist / eval-flag / git-revert rules, then the validated
  string runs via `sh -c`. Redirection (`>`/`<`) and `&`-backgrounding stay
  blocked — with actionable messages, as does inline eval (now points to the
  script-file alternative). Requires `@trymesh/core` ≥ 0.2.2. The old blanket
  "shell control operators are not allowed" sent models into 3–5-call blocked-
  workaround spirals (`node -e`, helper scripts) that then tripped the spam
  gate and ended turns with "mache ich im nächsten Schritt".
- **Stable cache prefix is default-ON** (kill-switch `MESH_STABLE_CACHE_PREFIX=0`).
  Volatile per-step blocks (runtime context, active directives) no longer ride
  inside the system message; they're carried as an ephemeral trailing context
  message instead. The system+tools prefix is byte-stable turn over turn, so
  provider-side implicit caching engages on every step after the first.
  Baseline that motivated this: a 183K-token-in session with 0 cached tokens
  and 9% total savings.

## 0.4.95 — 2026-06-10

TUI fixes, full-toolset access, stale-cache invalidation, leaner `.mesh`, a
shutdown-hang fix, and honest savings counting.

### Fixed
- **TUI `/start` no longer breaks the terminal.** It ran the raw-stdout,
  enquirer-prompt, foreground-index `runStart` inside the alt-screen, corrupting
  it and stranding you in indexing. `/start` is now TUI-native: brief doctor →
  background index → status card.
- **`/status` no longer dumps raw `git status`.** The card pasted the full
  porcelain output into its headline; now shows a compact `(ΔN)` change count.
- **Interactive pickers restored.** Bare namespaces (`/tui`, `/config`, …) open
  an arrow-select picker instead of printing a static list.
- **Thinking indicator moved into the chat** (live spinner + elapsed + streaming
  chars where the answer forms) and **out** of the input-box border; removed the
  turn-end "Turn complete" toast that distorted the composer.
- **Shutdown hang / double Ctrl+C.** Long sessions left handles (MCP children,
  websocket, watchers) keeping the loop alive after "Session saved…", so the
  process hung until a second Ctrl+C. Now force-exits cleanly; the signal handler
  prints immediately, has a watchdog, and hard-exits on a second signal.
- **Stale caches resurfacing fixed bugs across sessions.** Path-less grep/search
  results (artifact-memory) and RAG query rankings are now invalidated by the
  **git HEAD** — a commit/checkout makes them stale instead of replaying old
  findings. The orphaned `/tmp` read-cache is deleted on init.
- **Savings counting accuracy:** `read_multiple_files` byte savings are now
  counted (was 0 — the largest gap), and artifact savings/metrics are recorded
  once per turn (`recordTurnMetrics` was firing twice → ~2× artifact over-count).

### Changed
- **The model gets the full standard toolset every turn** — read, search,
  `write_file`, `patch_file`, `run_command`, `validate_patch`, `git_diff`. It was
  withholding `run_command`/edit tools by detected "intent", so "run the tests"
  had no way to run anything. Only exotic tools (timelines, runtime autopsy,
  multi-agent, company/causal memory, UI) stay intent-gated. Restraint is now
  enforced by the system prompt, not by hiding tools.
- **`/view` removed; `/dashboard` is top-level** (dropped inspect/preview/chatops).
- **`.mesh` no longer floods `capsules/read`.** The index stopped bulk-prewarming
  a read-capsule for every file (thousands of never-read locale files); read
  capsules are now lazy, built on the first real `read_file`
  (`MESH_PREWARM_READ_CACHE=1` to opt back in).

## 0.4.94 — 2026-06-10

Big RAG + capsule + `.mesh` reliability pass (cursor-improvement-plans 1–4).

### Added
- **Binary vector sidecar** (`vectors.bin`). Embeddings moved out of `index.json`
  into a compact binary file beside the index. This sidesteps V8's ~512MB string
  limit that, on large repos, silently dropped vectors and collapsed semantic
  search to keyword-only until a manual `/index repair`. Vectors now survive cold
  start at any repo size; `index.json` stays small.
- **`/mesh gc [--dry-run|--apply]`** — reclaim cache/ephemeral/old-trace files
  under `.mesh` (dry-run by default). Doctor's GC suggestion now points at it.
- **Quarantine-on-boot** — corrupt `.mesh` JSON is moved to `.mesh/quarantine/`
  during init instead of failing the session.
- **`.mesh` disk visibility** — auto-maintained `manifest.json`, a three-root
  storage map in `/debug doctor`, and disk breakdown in `/status full`.
- **Inject dedup** — briefing / co-occurrence / speculative prefetch no longer
  ship the same file into context twice in one turn.
- CI: `check-core-version.cjs` guard (declared-range + npm-published vs workspace
  core) and a `publish-core.yml` workflow.

### Changed
- **`grep_capsules`** scans the in-memory index capsule surface instead of
  stat+reading up to 10k files from disk on every call.
- **`ask_codebase`** now applies the same hybrid lexical rerank as
  `semantic_search` (reconciled — no double-counted lexical signal).
- **RAG query cache** loosened 0.95 → 0.88 but guarded by token-overlap +
  index-version + TTL + best-match, so a looser bar can't serve stale/wrong
  results; legacy cache entries still require the strict bar.
- **Retrieval gate** now also nudges on *low-confidence* hits ("read before you
  assert/edit"), not only empty results.
- **Read capsule cache** moved from `/tmp` to `.mesh/capsules/read/` (survives
  reboot; opt back into tmp with `MESH_READ_CACHE_IN_TMP=1`).
- **Auto-compact** thresholds now track the actual model context window
  (`modelInputTokenLimit`) instead of a flat 800k default.
- **Tool-result ledger** wired into the context assembler: identical tool
  outputs are deduped and reads superseded by an edit are demoted in the prompt.
- Unified the file **purpose** generator so index and read capsules describe a
  file identically; briefing drops the reverse-index for Q&A/locate prompts.
- Moonshot `.mesh` dirs are now classified with age-based GC rules.
- Removed the always-on AST-capsule warm-up (parsed up to 1000 files per session
  via ts-morph + an fs.watch, but nothing read the store).

### Fixed
- Status report no longer fails to typecheck on the ledger savings field.
- Ledger stale-read eviction now matches paths correctly (shared path
  normalization) instead of silently no-opping on `./` / doubled-slash forms.
- `inject-log.jsonl` no longer grows unbounded (dedup is in-memory per turn).
- Artifacts `index.json` is excluded from age-GC (deleting it would have wiped
  every artifact blob on the next run).
- `publish-core.yml` uses `pnpm publish` (not `npm`) so core's `workspace:*` dep
  is rewritten at pack time; both publish workflows trigger on `master`.

## 0.4.93 — 2026-06-10

### Changed
- **Slash commands consolidated: 62 → 19 (hard cutover).** The command surface
  grew to 62 top-level entries. It is now 11 plain actions
  (`/help /plan /change /fix /undo /compact /model /goal /start /clear /exit`)
  plus 8 namespaces: `/status`, `/tui` (color, statusline, copy, export, search,
  keys, tools, status), `/config` (setup, approvals, steps, voice, daemon,
  entangle, resume, sync, reset, debug), `/memory` (show, brain, company, learn,
  distill, twin), `/debug` (doctor, index, bisect, replay, whatif, hologram,
  causal, repair, sheriff), `/ship` (autopilot, issues, intent, fork,
  production, audit), `/view` (dashboard, inspect, preview, chatops), and `/lab`
  (run, ghost, synthesize, tribunal). The taxonomy in `command-taxonomy.ts` is
  the single source of truth for the menu, palette, and autocomplete; dispatch
  translates each new command back to its existing handler, so behavior is
  unchanged — only the names moved. Old names are hard-cut: typing one (e.g.
  `/cost`, `/doctor`) prints a one-line "moved to …" notice. (Headless
  `mesh /<old>` still resolves legacy names.)
- **`/status` is now one card.** `/cost`, `/savings`, and the new context/budget
  view are merged into a single `/status` card (model, context budget + ratio
  bar, auto-compact status, tokens, cost, savings). `/status full` opens the
  detailed overlay.

### Added
- **Model-aware auto-compaction.** A `ContextBudgetManager` replaces the fixed
  250-message / 600k-char trigger with ratio-based thresholds against the actual
  assembled prompt size and model window (warn 60% / soon 75% / now 85% / block
  92%). Override with `MESH_AUTO_COMPACT_RATIO`.
- **Managed `.mesh` state.** `/doctor` now reports `.mesh` health — total size,
  per-kind rollup, corrupt JSON state files, and dangling artifact references —
  and offers garbage collection of regenerable/old state while never touching
  human-authored files (`mesh-state.ts`).
- **Tool activity is separated from the chat transcript** so a 30-tool turn no
  longer creates 30 chat items; routine successful tools live in an activity log
  with the live tool shown in the status area.

### Fixed
- **Agent grounding — no more "may I read the file?" stalls.** When a whole-file
  rewrite is blocked (it would delete named definitions or is a disguised small
  edit), the rejection now ships the file's current content (or, for large
  files, a symbol outline) back to the model so it patches immediately, instead
  of asking the user for permission to read. The system prompt gains an explicit
  read-before-edit rule (reads are free and auto-approved — never ask) and a
  rule to ground improvement/refactor suggestions in files actually read, not in
  filenames. (`local-tools.ts`, `system-prompt-builder.ts`, `patch-strategies.ts`)
- **Pasted-image payloads** are converted to attachments instead of dumping raw
  base64 into the prompt, and image blocks are no longer mis-counted as ~200k
  text tokens by the context estimator (which falsely tripped the context wall).

## 0.4.92 — 2026-06-10

### Fixed
- **Self-revert death-loop — the agent no longer discards its own edits.** Root
  cause of the "all edits were wrong / nothing landed" sessions (e.g. CALOR
  aaf81481): the model would `write_file` a fix, then immediately
  `git checkout <file>` to "start clean" — reverting the edit straight back off
  disk — and repeat that 3–4× per file. No edit-quality gate caught it because
  the revert went through `run_command`, not a patch tool, so the workspace
  ended byte-identical to the start while the model reported "done". The command
  allowlist now blocks the working-tree-discard forms — `git checkout <file>`,
  `git checkout -- <paths>`, `git checkout .`, `git restore` (worktree), and
  `git stash`/`git stash push` — with a message steering the model to
  `patch_surgical`/`write_file`. Branch operations (`git checkout <branch>`,
  `-b`, `origin/main`, tags) and safe forms (`git restore --staged`,
  `git stash pop`) are untouched. Default-ON; override with
  `MESH_ALLOW_GIT_REVERT=1`. (`packages/core` command-safety)
- **open_artifact token churn.** Gemini 3.x reflexively re-opened the same
  artifact after nearly every tool call (38× in one session), re-shipping
  identical content at full token cost despite the tool result already being in
  context. Exact-repeat opens (same id+query+mode) now return a short stub
  pointing at the content already in the conversation; a re-open with a
  different query/mode still serves fresh content. A direct contributor to the
  multi-million-input-token cost on long sessions.

## 0.4.91 — 2026-06-10

### Changed
- **TUI transcript restyle** — the ╭─/│/╰─ rails around every message are gone
  (three frame glyphs per line made the chat read like nested boxes). Messages
  now render as one compact header (`● mesh · time`, `❯ you · time`) with the
  body indented beneath. System notices (permission mode, trims, resume hints)
  are a single dim `›` line instead of a full bordered SYSTEM block, and
  Shift+Tab permission-mode changes are a transient toast, not a permanent
  transcript entry. Short command confirmations ("/dashboard completed.")
  collapse to one `✓` line. The startup banner lost its surrounding box.
- **Readable code, less of it** — code blocks in replies are no longer all-dim
  gray; they get a minimal syntax tint (keywords cyan, strings green, comments
  dim). Edit diffs preview 4 lines instead of 12, and file *creations* show no
  body at all by default — the header's `+N` says it; Ctrl+E still expands.
- **Statusline made trustworthy** — duplicate components (mode/approvals twice)
  are deduped; the tool counter is session-cumulative instead of zeroing at
  turn end (the "0 ok / 0 err │ 5 err" contradiction); the per-turn error chip
  is labeled `turn N err`; dollars show 2 decimals ($0.28, sub-cent keeps 3)
  instead of $0.2828 fake precision.
- **Dashboard rebuilt around the session, not vanity panels** — dependency
  hubs (all zeros), workspace file census, regex "risk hotspots", the raw
  TOOL_START feed, and the 16% "efficiency" KPI are gone. In their place: an
  activity timeline parsed from the session transcript (per turn: prompt,
  tool calls humanized, edits with +/−, reply), a files-changed rollup, honest
  KPIs (tokens, cost, saved, turns), one slim context-window bar, and a GLOBAL
  tab that actually aggregates `context.jsonl` (totals, 14-day usage, per-model
  spend, recent sessions). Calmer visual design throughout.

### Fixed
- **Stale-rewrite protection (the CALOR lesson)** — when a surgical patch fails
  (stale search block), a follow-up `write_file` on the same file in the same
  turn is now blocked with re-read-and-retry instructions. Previously the model
  "recovered" by regenerating the whole file from its stale view — silently
  deleting URL-resolution helpers, breaking the frontend request contract, DB
  column names, and an email call signature while `npm run build` stayed green.
- **Rewrites can't silently delete code** — `write_file` over an existing file
  is rejected when the new content drops ≥2 named definitions entirely
  (definition + every reference), listing what would vanish. Deliberate
  removals stay possible via `patch_surgical`, where each deletion is an
  explicit diff.
- **Redundant-verify damper** — the 4th `git diff`-family call in a single turn
  (diff → diff --stat → diff -U1 | head → …) is stopped with "use the output
  you already have"; non-git commands only collide on exact repeats. One
  session burned 9 diff inspections on an unchanged tree.
- **Patch failures now steer recovery** — the "search block not found" error
  explicitly says: re-read, retry surgically, do NOT fall back to a whole-file
  rewrite.
- **Edit discipline in the system prompt** — four rules distilled from the same
  session: failed patch ⇒ re-read (never rewrite); grep callers before changing
  signatures/response shapes/DB columns; never silently remove functionality
  (name every removal); a passing build is NOT verification.

## 0.4.90 — 2026-06-10

### Fixed
- **Honest, consistent cost & savings** — the displayed "saved tokens" was
  inflated 4.2× by a display multiplier and is now the real figure. `/cost`, the
  header box, and the status line read one shared source, so they no longer show
  different numbers. Switching models mid-session no longer reprices the whole
  session — each model's tokens are valued at its own rate.
- **No more throwaway `dummy.txt` files** — on a pure question/analysis turn the
  agent could be forced to emit a tool call and would write a junk file to
  satisfy it. The forced-tool gate and the system prompt no longer compel a tool
  call when a prose answer is all that's needed.
- **Edits no longer silently revert** — an edit turn that patched a file and then
  reverted it (net-zero) used to end with nothing applied, forcing the user to
  ask "are you done?". It now re-applies the change once instead of stopping.
- **Question turns converge faster** — pure questions no longer fan out into 12+
  redundant read/grep calls before answering (gate lowered to 7 for non-edit
  turns).
- **Quieter, clearer status line** — the "Transcript trimmed: removed N older
  message" spam is coalesced and throttled to near-silence; the first-message
  "plan" segment is gone; the header git label now reads `branch <name> · clean`
  / `· ●N`.

### Changed
- **Proxy: per-model Vertex region routing** — Gemini and `*-preview` models are
  hard-pinned to `location=global` (they 400 on regional endpoints), keeping
  `gemini-3.1-pro-preview` reachable on every dispatch path. Upstream 400/404s
  now return an actionable error (model id, provider, region) instead of a bare
  status code.

## 0.4.88 — 2026-06-10

### Changed
- **Embeddings are proxy-only by default** — a stale/invalid local
  `GEMINI_API_KEY`/`GOOGLE_API_KEY` in the user's shell used to hijack the
  embedding fallback (HTTP 400) and silently break indexing. Local BYOK keys are
  now ignored unless `MESH_EMBED_ALLOW_LOCAL_KEY=1` is set; everything routes
  through the Mesh proxy.

### Fixed
- **`/index repair` now sticks** — repair computed coverage over just the
  re-embedded subset and compared it to the full prior index, so the guard
  discarded every repair and coverage was pinned (the "49% forever" bug). Repair
  is additive and now persists the strictly-more-complete index, reporting
  coverage over the full file set.
- **Repair never regresses** — a failed embedding (provider 4xx/timeout) no
  longer overwrites a previously-good vector with an empty one.

## 0.4.87 — 2026-06-10

### Fixed
- **Indexing no longer hangs forever** — the shared-proxy embedding path could
  stall a rebuild for 20+ minutes under rate-limiting (nested 429 backoff loops
  with no ceiling), leaving `.mesh/workspace.json` stuck at `status:"indexing"`
  with a live pid that then blocked every future `/index`. Added a per-call
  proxy wall-clock budget (`MESH_EMBED_PROXY_BUDGET_MS`, default 60s) and a
  per-rebuild latch so once the proxy rate-limits, remaining slices go straight
  to the local Gemini key.
- **Background index can't wedge the lock** — an overall job timeout
  (`MESH_INDEX_MAX_MS`, default 300s) force-fails a stuck rebuild with
  `status:"failed"` instead of hanging, and the "already running" guard now
  reclaims a hung-but-alive job after a stale heartbeat (180s) instead of
  blocking new runs indefinitely.
- **Degraded rebuild persists** — a smaller high-coverage prior index no longer
  blocks a structurally more complete (more files) rebuild from being saved.

## 0.4.86 — 2026-06-09

### Fixed
- **`/index status` while job runs** — no more contradictory “No index built”
  alongside “Scanning 100%”; live progress is the primary display.
- **Duplicate background index jobs** — `/index repair` reuses an already-running
  pid instead of spawning another.

## 0.4.85 — 2026-06-09

### Fixed
- **Embedding repair** — embed-only repair for fresh files, smaller batches,
  patient 429 backoff, and live progress written to `.mesh/workspace.json`.
- **`/index` output** — human-readable report with headline, overview, and
  clear “what to do” section.
- **TUI index progress** — live bottom-right progress bar while background
  repair runs.

## 0.4.84 — 2026-06-09

### Fixed
- **Embedding index 429 at ~32%** — proxy and CLI now use Gemini
  `batchEmbedContents` (one upstream call per batch) instead of per-text
  `embedContent`, which was exhausting Google RPM around 100 requests.
- **`/index` TUI output** — preserves the boxed status layout with colors,
  section separators, and spacing instead of flattening to plain lines.

## 0.4.83 — 2026-06-09

### Fixed
- **Embedding index stuck at ~34%** — proxy path now sends batched `/embeddings`
  requests (one RPM slot per batch, not 24 parallel singles). Inter-batch pause
  and improved 429 retry backoff.

## 0.4.82 — 2026-06-09

### Changed
- **README** — token-efficiency positioning, try-mesh.com account flow, trimmed
  command list; top in-session commands highlight `/dashboard` over `/compact`.
- **CLI** — removed broken `mesh --classic` legacy UI path; TUI is the default.

### Fixed
- **TUI** — statusline component builder, grouped `/help` and `/status` overlays,
  toast-only setting changes, image paste compression, no idle summary spam;
  `[Image #N]` no longer leaks into the statusline plan segment.

## 0.4.81 — 2026-06-09

### Fixed
- **`/index` output** — shorter errors (no raw JSON dumps), deduped log lines,
  one clear “next step” hint; TUI chat shows a compact summary.
- **Embedding rate limits** — proxy uses a separate 3000 RPM bucket for
  `/embeddings` (chat stays at 120 RPM); indexer retries 429s with backoff.

## 0.4.80 — 2026-06-09

### Added
- **TUI copy** — `/copy [last|user|all|pick]`, click a message header, `Ctrl+O`
  picker, `Ctrl+Shift+C` for the last reply.
- **Shared Gemini embeddings** — logged-in users embed via the Mesh proxy's
  `GOOGLE_API_KEY` (Gemini API, not Vertex's 50 RPM quota). Local
  `GOOGLE_API_KEY`/`GEMINI_API_KEY` remains a fallback.

### Fixed
- **`/index` in the TUI** no longer dumps the full alt-screen frame into chat —
  slash commands suspend the renderer while output is captured.
- **Embedding coverage** no longer stalls around ~25% when Vertex rate-limits;
  indexing prefers the proxy Gemini API path.

## 0.4.73 — 2026-06-09

### Added
- **TOON tool-result encoding** — opt-in via `MESH_TOON_TOOL_RESULTS`
  (default off). Uniform JSON arrays in current-turn tool results are
  re-encoded as Token-Oriented Object Notation (a tabular
  `key[N]{f1,f2}:` header + comma rows), dropping repeated keys and
  braces for ~30-60% fewer tokens on list/table payloads. The encoder
  bails to the original JSON on any non-uniform/nested/empty structure
  and only swaps in TOON when it is strictly shorter — it can never
  corrupt a result.
- **Extractive prose compaction** — opt-in via `MESH_NL_COMPRESS`
  (default off). When the context budget overflows, non-source turns are
  compressed with a deterministic SumBasic frequency extractor (EN+DE
  stopwords, duplicate-segment folding) instead of the old
  `substring(0,100)` head-clip. The retention ratio tracks the budget
  tier, mirroring the native AST compressor used for source code.

### Changed
- **Cache-prefix stability.** Tool JSON-Schemas are now serialised with
  deep-sorted keys (`MESH_CANONICAL_TOOLS`, default on, kill-switch
  only; array order preserved) so an identical schema produces identical
  bytes turn over turn — a prerequisite for implicit prompt-cache hits.
  Adds an opt-in `MESH_STABLE_CACHE_PREFIX` (default off) that caches
  only the marked-stable system blocks on the Vertex path and re-injects
  the volatile tail into the request body.

## 0.4.72 — 2026-06-07

### Added
- **Native Gemini thinking-budget, tiered by turn class** — opt-in via
  `MESH_THINKING_BUDGET` (default off). Trivial turns spend no thinking tokens;
  complex turns get a deeper budget. Applies on the Vertex-direct path
  (`MESH_VERTEX_PROJECT`); `on`/`auto` use the per-turn tier, an integer sets a
  fixed budget, `dynamic` lets the model decide.
- **External skill/rule files are surfaced in the system prompt.** Mesh scans
  `.claude/skills` / `.github/skills` (`SKILL.md`) and `.cursor/rules`
  (`*.mdc`) and injects the relevant ones as additional instructions.
- **Semantic-search degraded signal.** When vector embeddings aren't available
  (no API key, or degraded coverage), the prompt now tells the model to prefer
  exact `grep_ripgrep`/regex queries over fuzzy conceptual ones and points to
  `/index` to restore them.

### Changed
- **Superseded file reads are evicted from context by default** (opt out with
  `MESH_EVICT_STALE_READS=0`). When a later read/patch of the same file sits in
  the live tool-result window, the earlier full copy is demoted to a one-liner
  so the model never reasons on stale bytes.
- **The critic flags `TODO`/`FIXME` only on lines the diff actually added**, not
  on pre-existing markers in the surrounding context.

## 0.4.71 — 2026-06-07

### Fixed
- **Edit quality gate no longer false-blocks ordinary edits.** The fake-data,
  static-session-id, fake-success-redirect and unbound-identifier checks now
  only fire on high-risk targets (API routes, checkout/payment/auth/email/order
  paths). `placeholder` is no longer treated as stub content when it is an
  HTML/CSS/object attribute (`<input placeholder=…>`, `::placeholder`,
  `{ placeholder: … }`), and the unbound-identifier scan now recognizes TS
  utility types (Record/Partial/Pick/Omit/…) and common JS/Web globals so a
  normal TypeScript patch can't trip it. Stub markers in comments and bare
  `dummy`/`fake` words are still caught everywhere.
- **Patch-storm hard stop counted each failed edit twice**, so it tripped after
  2 real failures instead of the intended 3. Edit failures are now counted in a
  single place.

### Changed
- **The LLM-written statusline task label is now opt-in** via
  `MESH_TASK_SUMMARY_LLM=1`. By default the local heuristic label is shown, so
  the statusline no longer costs a model call per turn on the main turn model.
- **Improvement questions that also carry a direct command now edit.** A turn
  like "was sollten wir verbessern – schreib die Route neu" unlocks edit tools
  again, while a pure "was können wir optimieren?" stays advisory.
- The git branch/dirty badge now refreshes as edits land mid-turn (throttled),
  and the task-summary cache is bounded so long sessions can't grow it without
  limit.

## 0.4.70 — 2026-06-05

### Added
- **Edit Quality Gates for risky changes.** API routes, auth, checkout,
  payment, billing, webhook, DB, email, ticket, order, and admin paths now
  require a concrete edit plan before the first patch: goal, existing flow to
  preserve, why this file/block, and verification.
- **Post-edit risk scanning before writes land.** Mesh blocks patches that
  introduce dummy/test/mock/fake placeholders, static checkout/session IDs,
  fake customer emails, unbound capitalized identifiers, or success early
  returns that bypass existing payment/order/email/database flow.
- **Patch-storm hard stop.** Repeated failed edits or repeated edits to the
  same file without successful verification now stop further patching in the
  current turn and force a useful summary/proposal instead of another edit.

### Changed
- **Final answers cannot claim "works" without verification.** When edits were
  applied but no verification command passed, Mesh rewrites unsupported
  "works/funktioniert" claims into an explicit not-verified status.
- **Verification selection now prefers stronger checks before lint.** For
  package projects, Mesh prioritizes typecheck/build/test before lint so route
  compile failures are more likely to be caught.

## 0.4.69 — 2026-06-05

### Fixed
- **Improvement questions now stay advisory.** Prompts like "was können wir
  als nächstes optimieren" or "what should we improve next?" no longer unlock
  edit tools or trigger patch-forcing gates. Mesh should first propose options
  instead of editing files unless the user gives a direct edit instruction.
- **TUI turn summaries now explain what happened in words and group repeated
  edits by file.** The "Zusammenfassung" block includes a short `Kurz:` line
  from the assistant's final answer and collapses repeated edits on the same
  path into one file entry with an operation count.

## 0.4.68 — 2026-06-04

### Added
- **Claude-Code-style permission modes, cycled with Shift+Tab.** Three modes:
  `ask` (prompt before every edit/shell tool — the default), `auto-accept
  edits` (auto-apply edit tools, still prompt for shell commands), and
  `auto-approve all`. Press Shift+Tab anytime to cycle; the active mode shows
  in the statusline (new `{mode}` placeholder, added to the default template)
  and in `/tui-status`. `/approvals <ask|edits|auto>` sets it directly.
- **Arrow-key navigation in the approval prompt.** The "Approve this action?"
  box is now selectable with ↑/↓ + Enter (Codex/Claude-Code style); the
  number keys (1/2/3) and Y/A/N shortcuts still jump straight to an option.
  The box header shows the current permission mode and the Shift+Tab hint.

## 0.4.67 — 2026-06-04

### Added
- **End-of-run summary in the TUI.** When a turn that edited files or ran
  tools finishes, mesh now prints a "Zusammenfassung" block listing the files
  it touched (verb + path + ±line counts) and a footer with tool count,
  errors, duration, tokens, and cost. Pure chat replies (no edits, no tools)
  get no summary, and the local-intent fast path no longer shows a stale one.

## 0.4.66 — 2026-06-04

### Changed
- **Approval prompts now lead with a plain-language summary of the edit.**
  Each write/delete/move/patch approval preview starts with a one-line
  description of what the tool will do (e.g. "Ändert einen Block in
  components/TicketShop.jsx", "Löscht Datei …", "Verschiebt Datei … -> …")
  before the diff, so it's clear what is being changed before it's applied.
  Also replaced the garbled `?` placeholder glyphs in the previews with ASCII.

## 0.4.65 — 2026-06-04

### Fixed
- **Edit/delete/move tools applied changes without an approval prompt.**
  `workspace.patch_file`, `workspace.move_file`, and `workspace.delete_file`
  were missing the `requiresApproval` flag that `write_file`,
  `patch_surgical`, and `alien_patch` already carried. The approval gate keys
  off that flag, so these three write-class tools bypassed it and applied
  edits (and deletions) directly. All write/delete/move tools are now gated
  consistently; `MESH_AUTO_APPROVE=1` / `/approvals on` remains the opt-out.

## 0.4.62 — 2026-06-03

### Fixed
- **Silent mid-turn exit on edit/read prompts (exit 0, empty output, no
  edits).** `WorkspaceIndex.partialUpdate()` is invoked from inside
  `ensureIndexInner()`, whose promise is registered as `ensureIndexInFlight`.
  It re-entered `await this.ensureIndex()`, which handed back that very
  in-flight promise — a circular await that never settles. With no pending I/O
  to keep the loop alive, Node drained the event loop and exited 0 mid-turn.
  `partialUpdate()` now uses the already-loaded `cachedIndex` instead of
  re-entering `ensureIndex()`. The no-index rebuild path had the same failure
  mode via an `unref()`'d race timer; it is now ref'd and cleared in `finally`,
  guaranteeing a fallback to the placeholder index so the turn always reaches
  the model loop.
- **Edit turns no longer loop after the change lands.** Once an edit has
  actually been applied this turn, the forced-tool-choice gate stops narrowing
  to edit-only tools and stops forcing `tool_choice="required"`, so the model
  can verify and emit a final answer instead of re-issuing
  `write_file`/`patch_file` every step (observed: 19+ steps / 196 s).

## 0.4.63 — 2026-06-03

### Fixed
- **`create` / `rename` / `delete` requests now actually edit instead of
  printing prose.** Tool-selection and the forcing gate carried two separate,
  drifted verb lists, so "benenne … um" / "entferne die funktion" / "erstelle
  eine datei" were classified as edits by the gate while the write tools were
  never put in the inventory — the model replied "keine Schreibwerkzeuge zur
  Verfügung" and emitted a code block. (`\berstell\b` also couldn't match
  "erstell**e**".) Both now share one `looksLikeEditIntent()` predicate with
  prefix-tolerant DE/EN stems, so create/rename/delete reliably unlock
  `write_file`/`patch_file`. Pure read/search turns still get no edit tools.

### Performance
- **Trivial-tier edits no longer downgrade to flash-lite.** A one-line edit
  classified "trivial" was routed to `gemini-2.5-flash-lite`, which produces
  lower-fidelity patches and thrashed in re-patch/verify loops — stretching a
  ~15 s task to ~150 s. Edit-intent turns now stay on the more capable default
  model; the per-edit token saving wasn't worth the multi-minute blowups.
- **No more post-edit discovery wandering.** After the edit lands, the model is
  unforced but used to wander into `list_directory` / `list_files` /
  `search_files` / `semantic_search` / `ask_codebase` re-confirming a change it
  already made (one `list_directory` call alone burned 20 s; ~9 wasted steps
  total). These pure-discovery tools are dropped once an edit is on disk —
  `read`/`grep`/`git_diff`/`run_command`/edit stay, so rename-all-occurrences
  and verification still work. Combined effect: the vague-edit case dropped from
  152 s → 15 s, rename from 144 s → 56 s, with correctness unchanged (10/10).

### Fixed (Vertex-direct / prompt cache)
- **Vertex-direct mode re-enabled and corrected (opt-in via
  `MESH_VERTEX_PROJECT`).** Gemini traffic over the Mesh proxy uses Vertex's
  OpenAI-compat endpoint, which neither engages Gemini implicit context
  caching nor reports `cachedContentTokenCount` — so cross-turn `--resume`
  cache savings were structurally unreachable (always `cached=0`, regardless
  of prefix size). The native `generateContent` path that *does* cache was
  disabled because its OpenAI→Vertex translation dropped tool calls on replay
  turns. Three translation bugs fixed: (1) `functionResponse.name` was read
  from a non-existent field on tool messages (they carry `tool_call_id`, not
  `name`) — now resolved via an id→name map built from the assistant turn;
  (2) Gemini's `thoughtSignature` was neither echoed on replayed
  `functionCall` parts nor captured from the native response — now round-trips
  both ways (mandatory for Gemini 2.5+, else HTTP 400); (3) one `functionResponse`
  per `content` turn violated Gemini's "response-part count must equal
  call-part count" rule — adjacent same-role turns are now coalesced. Verified:
  2-turn `--resume` now succeeds (no HTTP 400, history preserved) and routes
  natively, where implicit caching is reachable on large prefixes.
  Note: implicit caching is best-effort and only engages on large prefixes
  (~15–20k tokens; verified empirically — 2.8k → 0 cached, 20k → ~19k cached),
  so Mesh's lean context (its main cost win) naturally produces fewer cache
  hits than a context-heavy baseline. The native path makes caching *possible*;
  it is not a guaranteed per-turn refund.

## 0.4.61 — 2026-06-03

### Changed
- **Edit-class tools are never gated.** `write_file` / `patch_file` /
  `patch_surgical` / `change` now bypass every per-turn block — tool budget,
  duplicate-signature guard, family ceiling, and the anti-thrash verify-gate —
  so a vague request like "setz das um" always lands the change instead of
  punting with "tools blocked" prose. The loop guards still apply to
  read/search/command tools, and the outer `maxSteps` cap still bounds the turn.
  The error-storm breaker and the early-exit gate are now edit-aware: on an edit
  turn they keep ONLY the edit tools and force a call (apply the change) rather
  than stripping all tools to text-only. The misleading "[ANTI-THRASH ACTIVE]
  edits are blocked" banner is replaced with a non-blocking verify *suggestion*.

### Fixed / Hardened
- **No more multi-minute model-call hangs.** A single model call could run
  unbounded (observed: ~58 min with empty output) because the streaming
  watchdog only guarded *gaps between* SSE events — an upstream trickling
  keep-alive/whitespace/empty deltas reset the idle timer forever. The stream
  now enforces an absolute wall-clock ceiling (`MESH_STREAM_MAX_CALL_MS`,
  default 150 s) that no event can reset, ending gracefully with whatever was
  buffered. The non-streaming `converse` candidate loop gained a matching hard
  ceiling (`MESH_MODEL_CALL_HARD_CEILING_MS`, default 4× the per-attempt
  timeout) so cumulative retries across fallback models can't stack into
  minutes either.
- **Vague requests reliably edit instead of stalling.** Stub-mode tool specs
  used to drop `inputSchema` entirely after turn 1, so weaker models (notably
  the default gemini-3.5-flash) forgot which arguments were mandatory and
  emitted malformed calls (e.g. `read_file` with no `path`); the schema
  validator rejected them, the model repeated the broken call, and the
  anti-loop guard then reported "tools blocked" on a plain "setz das um". Stubs
  now retain a minimal required-only schema (type + required + bare property
  types), and the dedup-gate message is context-aware: when the repeated calls
  were *failing*, it tells the model the arguments are wrong (with an example)
  and that the tools are NOT blocked, instead of declaring them unavailable.

## 0.4.60 — 2026-06-03

### Added
- **Skeleton-first reads.** `read_file` on a large file (24–256 KB) now returns
  the capsule *plus* a symbol outline with exact line ranges (`L<start>-L<end>`),
  so the model jumps straight to `read_slice` / `read_file_lines` on the one
  symbol it needs instead of re-reading the whole file or grepping blindly.
- **Session cache-hit-rate metric.** The live status row and `session cost:`
  line now surface a `cache NN%` figure (cached input tokens / total input
  tokens), making prompt-cache effectiveness visible turn over turn.

### Changed
- **Cache-stable tool gating.** The prompt-cache breakpoint now lands on the
  last always-present core tool instead of the churning last per-turn tool, so
  the cached tool prefix stays byte-identical across turns and actually hits.
- **Cache-sticky model routing.** Within a session the router prefers the
  already-warmed model (configurable TTL via `MESH_CACHE_STICKY_MS`) rather than
  switching models and evicting the per-model prompt cache.
- **Force patch over whole-file rewrite.** `write_file` rejects a rewrite of an
  existing file when ~70%+ of its lines are unchanged, steering the model to
  `patch_surgical` (override with `MESH_ALLOW_WHOLE_FILE_REWRITE=1`).

### Fixed / Hardened
- **Workspace path safety:** symlink-aware `ensureInsideRoot` (real-path check),
  SSRF guard with manual per-redirect revalidation and a 5 MB body cap on doc
  fetches, atomic writes (temp-file + rename) across all tool writes.
- **Process safety:** `run_command` spawns in its own process group with
  SIGTERM→SIGKILL escalation on timeout and a 5 MB stdout/stderr cap; `node
  --check` / `tsc` invocations carry explicit timeouts.
- **Command-safety gate** now blocks `git -c`/`--config-env` overrides that can
  execute commands (hooks, editors, pagers, credential/askpass helpers, ssh,
  diff/merge/filter drivers), including the single-token `-c=name=value` form.
- **Stream/tool JSON errors** are no longer silently swallowed; malformed tool
  arguments surface a structured re-send hint to the model (debug via
  `MESH_DEBUG_LLM=1`).

## 0.4.59 — 2026-06-03

### Changed
- **Live tool activity is now a single clean working line, Claude-Code / Codex
  style.** The old feed always printed a 3-deep breadcrumb panel
  (`tools` header + `RUN/OK/ERR workspace_read_file {json args}` × 3) on top of
  the composer's own spinner — two animated indicators and a wall of wire-tool
  noise. Now there is exactly one working line: the composer spinner shows a
  humanized action (`⠋ Read src/x.ts`, `Edit app.tsx`, `Search useState`,
  `Run pnpm build`) for whatever Mesh is doing *right now*, and nothing
  lingers once the turn finishes. Wire-tool JSON is no longer surfaced.
- **Approval prompt is a clean numbered menu.** Replaced the single dense
  `Y approve once  A approve all…  N reject + redirect  Esc reject` line with a
  Claude-Code / Codex-style menu: `❯ 1. Yes` / `2. Yes, and don't ask again this
  session` / `3. No, tell Mesh what to do differently (esc)`. Number keys 1/2/3
  work alongside the existing Y/A/N shortcuts; the redirect prompt is tidied to
  a single `❯` input line.

## 0.4.58 — 2026-06-03

### Changed
- **Changed-file display is now a real Claude-Code / Codex-style diff.** Edits
  used to render as a plain SYSTEM block with a mojibake header (`? patch_file ?
  components/Foo.jsx (literal) +1 -1`) because the renderer's edit-detection
  looked for a `✎` glyph the emitter no longer produced — so every edit fell
  through to the generic word-wrapping system block, which also mangled diff
  alignment. Edits now flow through a structured `onEditDiff` feed and render as:
  a `●  Update  components/Foo.jsx  +1 -1` header (verb color-coded:
  Update=cyan, Create=green, Delete=red, Move=yellow) followed by a real diff
  hunk with **1-based line-number gutters** located in the post-apply file, red
  `-` / green `+` lines, aligned, and a `… N more changed lines` overflow note.
  Headless callers get a clean plain-text fallback. Removed the dead
  `formatEditDiff` / `parseEditSystemMessage` glyph-parsing path.

## 0.4.57 — 2026-06-03

### Fixed
- **`read_file` (and every other tool) no longer rejects valid calls over a key-
  name dialect.** The schema validator runs BEFORE the tool body, so when the
  model passed the path under an alias — `{file: "…"}`, `{filePath: "…"}`,
  `{filename: "…"}` — it was rejected with `workspace.read_file.path is required`
  and the model looped (wrong key → reject → re-read → wrong key), then punted
  "Tools wurden entzogen, ich mache es im nächsten Schritt". Tool arguments are
  now **alias-normalized before validation**: for any canonical key a tool
  declares (`path`, `content`, `pattern`, `command`, `search`, `replace`,
  `startLine`, `endLine`, `symbol`), a known alias is mapped onto it. An
  explicitly provided canonical value is never overwritten, and tools that don't
  declare the key are untouched.
- **The convergence "force-edit-only" gate no longer deadlocks edits that still
  need to read.** It stripped the inventory down to edit tools only, so a
  search/replace patch couldn't fetch the file's exact current text — the model
  reported it could neither read nor patch. The restricted set now keeps a
  minimal read kit (`read_file`, `read_file_lines`, `grep_ripgrep`) alongside the
  edit tools, and forces a tool call without pinning to one tool, so the model
  reads-then-applies instead of punting into prose.

## 0.4.56 — 2026-06-02

### Fixed
- **Savings cost no longer inverts as you spend more.** The displayed "saved $"
  valued saved tokens at the session's *realized* input rate (realized input $ ÷
  input tokens). That realized rate bakes in the 0.25× cache discount, so the
  more Mesh cached — i.e. the more it actually saved — the lower the rate became,
  and the displayed savings *shrank* as spend grew. Exactly backwards. Saved
  tokens are now valued at the **weighted FULL (undiscounted) catalog input
  rate** of the models actually run this session (Σ inputTokens·catalogRate ÷ Σ
  inputTokens), so pricier-model sessions value savings higher and caching never
  deflates the figure.
- **Savings is shown as a positive number, not `-$0.2567`.** The saved-cost
  figure is a saving, but it was rendered with a leading `-$`, which read as a
  loss. Dropped the minus across the status line, the live usage row, and the
  `/savings` breakdown.

### Added
- **run_command-as-read interception (ULTRA 3).** `cat`/`head`/`tail`/`less`/
  `more` and `node -e readFileSync(...)` bypass the read-dedup cache, the capsule
  clamp, and mtime tracking — so the same file content is re-shipped to the model
  at full token cost on every call (the dominant waste in stuck sessions). Mesh
  now detects these trivial single-file reads and routes them through the
  optimized `workspace.read_file` / `read_file_lines` path (cached, clamped,
  dedup-tracked), and nudges the model to call the read tools directly next time.
  Anything with shell plumbing (pipes, redirects, chaining, globs, multiple
  files, unmodeled flags like `tail -f`) is left to the real shell untouched.
  Disable with `MESH_INTERCEPT_READ_COMMANDS=0`.
- **Actionable schema hints on bad tool arguments.** A new `bad-arguments`
  failure class detects schema/argument errors (`requires …`, `invalid
  argument`, `unknown field`, …) and surfaces the EXACT correct argument shape
  for the offending tool (e.g. `patch_file` → `{path, search, replace}` plus the
  accepted dialects, `read_file_lines` → `{path, startLine, endLine}`) instead of
  a generic "check the arguments". Breaks the wrong-shape → vague-error →
  re-read → wrong-shape loop, and stops fast (after 2) if the model ignores it.

### Note
- The read-dedup cache (#1 of the requested batch) was already shipped in 0.4.31
  (`ToolResultCache`, mtime/TTL/LRU invalidation) and is verified intact.

## 0.4.55 — 2026-06-02

### Fixed
- **Edits actually land now — the "1.4M tokens, nothing implemented" failure.**
  Root cause: `gemini-3.5-flash` calls `workspace.patch_file` with the wrong
  argument shape — `{file, operations:[{action, current_text, new_text}]}` (the
  VS Code / Anthropic apply-patch dialect) instead of the declared
  `{path, search, replace}`. The tool threw `requires path and search string`
  on every attempt (the "3 tool errors"), so the model gave up and re-read the
  same file dozens of times via `run_command` (the token blowup), then punted
  the change into prose. `patch_file` and `patch_surgical` now **normalize the
  common patch dialects** (`path`/`file`, `search`/`current_text`/`old_string`,
  `replace`/`new_text`/`new_string`, and a multi-op `operations[]`/`edits[]`
  array), so the edit applies on the first call regardless of which shape the
  model emits.
- **The tool-spam "hard gate" no longer forces an edit turn to punt into prose.**
  When the model looped on reads, the convergence gate stripped its ENTIRE tool
  inventory for the next call — which, on an edit request, guaranteed the answer
  "Tool-Limit erreicht, ich mache es im nächsten Schritt" (a step that never
  comes). On edit turns the gate now keeps ONLY the edit tools and forces a
  call, so the model must apply the change instead of describing it. (The
  non-streaming path — default since 0.4.52 — also previously *ignored* the
  strip flag entirely while telling the model "no tools available", so the model
  punted on a lie; the restriction is now actually applied.)
- **"setz die alle um" / "umsetzen" now triggers the edit gate.** The
  edit-intent detector missed the German separable verb "setz … um" (and
  "umsetzen"/"anwenden"), so a plain "setz die alle um" fell through to a prose
  answer instead of forcing a tool call. Added those forms.

## 0.4.54 — 2026-06-02

### Fixed
- **The "model call · 156s" hang on the non-streaming path is gone.** When the
  default `gemini-3.5-flash` endpoint accepted the connection but never
  answered, the per-attempt deadline (now 45s) would fire — but the catch
  handler treated that timeout like a transient blip and **retried the same
  hung model up to 3 more times**, so a dead endpoint burned ~4 full timeout
  windows (150s+) before any fallback was tried. A deadline timeout
  (`TimeoutError`/`AbortError`, not a caller Ctrl+C) now **jumps straight to the
  next candidate model** (a known-good Gemini sibling) instead of re-waiting on
  the dead one. Genuine transient errors (network resets, 429/5xx) still retry
  in place. The per-attempt deadline also drops 60s → 45s and is overridable via
  `MESH_MODEL_CALL_TIMEOUT_MS`.

## 0.4.53 — 2026-06-02

### Fixed
- **"ja bau das ein" now actually edits files instead of just describing the
  change.** Turning streaming off in 0.4.52 silently dropped the edit-intent
  force-tool-call gate, because that gate only lived in the streaming code path.
  The non-streaming path is now the default, so a confirmation like "ja bau das
  ein" / "integrier das" / "binde das an" fell through to a prose answer. The
  gate is now a single shared `applyForcedToolChoiceGate` used by **both** the
  streaming and non-streaming paths, so an edit-directive turn always forces a
  tool call (write/patch) regardless of streaming mode. The edit-verb set also
  now catches the integration verbs that kept slipping through (`bau … ein`,
  `integrier`, `anbind`, `einbau`, `wire`, `implement`).
- **The "saved cost" figure is no longer absurdly small next to actual cost.**
  Saved tokens were always priced at the *currently-selected* model's catalog
  input rate (gemini-3.5-flash, $0.000075/1k), even when the session had billed
  far pricier models — so a session that actually paid an effective input rate
  ~14× the flash rate still valued its savings at the flash rate, making "saved
  ≈ -$0.0152" look nonsensical beside a "$1.21" cost. Saved cost is now valued at
  the session's **realized blended input rate** (actual input $ ÷ actual input
  tokens), with the catalog rate as a floor, so the saving is always
  proportional to what the session really spent.

## 0.4.52 — 2026-06-02

### Changed
- **Streaming is now OFF by default.** A stalled streaming endpoint (an SSE
  connection that opens but never delivers a token) was the single biggest
  source of multi-minute "model call" hangs, and the live token deltas weren't
  worth that fragility. Every turn now runs through the non-streaming `converse`
  path, which has full transient-retry + model fallback. The answer still
  appears in the TUI (delivered in one block when complete) and one-shot
  `mesh "task"` still prints its reply. Re-enable with `MESH_ENABLE_STREAMING=1`.

### Fixed
- The default `gemini-3.5-flash` model is now far more resilient. `converse` now
  (1) **retries transient failures** (429 / 5xx / network timeout) on the same
  model with backoff (600ms → 1.5s → 3s) before falling back, so a brief
  upstream blip recovers in place instead of erroring the whole turn, and
  (2) **always includes the Gemini siblings** (`gemini-2.5-flash`,
  `gemini-2.5-flash-lite`) as deep fallbacks — independent of any NVIDIA key —
  so a genuine 3.5-flash outage is always rescued by a healthy Google model. A
  caller-driven abort (Ctrl+C) still stops immediately without retrying.

## 0.4.51 — 2026-06-02

### Fixed
- The "model call · 43s" hang on a stalled model is gone. When a streaming call
  delivered zero events (the model's endpoint proved unresponsive), the
  non-streaming fallback previously retried **the same just-stalled model first**
  with up to a 40s deadline — so a dead primary cost the 25s first-token
  watchdog *plus* another ~40s hitting the same dead endpoint before anything
  else was tried. The zero-event fallback now routes to a **different, known-good
  model** (`gemini-2.5-flash`, or `gemini-2.5-flash-lite` if 2.5-flash was the
  one that stalled) so the turn recovers on a healthy endpoint instead of
  re-waiting on the dead one. The first-token watchdog default also drops from
  25s to 18s (`MESH_STREAM_FIRST_TOKEN_TIMEOUT_MS`) so stalls are caught sooner.
- Stalled **tool** turns no longer double-hang. After a stream stall the Gemini
  wrapper used to re-fetch on the original (stalled) model just to recover
  `thought_signature`; since the fallback `converse` already returns canonical
  tool calls with the signature, that redundant re-fetch is now skipped.

## 0.4.50 — 2026-06-02

### Changed
- Exit is now driven entirely by `Ctrl+C` — `Ctrl+O` no longer exits. A single
  `Ctrl+C` aborts the current turn and arms a `press Ctrl+C again to end the
  session` prompt (3s window); a second consecutive `Ctrl+C` ends the session.
  Any other keypress cancels the pending exit. This replaces the two-press
  `Ctrl+O` flow from 0.4.49.

## 0.4.49 — 2026-06-02

### Changed
- Exit flow is now Claude-Code-simple and harder to trigger by accident.
  `Ctrl+C` only ever **aborts the current turn** — it never ends the session.
  Ending the session now takes **two consecutive `Ctrl+O` presses**: the first
  arms a `Press Ctrl+O again to end the session` prompt (3s window), and any
  other keypress in between cancels it. Previously a single `Ctrl+O` exited and
  an idle `Ctrl+C` exited, so a stray keystroke could drop you out mid-session.

### Fixed
- Broken terminal after exit. On some exit paths the TUI left the terminal in
  raw mode with SGR mouse reporting still on, so the shell couldn't be typed in
  normally afterward (stray escape junk, no echo). Terminal teardown is now a
  single idempotent reset (`?1000l ?1006l` mouse off, `?25h` cursor on,
  `?1049l` alt-screen off, raw mode off) shared by every exit path — including
  the slash-command exit (`/exit`) path, which previously skipped the reset
  entirely. A last-resort restore guard on `exit`/`SIGINT`/`SIGTERM`/`SIGHUP`
  guarantees the terminal is restored even if the process is killed externally.

## 0.4.48 — 2026-06-02

### Fixed
- Stalled-upstream turns no longer freeze for ~2 minutes. When a streaming
  model call delivers zero events, the first-token watchdog (25s) aborts and
  falls back to a non-streaming `converse`. That fallback previously used the
  full 60s call timeout, so a genuinely unresponsive endpoint cost 25s + 60s
  (× model fallbacks) — a trivial no-tool question could hang at "model call"
  for 85s+. The fallback now uses a tighter deadline
  (`MESH_STREAM_FALLBACK_TIMEOUT_MS`, default 40s) precisely when the stream
  produced nothing, since the endpoint has already proven slow: it either
  recovers fast or surfaces the error fast instead of waiting out the full
  timeout. Streams that delivered some events before failing keep the normal
  deadline.

## 0.4.47 — 2026-06-02

### Added
- Per-turn timing footer: a single dim line after every answer —
  `~ 3 hops · 18.2s · 92% cached · 12 tools · $0.0140` — so latency and cost
  regressions are visible at a glance without enabling `MESH_HOP_TIMING`.
  Silence with `MESH_HIDE_TIMING=1`.
- Prompt-cache hit-rate is now surfaced. `MESH_HOP_TIMING` shows the aggregate
  cache share on the summary line and a per-hop `cache=Nk (P%)` figure. Cache
  misses are the single biggest silent cost driver, so this makes them
  measurable before optimising anything else.
- Speculative first-read warming: files the prompt names explicitly are warmed
  into the capsule / OS cache un-awaited, just before the first model hop, so
  the model's first `read_file` hits a warm cache instead of cold disk + AST
  build. Bounded, deduped per session, best-effort. Kill-switch
  `MESH_SPECULATIVE_WARM=0`.
- Per-turn read budget: once whole-file reads cross a soft cap (~240k chars,
  `MESH_READ_BUDGET_CHARS`), Mesh nudges the model toward ranged reads
  (`read_file_lines` / `read_slice`) and `grep` so a turn can't silently
  balloon context and cost by pulling in large files. Fires once per turn,
  steers only — never blocks a tool.

### Changed
- Failure classifier covers two more error families that previously fell
  through to `(unknown)`: `rate-limit` (HTTP 429 / overloaded / quota — gets a
  back-off-and-shrink hint and extra retries) and `io-error` (EISDIR, ELOOP,
  EMFILE, non-UTF-8/binary — the most common silent `read_file` failures, now
  with a directory/binary/symlink hint and a fast stop).

## 0.4.46 — 2026-06-02

### Changed
- Parallel tool calls are now the steered default. The system prompt (both the
  light and full working-style blocks) instructs the model to emit independent
  tool calls — reading several files, grep + list, multiple searches — together
  in a single turn instead of one per turn. The executor already ran batched
  calls concurrently (staged write/read/command, `Promise.all` per stage); the
  bottleneck was the model issuing one read per LLM round-trip. Batching cuts the
  number of round-trips, which is the dominant cost of slow turns. Calls that
  genuinely depend on a previous result (grep for a path, then read it) still
  serialize.

## 0.4.45 — 2026-06-02

### Fixed
- Tool failures now surface the real error. Previously a failing tool showed only
  its failure class — e.g. `workspace.read_file failed (unknown)` — hiding the
  actual cause, so the model retried blindly. The recovery text now appends the
  raw error message (truncated to 300 chars) so both the model and the user can
  see what actually broke.
- Error-storm circuit breaker. When ≥5 tool calls fail in a turn and the failures
  make up at least half of all tool calls, Mesh now strips tools for the next
  model call and forces a plain-language final report (what was accomplished vs.
  what is blocked, quoting the real error) instead of retrying the same failing
  action dozens of times. This stops the runaway-cost / multi-minute turns that
  blind retries caused (one CALOR session burned 332K input tokens / ~$0.20 / 220s
  retrying a single failing read 13 times).
- Approval overlay rendering. The modal previously re-showed the underlying chat
  line on both sides of the box, so chat text bled around it and it looked broken.
  The overlay now blanks the full row band before centering the box.

## 0.4.44 — 2026-06-02

### Added
- Interactive first-edit approval gate (Claude-Code style). In the TUI, the
  first edit/shell tool call now prompts: `Y` approve once, `A` approve all for
  the session, `N` reject and type a redirect instruction (fed back to the model
  as the tool result so it follows your new direction), `Esc` plain reject. The
  gate is on by default for the interactive TUI only; one-shot `mesh "task"`
  runs and non-TTY/headless invocations stay auto-approved. `MESH_AUTO_APPROVE`
  forces auto-approval; `/approvals on|off` toggles it mid-session.
- `MESH_HOP_TIMING=1` prints a per-turn latency breakdown to stderr at end of
  turn — total wall-clock split into LLM hops, tool execution, and overhead,
  with a per-LLM-call line (tokens + cache) and tools aggregated by name. Answers
  "why did that turn take 280s" without a profiler.

### Fixed
- Chat scrolling in the TUI: the alt-screen has no native scrollback, so the
  trackpad/mouse wheel did nothing and long replies couldn't be scrolled back.
  The TUI now enables SGR mouse reporting and maps wheel up/down to chat scroll
  (keyboard PageUp/Shift+↑/Home still work). Mouse sequences are reassembled and
  swallowed so they never leak digits into the input line.
- Edit tools were never offered for several common German/English implementation
  verbs ("optimier", "optimize", "verbessere", "ersetze", "repariere",
  "schreib … neu"), so the model emitted code as prose instead of editing files.
  The verb classifier is now prefix-tolerant across conjugations.

## 0.4.43 — 2026-06-02

### Added
- Hybrid lexical rerank for semantic search (`retrieval-rerank.ts`): blends
  embedding similarity with verbatim identifier/term overlap so a file the user
  named a symbol from floats above a higher-cosine-but-irrelevant hit. Pure,
  local, zero added latency. Kill-switch `MESH_RETRIEVAL_RERANK`.
- Retrieval-confidence hint that nudges the model when a search tool returns no
  matches, reducing "not found" hedging. Kill-switch `MESH_RETRIEVAL_GATE`.
- Dedup stub for large repeated tool reads, collapsing already-provided content
  to a short reference. Kill-switch `MESH_DEDUP_STUB`.

### Fixed
- Prompt-cache stability: history cache-boundary markers are now placed
  clone-on-mark instead of mutating memoized history objects in place, so
  markers no longer accumulate across turns or exceed the provider's 4-marker
  limit. Kill-switch `MESH_STABLE_HISTORY_CACHE`.
- README banner now loads on npm: switched the relative image path to a public
  jsdelivr CDN URL (the source repo is private, so GitHub-raw resolution 404'd).

### Changed
- Patch-over-rewrite steering now applies on the small task tier too (was only
  on complex), gated on the actual availability of a patch tool.

## 0.4.42 — 2026-06-02

### Fixed
- Replaced leaked `workspace:*` dependency specifiers in the published CLI
  package with real npm versions so `npm install -g @trymesh/cli` works.
- Updated the release smoke test to pack with `npm pack`, matching
  `npm publish`, so workspace protocol leaks are caught before release.

## 0.4.41 — 2026-06-02

### Fixed
- Re-published the 0.4.40 CLI fixes with a clean dashboard build output so the
  npm tarball does not include duplicate generated assets from repeated local
  release attempts.

## 0.4.40 — 2026-06-02

### Fixed
- Prevented empty model responses from ending a turn as `(no answer)`: Mesh now
  retries once with an explicit recovery prompt and forces a tool-less final
  answer fallback when needed.
- Moved turn tool-call accounting from UI render events to actual scheduled
  tool executions, so traces and diagnostics no longer report `toolCalls: 0`
  when the model did use tools.

## 0.4.39 — 2026-05-30

### Changed
- Rewrote the README to a minimal, professional layout and shipped a new
  banner. The banner is now a PNG referenced by a package-relative path so it
  renders on the npm package page (npm's README pipeline blocks external SVGs).

## 0.4.38 — 2026-05-30

### Fixed
- Streaming model calls could hang forever when the LLM endpoint opened a
  connection but never delivered a first SSE event (stalled proxy /
  unresponsive upstream): the turn froze at "model call" with `0 in / 0 out`
  and no usage ever logged. The streaming reader now enforces a first-token
  timeout (`MESH_STREAM_FIRST_TOKEN_TIMEOUT_MS`, default 25s) and an
  inter-event idle timeout (`MESH_STREAM_IDLE_TIMEOUT_MS`, default 30s). A
  first-token timeout aborts the stalled request and falls back to the
  non-streaming `converse` path so the turn still completes; a mid-stream
  stall ends gracefully with whatever was already streamed.

## 0.4.37 — 2026-05-29

### Added
- Learned per-workspace tool routing: tool-usage frequency persists to
  `.mesh/tool-usage.json` and biases per-turn wire-tool selection toward the
  tools this workspace actually uses (keyword matches keep priority).
- Predictive capsule warming: the co-occurrence predictor now warms the
  low-tier capsules of files usually read alongside the prompt's named files
  into the next turn's context. Fire-and-forget, bounded, complex-tier only.
- Context-deltas on re-read: re-reading a file already read earlier in the
  same turn returns just the changed lines instead of the whole file.
- Opt-in flash-lite tool-result distillation (`MESH_DISTILL_TOOL_RESULTS=1`)
  for very large reads, and opt-in confidence-gated early exit
  (`MESH_EARLY_EXIT=1`) when steps stop surfacing new evidence.

### Changed
- `write_file` / `patch_surgical` tool descriptions and the system prompt now
  steer edits of existing files to surgical patches, cutting output tokens
  spent re-emitting unchanged lines.

## 0.4.23 — 2026-05-27

### Added
- Auth handshake now backed by Google Cloud Firestore (replaces Vercel Blob).
- IDE companion: shared `~/.config/mesh/session.json` so one `mesh login`
  covers the CLI, the Mesh IDE (electron + web), and the marketing dashboard.
- Headless-mode bail-out: the CLI exits cleanly when invoked without a TTY
  and no fresh session is available, preventing background polling loops.

### Fixed
- Artifact memory churn between turns; tool routing more deterministic.
- Edit gates respect the `/plan` mode (read-only) consistently.
- `thought_signature` survives context-trim cycles.
- `tab` no longer doubles characters in autocomplete.

### Internal
- Robust cursor moves on non-VT100 terminals.
- Floating statusline rendering during long tool calls.
- Background dev servers logged separately, not in the agent loop.

## 0.4.22 and earlier

Prior versions shipped iteratively. The repository history is the canonical
record — `git log apps/cli/` from this tag for the full diff. Highlights:

- Bordered input box; transient stats line.
- Autonomy hardening + relaxed tool guards behind feature flags.
- Compaction floor raised to keep recent context warm.

For licensing inquiries, contact licensing@try-mesh.com.
