Owner request: a real app home (health, usage, costs) and a settings screen (provider, API key, test connectivity).
Provider settings (assistant_provider): type (registered AssistantProviderType, with AnthropicProviderType in the Anthropic module), model, enabled/default, base URL override, monthly cost cap (USD), max output tokens per request, max concurrent requests, and the last connection test. The default enabled row is the active provider. With no rows, the provider registered in code answers, so current hosts keep working.
API keys are never stored. A row names the environment variable that holds the key (apiKeyEnvName). Only allow-listed names are accepted: each type's default plus allowedApiKeyEnvNames. A base URL override must be in allowedBaseUrls. apiKeyStatus is computed on read (Set • …abcd (from ANTHROPIC_API_KEY)) and dropped on save, so no read path returns the key. A pasted key is refused before it is stored. See docs/DESIGN.md for why we use a reference: core has no secret field type, and the RC.6 cipher (qqq#1123/#1107) is for backup artifacts and is unreleased.
Test connection: a record action on providers, as a standard streamed ETL with Review and Result lines and no custom form. It makes one short live call (32 output tokens max, effort low). The result is saved on the row and recorded as a TEST usage row.
assistant_usage: one row per model call (turn steps and tests) with provider, model, status, error kind, HTTP status, latency, tokens and cost. It is a child list on conversations and providers.
assistant_model_price: editable USD per million tokens, used for every call's cost. The reference DDL seeds the Anthropic list prices of 2026-10, and the config defaults now cover all current Claude models.
App home (assistantHome): health, providers, usage by day, costs against the caps, by user, top tools and recent failures. It uses real rows only, with empty states, in flow/grid layout (no tables, no inner scroll), light and dark.
Nav: the home plus Providers, Conversations, Model prices and Policy. Retention is now a table action on conversations.
the key is never returned (query, get, entity JSON), and the forged status is not stored
unsafe settings refused (pasted key, DATABASE_PASSWORD, unknown URL, bad limits)
default/active selection
the Test connection action: Review and Result lines, saved result, TEST usage rows, no-key short-circuit
per-call usage and price math, the per-request cap and the monthly cap
the home, empty and with data
New AnthropicProviderTypeTest runs Test connection against the recorded HTTP server: the key reaches the API as x-api-key; a 401 gives a readable error without the key.
Screenshots
docs/screenshots/ (local AssistantDemoServer, never production): home, provider, provider without a key, edit, prices. Light and dark at 390 and 1440.
Upgrade
Three new tables (DDL in docs/schema-postgres.sql) and withProviderType(new AnthropicProviderType()). See UPGRADE.md.
## What
Owner request: a real app home (health, usage, costs) and a settings screen (provider, API key, test connectivity).
- **Provider settings** (`assistant_provider`): type (registered `AssistantProviderType`, with `AnthropicProviderType` in the Anthropic module), model, enabled/default, base URL override, monthly cost cap (USD), max output tokens per request, max concurrent requests, and the last connection test. The default enabled row is the active provider. With no rows, the provider registered in code answers, so current hosts keep working.
- **API keys are never stored.** A row names the environment variable that holds the key (`apiKeyEnvName`). Only allow-listed names are accepted: each type's default plus `allowedApiKeyEnvNames`. A base URL override must be in `allowedBaseUrls`. `apiKeyStatus` is computed on read (`Set • …abcd (from ANTHROPIC_API_KEY)`) and dropped on save, so no read path returns the key. A pasted key is refused before it is stored. See docs/DESIGN.md for why we use a reference: core has no secret field type, and the RC.6 cipher (qqq#1123/#1107) is for backup artifacts and is unreleased.
- **Test connection**: a record action on providers, as a standard streamed ETL with Review and Result lines and no custom form. It makes one short live call (32 output tokens max, effort low). The result is saved on the row and recorded as a `TEST` usage row.
- **`assistant_usage`**: one row per model call (turn steps and tests) with provider, model, status, error kind, HTTP status, latency, tokens and cost. It is a child list on conversations and providers.
- **`assistant_model_price`**: editable USD per million tokens, used for every call's cost. The reference DDL seeds the Anthropic list prices of 2026-10, and the config defaults now cover all current Claude models.
- **App home** (`assistantHome`): health, providers, usage by day, costs against the caps, by user, top tools and recent failures. It uses real rows only, with empty states, in flow/grid layout (no tables, no inner scroll), light and dark.
- **Nav**: the home plus Providers, Conversations, Model prices and Policy. Retention is now a table action on conversations.
- Every `assistant_*` table has AI exposure `NONE`.
## Tests
`CI=true ./mvnw verify`: green. Core 78 tests, Anthropic 10, checkstyle clean, coverage gates met.
- New `AssistantSettingsTest` covers:
- metadata
- the key is never returned (query, get, entity JSON), and the forged status is not stored
- unsafe settings refused (pasted key, `DATABASE_PASSWORD`, unknown URL, bad limits)
- default/active selection
- the Test connection action: Review and Result lines, saved result, TEST usage rows, no-key short-circuit
- per-call usage and price math, the per-request cap and the monthly cap
- the home, empty and with data
- New `AnthropicProviderTypeTest` runs Test connection against the recorded HTTP server: the key reaches the API as `x-api-key`; a 401 gives a readable error without the key.
## Screenshots
`docs/screenshots/` (local `AssistantDemoServer`, never production): home, provider, provider without a key, edit, prices. Light and dark at 390 and 1440.
## Upgrade
Three new tables (DDL in `docs/schema-postgres.sql`) and `withProviderType(new AnthropicProviderType())`. See UPGRADE.md.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
What
Owner request: a real app home (health, usage, costs) and a settings screen (provider, API key, test connectivity).
assistant_provider): type (registeredAssistantProviderType, withAnthropicProviderTypein the Anthropic module), model, enabled/default, base URL override, monthly cost cap (USD), max output tokens per request, max concurrent requests, and the last connection test. The default enabled row is the active provider. With no rows, the provider registered in code answers, so current hosts keep working.apiKeyEnvName). Only allow-listed names are accepted: each type's default plusallowedApiKeyEnvNames. A base URL override must be inallowedBaseUrls.apiKeyStatusis computed on read (Set • …abcd (from ANTHROPIC_API_KEY)) and dropped on save, so no read path returns the key. A pasted key is refused before it is stored. See docs/DESIGN.md for why we use a reference: core has no secret field type, and the RC.6 cipher (qqq#1123/#1107) is for backup artifacts and is unreleased.TESTusage row.assistant_usage: one row per model call (turn steps and tests) with provider, model, status, error kind, HTTP status, latency, tokens and cost. It is a child list on conversations and providers.assistant_model_price: editable USD per million tokens, used for every call's cost. The reference DDL seeds the Anthropic list prices of 2026-10, and the config defaults now cover all current Claude models.assistantHome): health, providers, usage by day, costs against the caps, by user, top tools and recent failures. It uses real rows only, with empty states, in flow/grid layout (no tables, no inner scroll), light and dark.assistant_*table has AI exposureNONE.Tests
CI=true ./mvnw verify: green. Core 78 tests, Anthropic 10, checkstyle clean, coverage gates met.AssistantSettingsTestcovers:DATABASE_PASSWORD, unknown URL, bad limits)AnthropicProviderTypeTestruns Test connection against the recorded HTTP server: the key reaches the API asx-api-key; a 401 gives a readable error without the key.Screenshots
docs/screenshots/(localAssistantDemoServer, never production): home, provider, provider without a key, edit, prices. Light and dark at 390 and 1440.Upgrade
Three new tables (DDL in
docs/schema-postgres.sql) andwithProviderType(new AnthropicProviderType()). See UPGRADE.md.🤖 Generated with Claude Code