mirror of
https://github.com/akitaonrails/ai-memory.git
synced 2026-10-02 03:24:46 +08:00
OpenCode Zen/Go notified operators that requests missing an
`x-opencode-session` header may start erroring, and reported ai-memory's
traffic as "Unknown client — your requests carry no user agent, so we
can't tell what sends them". Both halves are real: `reqwest` sends no
`User-Agent` unless one is configured, and nothing in the crate could
attach a caller-supplied header.
Add `AI_MEMORY_LLM_HEADERS` (`llm_headers` in config.toml): comma-separated
`Name=Value` / `Name: Value` entries, parsed and validated once at the
configuration boundary into a typed `ExtraHeaders`, then sent on every chat
request. This follows the rule provider auth already obeys — providers
consume typed material and never re-parse operator strings — and means a
malformed entry fails at startup rather than on the first consolidation
pass. Headers ai-memory sets itself are refused rather than duplicated:
`RequestBuilder::header` appends, so a second `authorization` would break
the request instead of overriding it. Values are marked sensitive and never
logged; `Debug` prints names only.
Two defaults ride on the same mechanism, layered *under* the operator's so
an explicit entry always wins:
- `User-Agent: ai-memory/<version>` on every provider. Not opencode-specific:
an unattributable request is the one a rate limiter throttles first, and
any gateway benefits from knowing what called it. Copilot is excluded — it
keeps `GitHubCopilotChat/<version>`, the agent GitHub's API expects.
- `x-opencode-session` on the `opencode` provider, one id per process, since
that header is Zen/Go's own request-correlation field.
Zen/Go permits this. Its documentation ("Where can I use it?", docs/go.mdx)
says Go "is designed to be used with OpenCode and other popular coding
agents that produce a similar types of requests", documents the
`https://opencode.ai/zen/go/v1/...` endpoints for direct use, and asks that
the calling tool "does not generate abusive traffic" and "properly
identifies itself (no broad user agents)". Hence naming ai-memory in the
agent string rather than copying OpenCode's own — identifying the caller is
the requirement, and impersonation would defeat it.
Integration tests drive `build_provider` against wiremock: asserting the
header is on the struct is not the same claim as asserting the right single
value is on the wire.