# v0.22.1

Patch release. Four fixes since [v0.22.0](https://github.com/cortexkit/aft/releases/tag/v0.22.0).

## Fixes

### Semantic search against OpenAI no longer fails with "you must provide a model parameter" ([#36](https://github.com/cortexkit/aft/issues/36))

When `semantic.backend: "openai_compatible"` pointed at `https://api.openai.com/v1`, AFT's embedding requests were rejected with HTTP 400 `"you must provide a model parameter"` even though the configured `model` was set correctly in `aft.json`. Root cause: AFT was sending two `Content-Type: application/json` headers on the wire — once implicitly via reqwest's `.json(&body)` (which serializes the body and sets the header) and again via an explicit `.header("Content-Type", "application/json")` call right after. reqwest's `.header()` calls `HeaderMap::append`, not replace, so both ended up on the wire. OpenAI's `/v1/embeddings` parser treats duplicate `Content-Type` as malformed and rejects the body — including the `model` field that's actually there.

The fix drops the redundant explicit header from both the OpenAI and Ollama backends. The Ollama branch had the same defect; most Ollama servers tolerate duplicate `Content-Type` so it never surfaced in user reports, but the fix lands consistently.

A new regression test captures the raw on-wire request and asserts exactly one `Content-Type` header is sent.

### Pi: stop downloading ONNX Runtime when the configured backend doesn't need it

Pi's startup gated the 60-80 MB ONNX Runtime download on `config.semantic_search` alone, so Pi users with `semantic.backend: "openai_compatible"` or `"ollama"` still triggered the download even though the runtime is never loaded for HTTP-based backends. Pi now mirrors OpenCode's gate — ONNX Runtime is only fetched when `semantic_search` is enabled AND the backend is `fastembed`.

### `aft_zoom` now accepts the `## ` / `<h2>` prefixed form that `aft_outline` shows

`aft_outline` emits Markdown and HTML headings with their level prefix:

```
## Basic usage 32:219
<h2> Features 219:234
```

Agents naturally copy-paste that prefixed form into the next `aft_zoom` call. Until now AFT rejected those lookups with `symbol '## Basic usage' not found` and only accepted the bare text form. `aft_zoom` now strips the level prefix on the Markdown/HTML resolution path so both `Basic usage` and `## Basic usage` resolve to the same section. Code-symbol resolution is unchanged — Rust attributes like `#[derive(Debug)]` still match exactly.

### Stop polluting Windows builds with unused-warnings

Six items in `bash_background/registry.rs`, `commands/bash.rs`, and `semantic_index.rs` only have call sites on non-Windows targets but weren't gated, so Windows builds emitted `unused_imports` and `dead_code` warnings. Added `#[cfg(...)]` predicates matching the call-site availability so the items only exist where they're reachable. Also corrected a stale doc comment in `windows_shell.rs` that referenced a `spawn_shell_command` function which no longer exists (its body was absorbed into `bash_background::registry::spawn_detached_child` during the v0.20.x foreground-as-polled-background refactor).
