Add an optional per-profile User-Agent that overrides the header on every
outbound LLM request. When empty (the default), the existing behaviour is
unchanged and the default User-Agent is left untouched.
The value is trimmed and validated: control characters / newlines (header
injection) and values longer than 256 bytes are rejected. Wired through the
profile config struct, the Postgres llm_profiles schema, the profile API/DTO,
the connection-test path, and the LLM profile form, following the existing
session-header field. Addresses part of #154; the fixed-full-URL mode is not
included.
POST /api/llm/test and POST /api/llm/models take provider and base_url from
the form but fell back to the stored profile key (and, for the test, the
global key) when the form had none. Any base_url typed into the form, or
supplied by a crafted request, received the saved key.
The saved key is now used only when the request's provider and base URL
normalize to the ones it was saved with (sameLLMEndpoint, built on
agent.ConfigFrom). Otherwise the key must be entered in the form, and the
error says why.
Two review fixes on top of the Ollama provider:
FromEnv no longer inherits OPENAI_API_KEY for the ollama format. Ollama
ignores the bearer token, so inheriting the cloud key could only ship it to
the local or LAN endpoint, contradicting the comment in NewProvider and the
documented behavior.
ConfigFrom now normalizes the base URL to the /v1 root, so the natural
Ollama URL (host:11434 without /v1) works for chat as well. Previously the
provider appended /chat/completions to it and the request landed on a path
Ollama does not serve, while pgListModels already appended /v1.
Tests: bare-host normalization assertion, the /v1/chat/completions path
assertion, and a new test that the env config carries no inherited key.
Descriptions written through the API were interpolated raw into the SKILL.md
frontmatter, so a value containing a colon+space made the whole frontmatter
unparseable: every field silently stayed empty, the skill loaded under its
directory name, and a skill-gated MCP server was silently skipped. The same
applied to updates through the meta rewrite, and the shipped scopesentry skill
had a malformed name line all along.
- yamlScalar: plain-safe tokens stay bare, everything else single-quoted
- fsCreateSkill/fsUpdateSkillMeta emit safe values and re-verify the written
file with the real skill parser (name + mcps must survive), rejecting the
write otherwise
- warnBrokenSkills at startup logs skills whose name fell back to the
directory (the signature of broken frontmatter)
- fix skills/scopesentry/SKILL.md name line
Closes#39
Comments, doc comments, UI labels, log and error messages, LLM prompt
strings, markdown docs, test data and assertions. Identifiers, enum
keys, routes, placeholders and code structure are unchanged.
Notes:
- docs/漏洞流量证据.md renamed to docs/vulnerability-traffic-evidence.md
- tests that exercised CJK specific handling (GBK zip names, Chinese
full text search) now use ASCII equivalents; go build passes and the
test suite matches the pre translation baseline (remaining failures
need a PostgreSQL DSN and fail identically on the original)
- frontend mention syntax now uses English kinds (finding, asset, ...)
matching the backend, chat mentions tests pass