Two product decisions from tonight:
1. Public AI providers can now be published by an admin as named presets
(lib/ai/types.ts's PublicAiPreset: name/baseUrl/model/apiKeyEnvVar).
The admin names an env var, never a secret value - the actual key is
whatever ops has set in the server's real environment, same custody
model as the existing AI_SERVER_BASE_URL var. A new server route
(app/api/ai/public/chat) resolves it and makes the call itself, which
also sidesteps the CORS/wrong-base-URL failure class chatPublic hit
earlier tonight. Users pick a preset from a dropdown in Settings -
Answer with - no key field at all; personal BYOK (paste your own key)
stays available as a secondary "Add your own key" option, not removed.
Admin UI: new "Public - org-managed presets" card in the AI policy tab.
2. AI now defaults ON instead of requiring setup (lib/ai/auto-provision.ts):
on first load, if no provider is chosen yet, probe OpenCode (this app
auto-spawns `opencode serve` itself, so it's the one local option with
zero external install step) then Ollama via the existing auto-discovery,
and adopt whichever answers. Never overrides an explicit choice - only
fires while provider is still null. Wired into both AI entry points
(the Ask button and the Settings pane) so it resolves before either
renders its "not configured" state.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
New admin tab "AI" (app/(main)/admin/_tabs/ai-policy.tsx): provider-class
toggles, server model allow-list, BYOK provider allow-list, seats/usage
(front-end for the already-real lib/ai/entitlement.ts), retrieval on/off,
consent text + version bump.
Real backend, not cosmetic: AiConsoleConfig persisted via config-manager
(lib/ai/types.ts, ai-policy.json in the CONFIG dir). New GET/PUT
/api/admin/ai/policy. Enforcement wired at every real chokepoint, not just
the picker: /api/ai/server/chat checks classesEnabled.server and the model
allow-list, /api/ai/retrieve checks retrievalEnabled, /api/ai/server/models
filters by allow-list. GET /api/ai/policy folds classesEnabled into the
classes list clients see.
Resolved the spec's 3 open questions as recommended: BYOK allow-list stays
client-side/advisory (wired into ai-assistant-settings.tsx's addProfile),
tier picker stays cosmetic, master aiAssistantEnabled toggle stays in the
existing Policy tab (this tab links to it instead of duplicating it).
Defaults preserve today's behavior exactly (classesEnabled/allowlists all
start empty/null) — turning this on changes nothing until an admin touches it.