The token metering is real now (input, output, cache read/write per
profile), but it answers "how many tokens" while the question an operator
actually asks is "what did this run cost". Converting requires per-profile
prices, which nowhere exist.
Changes:
- llm_profiles gains four optional price columns (EUR per 1M tokens:
input, output, cache read, cache write; 0/NULL = unset), migrated
idempotently and round-tripped through SaveProfile / the profile API
in one small auditable write
- the usage aggregation computes cost_eur per profile from the stored
prices, in Go after the archived-task merge so cold-storage tokens are
priced identically; profiles without prices keep cost_eur nil instead
of showing a fake zero
- web types carry cost_eur and the price fields; the dashboard card and
profile-dialog inputs consume them in a follow-up (the API serves the
data already)
- integration test: priced profile + usage rows -> expected EUR to the
cent (2M in @1.5 + 0.5M out @4 + 1M cache-read @0.15 = 5.15), plus a
prices round-trip check
Note: the Go side activates on the next build; the schema migration is
idempotent at startup.
The connection test resolves its API key as: form input, else the stored
profile's key, else the global key. Form keys pass through ConfigFrom,
which trims surrounding whitespace; the stored-key fallback assigned
p.APIKey raw. A key pasted with a trailing space or newline therefore
behaved asymmetrically: conversations worked (the runtime path loads the
profile through ConfigFrom and trims) while the connection test sent the
malformed header and failed auth with 401. SaveProfile had the same gap
one layer down, storing the raw pasted value.
Changes:
- the llm/test fallback now trims the stored key and session header name,
matching ConfigFrom's normalization so the test sends byte-identical
headers to what the runtime sends for the same profile
- SaveProfile trims the key before storing (and before deriving the
hint), so surrounding whitespace never reaches the database
- tests: an integration test saves a profile with a whitespace-padded
key, runs the real handler against a fake anthropic endpoint, and
asserts the endpoint received the trimmed key; a second test pins the
ConfigFrom trim the fallback must match
Every connect attempt (discovery, refresh, and the per-agent-run wiring) now
records its outcome on the server row: last_connect_at/ok/err, written through
a change-deduped update so per-run wiring does not amplify writes. The MCP page
shows a health chip per card (ok with time, or the failure reason), and a new
read-only mcp_status tool (bound to auto, worker and pentest) lets a worker
notice that a server it expected is down and say so instead of silently
degrading while the cached tool count keeps the card looking healthy.
Closes#38
Add a rea skill (skill-gated MCP): agents get binary and application package
reverse engineering through a rea MCP server, with tools kept deferred so the
172-tool catalog never bloats the prompt until the skill loads.
Remote MCP servers now take timeout_seconds (default 120): analysis servers
with minutes-long tool calls were cut off mid-call by the fixed 120s client
timeout. Applies to the Streamable HTTP transport; wired through discovery,
the ScopeSentry sync client, the API and the MCP dialog.
Closes#8
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