This repository has been archived on 2026-04-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
core/docs/ROADMAP.md
Jeffrey Smith 45fe965c32 Changeset 0.22.5 (#147)
Co-authored-by: Jeffrey Smith <jasafpro@gmail.com>
Co-committed-by: Jeffrey Smith <jasafpro@gmail.com>
2026-03-03 09:58:23 +00:00

71 KiB
Raw Blame History

Roadmap — Chat Switchboard

See also:

  • ARCHITECTURE.md — Core services design, store layer, scope model
  • EXTENSIONS.md — Extension system spec (Browser/Starlark/Sidecar tiers, manifests, browser tool bridge, surfaces/modes, model roles)
  • CHANGELOG.md — Detailed release notes for all completed versions

Versioning (pre-1.0): 0.<major>.<minor> — hotfixes use quad: 0.x.y.z No compatibility guarantees before 1.0.


Dependency Graph

Features have real dependencies. This ordering respects them.

v0.9.x  Stability + Quick UX Wins              ✅
          │
v0.9.3  Content Visibility & Block Controls     ✅
          │
v0.9.4  API Key Encryption + Vault              ✅
          │
v0.10.0 Model Roles (utility + embedding)       ✅
        + Usage Tracking
          │
v0.10.2 Summarize & Continue                    ✅
          │
v0.10.3 Frontend Refactor                       ✅
          │
v0.10.4 Model Type Pipeline + Role Fixes        ✅
          │
v0.10.5 UI Primitives + Extension Surfaces      ✅
          │
v0.11.0 Extension Foundation (browser tier)     ✅
          │
  ┌───────┴──────────────┐
  │                      │
v0.12.0 File Handling  v0.13.0 Admin Panel
  + Vision ✅            Refactor (fullscreen) ✅
  │                      │
  │                    v0.13.1 Web Search
  │                      + url_fetch ✅
  │                      │
  └───────┬──────────────┘
          │
v0.14.0 Knowledge Bases ✅ (embedding role + file storage + pgvector)
          │
v0.15.0 Compaction ✅ (utility role + background job)
          │
v0.15.1 Context Recall Tools ✅ (attachment_recall, conversation_search)
          │
  ┌───────┴──────────────┐
  │                      │
v0.16.0 User Groups    v0.17.0 Persona-KB Binding
  + Resource Grants ✅   + Enterprise KB Mode
  │                      + QOL / UX Wins
  └───────┬──────────────┘
          │
  ┌───────┴──────────────┐
  │                      │
v0.17.1 SQLite Backend ✅ v0.17.2 CodeMirror 6 ✅
v0.17.3 Notes Graph + Wikilinks ✅
  (dual DB)              (editor bundle, chat
  │                       input, ext editor)
  └───────┬──────────────┘
          │
v0.18.0 Memory ✅ (user + persona scopes, review pipeline)
          │
v0.18.1 Side Panel Architecture ✅ (single-slot, ctx.ui primitives, mermaid refactor)
          │
v0.19.0 Projects / Workspaces ✅ (project groups, KB resolution, drag-drop sidebar)
          │
v0.19.1 Active Project + System Prompt + Detail Panel ✅
          │
v0.19.2 Project Persona Default + Archive + Reorder ✅
          │
v0.20.0 Notifications + @mention Routing + Multi-model   ✅
          │
v0.21.0 Workspace Storage Primitive                      ✅
          │
  ┌───────┴──────────────┐
  │                      │
v0.21.1 Workspace ✅   v0.21.3 Surface Infra ✅
  Tools + Bindings       + REPL (parallel)
  │                      │
v0.21.2 Workspace ✅   v0.21.5 Editor Surface ✅
  Indexing + Search      (Development Mode)
  │                      │
v0.21.4 Git ✅         v0.21.6 Editor Surface ✅
  Integration            + Document Output
  └───────┬──────────────┘
          │
v0.21.7 Bugfix: scanJSON driver buffer aliasing   ✅
          │
v0.22.0 Provider Health + Capability Overrides     ✅
  + Workspace Pane Refactor (FE)
          │
v0.22.1 Provider Extensions (declarative config)   ✅
          │
v0.22.2 Routing Policies + Fallback Chains          ✅
          │
v0.22.3 Provider Admin UI + Deferred Polish          ✅
          │
v0.22.4 Provider Health UX + Project Files + Export   ✅
          │
v0.22.5 Surfaces + Go Templates + Admin Bug Fixes     ✅
          │
v0.23.0 Multi-Participant Channels + Presence
          │
v0.24.0 Auth Strategy (mTLS/OIDC) + Full RBAC + Group permissions
          │
  ┌───────┴──────────────┐
  │                      │
v0.25.0 Workflow       v0.26.0 Tasks /
  Engine                 Autonomous Agents
  (team-owned,           (service channels,
   staged processes,      scheduler, unattended
   human + AI collab)     execution)

Completed Releases

Full details for all completed versions are in CHANGELOG.md.

v0.9.0 — Schema Consolidation + BYOK

21 migrations → single schema, store layer abstraction, persona-as-trust-boundary, BYOK with auto-fetch, composite model IDs, journey integration tests.

v0.9.1 — Capability Architecture + Docs

Removed static known model table. Resolution chain: catalog → heuristic only. Frontend relies solely on backend for capabilities. Docs rewrite.

v0.9.2 — Quick UX Wins + Hardening

Collapsible code blocks, HTML preview, token counter, context warning, proxy interception detection, environment injection, team admin audit scoping.

v0.9.3 — Content Visibility & Block Controls

Button-driven code collapse, always-rendered thinking blocks, tool call persistence in history, Notes panel → unified side panel, streaming refactor (streamWithToolLoop), regenerate fix.

v0.9.4 — API Key Encryption + Vault

Two-tier AES-256-GCM: env-var key for global/team, per-user UEK (Argon2id) for personal BYOK. server/crypto/ package. Migration 003. Startup backfill. DOMPurify strict allowlist (XSS fix).

v0.10.0 — Model Roles + Usage Tracking + Vault Debt

Named role slots (utility, embedding) with fallback. Provider.Embed() interface. usage_log + model_pricing tables. Streaming token capture. Vault debt: UEK re-wrap on password change, destruction on admin reset.

v0.10.1 — Polish + UX

Admin system prompt injection, Personas tab, Team Management modal extraction, preview pane enhancements, code download, personal usage scoped to BYOK.

v0.10.2 — Summarize & Continue + Role Cleanup

User-triggered conversation compaction via utility role. Summary as tree node with boundary metadata. BYOK role overrides (personal → team → global). Utility rate limiting. Generation role removed.

v0.10.3 — Frontend Refactor

2 monolith files → 13 domain-scoped files (~544 lines avg). Zero features, no function renames. Vanilla JS, no build step. See REFACTOR-0.10.3.md.

v0.10.4 — Model Type Pipeline + Role Fixes

model_type (chat/embedding/image) captured end-to-end from provider API through catalog to frontend. Role dropdowns filter by type.

v0.10.5 — UI Primitives + Extension Surfaces

Shared Providers/Roles registries, 6 consolidated render primitives, showConfirm() replacing native dialogs, .popup-menu CSS primitive. Removed ~400 lines of duplication.

v0.11.0 — Extension Foundation (Browser Tier)

Full extension lifecycle: manifest, loader, scoped ctx.* API, browser tool bridge via WebSocket. Custom renderer pipeline. 2 server tools (calculator, datetime), 6 built-in browser extensions (Mermaid, KaTeX, CSV, Diff, JS Sandbox, Regex). See EXTENSIONS.md.

v0.12.0 — File Handling + Vision

S3/PVC storage backend, image/file upload (📎, drag-drop, paste), multimodal message assembly, text extraction pipeline (PDF/DOCX/XLSX/PPTX/ODT/RTF), vault CLI (rekey/status), per-chat model persistence.

v0.13.0 — Admin Panel Refactor

12-tab modal → fullscreen admin panel. 4 categories (People, AI, System, Monitoring) × section sidebar. URL-based routing, responsive, banner-aware. CSS design token cleanup.

v0.13.1 — Web Search + URL Fetch

web_search + url_fetch tools. Search provider abstraction (DuckDuckGo, SearXNG). Tool categories with per-tool toggle UI in chat bar.

v0.14.0 — Knowledge Bases

RAG: upload → chunk → embed (pgvector) → kb_search tool. Channel KB toggle. Team/personal KB scopes. Notes semantic search. Admin panel KB management. See DESIGN-0.14.0.md.

v0.15.0 — Compaction

Background scanner with configurable thresholds. Automatic summarization via utility role. Per-channel opt-in/out. Context budget guard rail (80% ceiling).

v0.15.1 — Context Recall Tools

attachment_recall (list + read, channel-scoped), conversation_search (full-text via plainto_tsquery). Token estimator attachment awareness.

v0.16.0 — User Groups + Resource Grants

Groups (global/team-scoped ACLs) decoupled from team membership. Three-way resource grants (team_only/global/groups) for Personas and KBs. Grant picker UI. Schema consolidation: 9 migrations → single 001_v016_schema.sql.


v0.17.0 — Persona-KB Binding + Enterprise KB Mode + QOL

Personas become gateways to knowledge. In enterprise deployments, users don't interact with KBs directly — they talk to Personas that have KBs attached. Plus quality-of-life improvements: chat rename, token display, state persistence, utility model auto-naming.

Depends on: knowledge bases (v0.14.0), user groups (v0.16.0).

Persona-KB Binding

  • persona_knowledge_bases table: persona_id, kb_id, auto_search (bool — always search vs tool-only)
  • When a channel uses a Persona with bound KBs, kb_search is auto-scoped to those KBs
  • No user toggle needed — Persona carries its own KB context
  • Channel KB toggle becomes "additional KBs" on top of Persona-provided ones
  • Persona system prompt auto-includes KB listing hint (reuses BuildKBHint pattern)
  • Admin/team admin UI: KB picker on Persona create/edit form

Enterprise KB Mode

  • discoverable flag on knowledge_bases: true (default — appears in user's KB popup) or false (hidden, only accessible through Persona binding)
  • Hidden KBs don't appear in GET /api/v1/knowledge-bases-discoverable for non-admins
  • Hidden KBs still searchable by kb_search tool when accessed through a Persona
  • Admin panel: toggle discoverability per KB
  • Platform policy: kb_direct_access — when false, disables channel KB popup entirely (strict enterprise mode)

Access Control Integration

  • Persona grants (v0.16.0 groups) control who can use a Persona
  • KB grants control who can manage a KB (upload/delete docs)
  • Persona-KB binding grants implicit search access through the Persona
  • Users who can use a Persona can search its KBs, even if they can't see those KBs directly

Team Admin Workflow

  • Team admin creates KB → uploads documents → marks non-discoverable
  • Team admin creates Persona → binds KBs → sets access to Team Only or specific Groups
  • Team members see the Persona in their preset list, use it, get KB-powered answers
  • Team members never see or manage the underlying KBs

Technical Debt (carried forward)

  • Security: KB create handler authorization — team/global scope verified
  • Test hygiene: stripped DIAG diagnostics from TestGroupBasedPersonaAccess
  • Architecture: KnowledgeBaseStore.UpdateDocumentStorageKey() — moved to store layer
  • UX: Embedding dropdown fix — tolerant type filter, manual model ID fallback, auto-switch on empty (already implemented in ui-primitives.js)
  • Minor: Paste-to-file character threshold — synced from backend PublicSettings (storage.paste_to_file_chars), admin-configurable, default 2000

Role Fallback Alerts (#69)

  • Audit log entry on fallback fire: action = "role.fallback" with primary/fallback model IDs and error string
  • role.fallback WebSocket event via EventBus (PublishAsync, zero latency impact on completion path)
  • In-memory cooldown on Resolver (per-role, 5-minute TTL) to deduplicate repeated fallback alerts
  • Admin UI: persistent alert banner on role.fallback event (persists until dismissed)
  • Wire *events.Bus into roles.NewResolver via .WithBus() builder

QOL / UX Wins

  • Chat rename: inline edit on sidebar item — dblclick title → input → blur/Enter saves via API.updateChannel(id, { title })
  • Chat token count: display Tokens.estimateConversation() in model bar, update on selectChat and after each message
  • State restore on refresh: persist App.currentChatId to sessionStorage on select, restore on load
  • Utility model auto-naming: after first assistant response, background utility role request → POST /channels/:id/generate-title → re-render sidebar. Fallback to truncation when no utility model configured. Debounced via _autoNamePending set
  • SW extension cache fix: exclude /extensions/ from service worker fetch handler
  • Mermaid renderer: promoted enhanced version (v2.0 — pan/zoom, SVG/PNG export, source copy) to extensions/builtin/mermaid-renderer/

v0.17.1 — SQLite Backend

Dual-database support. Every subsequent schema change is developed against both Postgres and SQLite from day one — no retrofit. SQLite enables single-binary deployment for dev, demo, edge, and single-user.

Depends on: v0.17.0 DB debt cleanup (store layer is sole DB interface).

Store Layer

  • 19 SQLite store implementations covering all domain interfaces
  • DB_DRIVER env var: postgres (default) | sqlite
  • Separate migration files per driver (migrations/sqlite/)
  • WAL mode + single-writer connection pooling for SQLite concurrency
  • CI matrix: full handler integration tests against both Postgres and SQLite

Vector Search

  • App-level cosine similarity in Go (pure Go, no CGO/sqlite-vec required)
  • Embeddings stored as JSON text in kb_chunks.embedding column
  • Full feature parity with pgvector: SimilaritySearch, InsertChunks
  • Graceful: adequate performance for SQLite-scale deployments (single-user, not millions of chunks)

Schema Compatibility

  • UUID generation: application-side store.NewID() (Go uuid.New())
  • JSONB → JSON text columns with ToJSON()/ScanJSON() helpers
  • UUID[] arrays → JSON arrays with ToJSONArray()/ScanJSONArray()
  • TIMESTAMPTZ → SQLite TEXT with datetime('now')
  • ON CONFLICT excluded.* pattern (works in both dialects)
  • INSERT RETURNING id → separate query pattern for SQLite

Test Infrastructure

  • dialectSQL() converts $N? and strips ::jsonb at runtime
  • database.PH(n) returns dialect-appropriate placeholder
  • database.TruncateAll() dialect-aware (DELETE + PRAGMA vs TRUNCATE CASCADE)
  • Generic provider config: PROVIDER/PROVIDER_KEY/PROVIDER_URL (replaces VENICE_API_KEY)
  • Model selection prefers non-reasoning models in live tests

What SQLite mode skips (feature-gated, not broken):

  • LISTEN/NOTIFY — WebSocket event fan-out uses in-process EventBus (already works)
  • Full-text search tsvector — conversation_search uses LIKE fallback

v0.17.2 — CodeMirror 6 Integration

Rich editor infrastructure for the frontend. CM6 is ESM-native; the build step runs in Docker (esbuild → single IIFE bundle). No change to dev experience for non-editor code — the rest of src/js/ stays vanilla.

Depends on: nothing (frontend-only). Prerequisite for: extension surfaces (v0.21.5 editor surface). See DESIGN-CM6.md for full spec.

Build Pipeline

  • src/editor/ directory: package.json, build.mjs, index.mjs, language/theme modules
  • scripts/build-editor.sh: shared build script for both Dockerfiles and local dev
  • New Docker stage (cm6-build): npm ci + node build.mjsvendor/codemirror/
  • Both Dockerfile.frontend and Dockerfile (unified) use the shared build script
  • Bundle output: codemirror.bundle.js (~295KB min, ~90KB gzip)
  • Graceful degradation: all integration points check window.CM — falls back to <textarea> if bundle unavailable

Languages (bundled)

  • Markdown, JavaScript, JSON, SQL, HTML, CSS, YAML, Go, Python, Rust
  • Additional modes: one-line import + rebuild

Phase 1: Extension Editor (admin panel)

  • CM.codeEditor() factory: line numbers, bracket matching, auto-indent, search/replace
  • Replace manifest <textarea> → CM6 JSON mode
  • Replace script <textarea> → CM6 JavaScript mode
  • Remove manual Tab-key handler in admin-handlers.js (CM6 handles it)
  • Update saveAdminExtension() to use .getValue()

Phase 2: Chat Input (markdown mode)

  • CM.chatInput() factory: markdown highlighting, minimal chrome (no line numbers, no gutter)
  • Enter=send, Shift+Enter=newline keybindings
  • Auto-growing height, placeholder text
  • Wire onChangeupdateInputTokens()
  • Update chat.js, attachments.js, tokens.js to use CM API
  • WYSIWYG fenced code block decorations (visual container matching claude.ai UX)
  • Inline code shortcut (Ctrl/Cmd+E)

Phase 3: Polish

  • Theme integration: CSS variable overrides (--bg, --border, --accent, etc.)
  • Dark/light/system mode toggle in appearance settings
  • Vim/Emacs keybinding preference in appearance settings (@replit/codemirror-vim, @replit/codemirror-emacs)
  • Vim/Emacs applies to code editor + extension editor only (never chat input)
  • SW cache: exclude vendor/codemirror/ with version bust
  • Update debug.js snapshot to include CM6 version
  • Documentation in ARCHITECTURE.md

CI/CD

  • Path-based change detection gating (FE-only → skip BE tests, docs-only → skip all)

Knowledge graph and bi-directional linking for the notes system. Transforms notes from flat documents into an interconnected knowledge base with Obsidian-style [[wikilinks]], backlinks, graph visualization, and transclusion.

Depends on: CodeMirror 6 (v0.17.2), Notes CRUD (v0.13.x). See DESIGN-0.17.3.md for full spec.

Backend: Note Links

  • note_links table (Postgres + SQLite migrations)
  • NoteLinkStore interface + both implementations
  • ExtractWikilinks() regex parser + tests
  • Link extraction on note create/update
  • Dangling link resolution on note create
  • notes.source_message_id column for chat-to-note provenance

API Endpoints

  • GET /notes/:id/backlinks — notes linking to this note
  • GET /notes/search-titles?q= — lightweight fuzzy search for autocomplete
  • GET /notes/graph — full graph topology (nodes + edges + unresolved)

CM6 Note Editor

  • note-editor.mjs factory: markdown live preview (heading sizes, blockquotes, code blocks)
  • wikilink.mjs CM6 extension: parse, decorate as clickable chips, autocomplete on [[
  • Textarea → CM6 swap in notes.js with graceful fallback
  • noteEditorTheme in theme.mjs

Graph Visualization

  • Canvas-based force-directed graph (~200 lines, no d3 dependency)
  • O(n²) Coulomb repulsion + Hooke spring + center gravity
  • Pan, zoom (0.154.0x), drag nodes, hover highlight, click to open
  • Node sizing by link_count, folder-based coloring
  • Ghost nodes for unresolved [[links]] with toggle
  • Energy-based pause (stop rAF below KE threshold)
  • ResizeObserver for responsive canvas

Note-from-Selection + Daily Notes

  • "Note" button in message action bar
  • Selection-aware capture (window.getSelection() within message)
  • Provenance tracking (source_channel_id + source_message_id)
  • "Today" button → find-or-create daily note in /daily/

Read Mode

  • [[link]] rendered as clickable wikilink chips
  • ![[embed]] transclusion with async content fetch, recursion guard
  • Backlinks panel (collapsible, with count badge)

v0.18.0 — Memory (User + Persona Scopes)

Long-term memory across conversations, with scope-aware isolation. Unlike KB (static documents) or compaction (within-conversation), this captures facts and preferences that persist across channels.

Key differentiator: Persona-scoped memory. Each Persona can build its own memory independently of user memory, preventing cross-context contamination and enabling specialized knowledge accumulation.

Depends on: compaction (v0.15.0), knowledge bases (v0.14.0), persona-KB binding (v0.17.0), SQLite backend (v0.17.1).

See DESIGN-0.18.0.md for full spec.

Memory Scopes

  • user — personal facts/preferences, persists across all conversations (current industry standard)
  • persona — shared across all users of that Persona, accumulated from conversations (differentiator)
  • persona+user — per-user within a Persona context (e.g. tutoring progress per student)
  • Configurable per Persona: which scopes are active, whether user memory is passed through

Data Model

  • memories table: id, scope (user, persona, persona_user), owner_id (user_id or persona_id), user_id (nullable — for persona+user scope), key, value, source_channel_id, confidence (float), status (active, pending_review, archived), created_at, updated_at
  • Index on (scope, owner_id, user_id) for fast lookup
  • Embedding column (vector(3072)) for semantic memory search

Tools

  • memory_save tool: LLM explicitly stores a fact
    • Scope-aware: saves to the active memory scope for the current Persona context
    • Structured: key (short label) + value (detail) + confidence
  • memory_recall tool: LLM queries stored facts relevant to current context
    • Merges results from applicable scopes (user + persona + persona+user)
    • Semantic search via embeddings + keyword fallback

Automatic Extraction

  • Background job: post-conversation analysis identifies memorable facts (opt-in)
  • Configurable extraction prompt per Persona (e.g. "extract FAQ-worthy Q&A pairs")
  • Extracted memories start in pending_review status

Review Pipeline

  • Admin/team admin review queue: pending memories with approve/reject/edit
  • Persona memory review: team admins see what Personas are "learning"
  • User memory review: users see their own memories in Settings

Memory Injection

  • At completion time: inject relevant memories into system prompt
  • Context budget aware: limit injected memory tokens
  • Scope priority: persona+user > persona > user (most specific wins on conflicts)

Use Cases

  • Helpdesk Persona: builds FAQ from repeated questions, human-reviewable
  • Roleplay Persona: isolated memory prevents tone/context bleeding from user's other conversations
  • Tutoring Persona: tracks per-student progress, common misconceptions
  • Sales Persona: accumulates objection responses from real conversations
  • Onboarding Persona: learns what new hires commonly struggle with

User Controls

  • Settings → Memory: view, edit, delete personal memories
  • Per-Persona memory toggle: "Allow this persona to remember things about me"
  • Admin: enable/disable, retention policies, per-team scoping
  • Privacy: user memories never cross team boundaries; persona memories respect group access

v0.18.1 — Side Panel Architecture

Refactored the side panel from a shared-tab layout to independent single-slot panels. Any action fills the slot, replacing whatever was there — no tabs, no association between panel types. Extension UI primitives give browser extensions safe access to host app features.

Depends on: Notes graph + wikilinks (v0.17.3). Prerequisite for: live collaboration (v0.23.0) where multiple panels need simultaneous visibility.

Panel System

  • Panel registry: named panels with independent open/close state
  • Single-slot model: action-driven, each open replaces the previous
  • Dual-view mode: two panels side-by-side (configurable split ratio via drag divider)
  • Panel-persistent state: each panel remembers scroll position, open note, etc.
  • Header label shows active panel name (replaces tab bar)
  • Keyboard shortcut: Ctrl/Cmd+\ cycles panels, Ctrl/Cmd+Shift+\ toggles dual

Preview Panel

  • Dedicated preview panel (independent of Notes)
  • HTML/document preview with iframe sandbox
  • Code output preview (extension-rendered blocks via pop-out)
  • Live-updating during streaming responses (500ms debounce)

Extension UI Primitives

  • ctx.ui.toast() — toast notifications
  • ctx.ui.openPreview() — load HTML into side panel preview
  • ctx.ui.isDark() — theme detection
  • ctx.ui.isMobile() — viewport width check
  • ctx.ui.isPanelOpen() — side panel visibility
  • ctx.ui.confirm() — modal confirm dialog
  • Mermaid extension refactored to use primitives exclusively (zero direct globals)
  • Context-aware expand: single button, fullscreen or side panel pop-out

Mobile

  • Swipe navigation between panels
  • Tap-to-close overlay
  • Graceful collapse to single-panel on narrow viewports
  • Enlarged touch targets, fullscreen close button (40px desktop, 48px mobile)

v0.19.0 — Projects / Workspaces

Organizational containers that group related conversations, KBs, and notes into a single workspace with shared access controls.

Depends on: user groups (v0.16.0), knowledge bases (v0.14.0).

Data Model

  • projects table: id, name, description, color, icon, scope (personal/team/global), owner_id, team_id, is_archived, settings (JSONB)
  • project_channels junction: ordered membership with UNIQUE channel_id, denormalized FK on channels.project_id
  • project_knowledge_bases junction: with auto_search flag
  • project_notes junction
  • resource_grants CHECK extended to include 'project'

Features

  • Channels can belong to a project (optional project_id FK)
  • KBs can belong to a project → auto-linked to all channels in the project (BuildKBHint + kbsearch)
  • Notes can belong to a project → auto-associated when created from project channel
  • KB resolution chain: Persona → Project → Channel → Personal
  • Project sidebar section: collapsible groups above time-based Recent section

UI

  • Sidebar: projects as collapsible groups with color dots, counts, options menu
  • Right-click context menu: move chat to project / remove / create-and-assign
  • Drag-and-drop channels between project groups and Recent
  • New Project button in split dropdown
  • Channel list project_id filter (?project_id=uuid or ?project_id=none)
  • Collapse state persisted in localStorage

Deferred to later versions:

  • Project creation dialog (name, description, default Persona, default KBs) — using prompt() for now
  • Project detail panel (KB + note management within a project) → v0.19.1
  • Project-level Persona default → v0.19.2
  • Folders within projects
  • Project templates
  • Admin-level team/global project management
  • Project-specific files (full project-level upload — deferred for proper architecture)
  • Notifications (moved to v0.20.0)

v0.19.1 — Active Project + Project System Prompt + Detail Panel

Quality-of-life improvements making projects usable for daily workflow. No migrations.

Depends on: v0.19.0 Projects/Workspaces.

Active Project

  • Pin a project as active — new chats auto-assign via addChannelToProject after channel creation
  • App.activeProjectId persisted in localStorage('cs-active-project')
  • Visual indicator: .project-group.active accent border + 📌 icon
  • Toggle from project ⋯ menu ("Pin as active" / "Unpin project")
  • Cleared on project delete

Project System Prompt

  • Per-project instructions in projects.settings JSONB (system_prompt key)
  • Injected in loadConversation between persona/channel prompt and KB hint
  • Merge semantics on update: reads existing JSONB, overlays patch keys, writes back
  • Both Postgres and SQLite stores updated

Project Detail Panel

  • Registered with PanelRegistry (same pattern as Notes, Preview)
  • System prompt textarea with Save button
  • KB management: list with names (LEFT JOIN), [+ Add] picker dropdown, [✕] remove
  • Notes list with titles (LEFT JOIN), [✕] unbind
  • Accessible from project ⋯ menu → "Project settings"

Bug Fixes

  • renderChatList early return prevented project groups from rendering when zero chats
  • Recent section drop target: "Drop chats here to unassign" hint with min-height: 40px

v0.19.2 — Project Persona Default + Archive + Channel Reorder

Completes the project management feature set. No migrations.

Depends on: v0.19.1.

Project Persona Default

  • Bind a persona to a project via detail panel dropdown
  • Stored in projects.settings.persona_id
  • Resolution chain: explicit req.PresetID → project persona → none
  • Inherits model, provider config, temperature, max tokens, system prompt as defaults

Project Archive Toggle

  • Checkbox in project detail panel
  • Archived projects hidden from sidebar by default
  • "▸ Show archived (N)" toggle button below project list
  • Chats in hidden-archived projects spill into Recent section (never vanish)
  • Dimmed + italic visual treatment for archived groups

Channel Reorder

  • loadProjectChannelPositions() fetches server-persisted positions on startup
  • _getReorderedProjectChats() sorts project chats by stored position
  • Right-click context menu: ↑ Move up / ↓ Move down (optimistic swap + API call)
  • Order maintained across add/remove/move operations

v0.20.0 — Notifications + @mention Routing + Multi-model

Notification infrastructure that makes collaboration real-time, plus multi-model routing for channels. Three-phase delivery: notifications core (in-app), @mention parsing + fan-out completions, email transport

  • per-user notification preferences.

(Notifications shifted from v0.19.0; @mention routing shifted from v0.17.0)

Depends on: v0.19.2 (Projects complete), EventBus + WebSocket hub (v0.9.x), channel_models table (v0.16.0 schema). See DESIGN-0.20.0.md for full spec.

Phase 1 — Notifications Core (in-app)

  • notifications table: id, user_id, type, title, body, resource_type, resource_id, is_read, created_at (Postgres + SQLite migrations)
  • NotificationStore interface + both implementations: Create, ListByUser (paginated), MarkRead, MarkAllRead, Delete, UnreadCount
  • notifications.Service with Notify() / NotifyMany() — centralized creation + WebSocket dispatch
  • EventBus routes: notification.new, notification.read (badge sync across tabs)
  • API endpoints: list, unread-count, mark-read, mark-all-read, delete (5 endpoints, all user-scoped)
  • Initial sources: role.fallback (EventBus subscription), kb.ready/kb.error, grant.changed
  • Frontend: bell icon + unread badge (capped 9+), notification dropdown (latest 10, click-to-navigate), full panel via PanelRegistry
  • WebSocket handler: real-time push + toast for high-priority types
  • Background cleanup goroutine (configurable retention, default 90 days)

Phase 2 — @mention Parsing + Multi-model Routing

  • mentions.Parse(): extract @mentions, resolve against channel model roster (case-insensitive, longest-match-first, trailing punctuation tolerant)
  • Parser tests: no mentions, unresolved, multiple, overlapping names, punctuation edge cases
  • Channel model CRUD handlers (4 endpoints: list, add, remove, update)
  • Completion handler: mention extraction → target resolution → sequential fan-out → N assistant messages
  • SSE streaming: model attribution in delta events (model_display field)
  • Frontend: model pills in chat header with add/remove UI
  • Frontend: @mention autocomplete via CM6 mentionCompletion extension (roster-backed)
  • Frontend: model attribution labels on multi-model assistant messages
  • Backward compat: channels with no extra channel_models rows work exactly as before

Phase 3 — Email Transport + Notification Preferences

  • notification_preferences table (Postgres + SQLite): per-user, per-type in_app/email toggles
  • Preference resolution chain: specific type → user wildcard * → system default (in_app=true, email=false)
  • EmailTransport: SMTP with implicit TLS (port 465) and STARTTLS (port 587), multipart MIME (HTML + plaintext)
  • Email templates: branded HTML + plaintext, domain-prefixed subjects, Go html/template
  • Preference CRUD endpoints (3: list, set with partial patch, delete)
  • Admin SMTP configuration: enable toggle, host/port/user/password/from/TLS fields, test email button
  • User notification preferences UI (Settings → Notifications tab, per-type checkboxes)
  • Notification service: preference check before dispatch, async email delivery (goroutine, 30s timeout)
  • Tests: preference resolution chain (7 tests), email templates (4 tests)
  • Deferred: digest mode (batched email), SMTP password vault encryption

Hotfix (carried forward from v0.19.2 investigation)

  • user_model_settings NULL scan fix: COALESCE on hidden/sort_order in SELECT + INSERT for both dialects
  • Gin release mode: auto-set gin.ReleaseMode when ENVIRONMENT=production, suppress health check log spam

v0.21.x — Workspace Platform + Extension Surfaces

Seven-release series decomposing workspace storage, tools, indexing, git, surface infrastructure, and editor/article modes. Full specification in DESIGN-0.21.0.md.

Depends on: file storage (v0.12.0), extension foundation (v0.11.0), CodeMirror 6 (v0.17.2), knowledge base embedding pipeline (v0.14.0).

v0.21.0 — Workspace Storage Primitive

Pure backend foundation. Workspaces as a polymorphic platform primitive owned by users, projects, channels, or teams. Dual-layer storage: PVC filesystem (source of truth) + DB metadata index (queryable cache).

  • workspaces table: polymorphic owner (user/project/channel/team), root_path, max_bytes quota, status
  • workspace_files table: metadata index (path, MIME type, size, sha256, is_directory)
  • WorkspaceStore interface: CRUD, file index (upsert/delete/list/prefix), ownership lookup, stats
  • Postgres + SQLite store implementations with upsert-on-conflict for file index
  • workspace.FS package: read, write (atomic via temp+rename), delete, mkdir, stat, list, reconcile (FS→DB sync)
  • workspace.Archive: zip/tar.gz extract + create with archive bomb protection (10K file limit, 100MB single file, quota enforcement), common-prefix stripping
  • Content type detection: extension-based (40+ source code types) with http.DetectContentType fallback
  • SHA256 computed during write via tee reader (enables content-addressed skip in v0.21.2)
  • Path traversal guards: cleanPath + absPath validation, symlink rejection
  • 15 API endpoints: workspace CRUD, file CRUD, archive upload/download, reconcile, stats
  • Owner-based authorization: user=self, channel=owner, project=member, team=member
  • Unit tests: path cleaning, traversal detection, MIME detection, unsafe path filtering, write/read round-trip, delete, mkdir

v0.21.1 — Workspace Tools + Channel/Project Binding

Make workspaces useful in chat mode via tool calls.

  • Workspace tools (category: workspace): workspace_ls, workspace_read (50KB soft limit), workspace_write (auto-mkdir), workspace_rm (recursive guard), workspace_mv, workspace_patch (find/replace)
  • channels.workspace_id FK, projects.workspace_id FK
  • Workspace resolution: channel workspace → project workspace (override pattern)
  • Tool injection in completion handler when workspace bound
  • Frontend: deferred (channel settings workspace toggle, project Files tab, chat bar workspace indicator)

v0.21.2 — Workspace Indexing + Semantic Search

Embed text files for semantic search. Reuses knowledge.SplitText + knowledge.Embedder.

  • workspace_chunks table (Postgres + SQLite migrations)
  • workspace_files additions: index_status, chunk_count columns
  • workspaces.indexing_enabled boolean (default true)
  • Store methods: InsertChunks, DeleteChunksByFile, SimilaritySearch, UpdateFileIndexStatus
  • Indexable types: text MIME types + 40+ code extensions + special filenames (Makefile, Dockerfile, etc.)
  • Code-aware chunking: 1500-char chunks with function/class/type/sub boundary separators
  • Content-addressed skip: sha256 comparison before re-chunking/re-embedding
  • workspace.Indexer: background goroutine with configurable concurrency semaphore
  • Single-file indexing via FS.WriteFile hook (SetIndexer)
  • Batch indexing via ExtractArchive (sequential within one semaphore slot)
  • workspace_search tool: semantic search with optional glob filtering, top-K capped at 20
  • GET /api/v1/workspaces/:id/index-status endpoint (per-status file counts, total chunks)
  • Configuration: WORKSPACE_INDEXING_ENABLED, WORKSPACE_INDEX_CONCURRENCY env vars
  • Graceful degradation when no embedding role configured
  • Mock store updated for FS unit tests

v0.21.3 — Surface Infrastructure + REPL

Pure UI architecture. No workspace dependency (parallel development).

  • data-surface-region attributes on index.html containers (surface-header, surface-main, surface-footer, sidebar-content)
  • surfaces.js — SurfaceRegistry: register, activate, deactivate, getCurrent, list
  • ctx.ui.replace() / ctx.ui.restore() with DOM preservation (DocumentFragment-based)
  • ctx.surfaces.register() API for extensions
  • Mode selector component in sidebar (#modeSelectorWrap, auto-shown when ≥2 surfaces)
  • Chat as default surface (implicit, always registered)
  • surface.activated / surface.deactivated / surface.registered / surface.unregistered EventBus events
  • REPL tab: AsyncFunction wrapper, global injection (API, Events, Extensions, Surfaces, DebugLog, Panels, UI, , $, sleep)
  • REPL tab: pretty-print results (collapsible JSON, type-colored primitives, DOM element summaries)
  • REPL tab: command history (sessionStorage, ↑/↓ navigation)
  • REPL tab: tab-completion on object graphs, event labels, globals
  • REPL tab: event label hints (Events.on/emit completion)
  • REPL tab: admin gate (admin role OR ?debug=1 URL param)
  • REPL tab: toolbar (clear, copy, help)
  • CSS: mode selector + REPL styles
  • Documentation in EXTENSIONS.md §6 updated with implementation details
  • Mobile: mode selector collapses to hamburger/bottom nav — moved to v0.22+
  • Integration with extension loader (surfaces from manifest — moved to v0.22+)

v0.21.4 — Git Integration

Layer git operations onto workspaces.

  • workspaces additions: git_remote_url, git_branch, git_credential_id, git_last_sync
  • git_credentials table: auth_type (https_pat/https_basic/ssh_key), encrypted_data (AES-256-GCM)
  • Postgres migration (012_v0214_git.sql) + SQLite migration (011_v0214_git.sql)
  • models.GitCredential, GitCredentialSummary, GitStatus, GitFileStatus, GitLogEntry
  • GitCredentialStore interface + Postgres/SQLite implementations (Create, GetByID, ListByUser, Delete)
  • workspace.GitOps with exec-based git operations (9 operations)
  • Credential injection: temp GIT_ASKPASS (PAT), .git-credentials (basic), GIT_SSH_COMMAND (SSH)
  • Security: reject file:// URLs, sanitize error output, GIT_TERMINAL_PROMPT=0
  • Clone, Pull, Push, Status, Diff, Commit, Log, BranchList, Checkout
  • SetGitLastSync on WorkspaceStore interface + both implementations
  • Post-operation reconcile + re-index hook (clone, pull, checkout)
  • 5 git tools: git_status, git_diff, git_commit, git_log, git_branch
  • Git tools filtered out when no workspace bound (completion handler)
  • 9 git API endpoints on /api/v1/workspaces/:id/git/*
  • 3 credential CRUD endpoints on /api/v1/git-credentials
  • Wired in main.go: GitOps creation, tool registration, route registration
  • Workspace settings UI: git config section — moved to v0.22+
  • User Settings: git credentials management UI — moved to v0.22+
  • Integration tests: clone, commit, push/pull cycle (requires git binary in CI)
  • .gitignore respect in workspace indexing — moved to v0.22+

v0.21.5 — Editor Surface (Development Mode)

IDE-like experience. Browser-tier surface consuming workspace primitives.

  • Layout: file tree (sidebar), code editor (CM6, main), AI chat panel (resizable split), status bar
  • File tree: workspace_ls backed, context menu (open, delete), nested directory expansion, file icons
  • Code editor: CM6 codeEditor() factory with textarea fallback, language auto-detection (20+ languages), tab bar, Ctrl+S save
  • AI chat panel: same channel context via Surfaces DOM borrowing, live update via workspace.file.changed event
  • Keyboard shortcuts: Ctrl+P quick open, Ctrl+S save, Ctrl+W close tab
  • Split pane layout with draggable resize handle
  • Status bar: file path, language mode, git branch
  • Workspace API methods on frontend (8 methods)
  • Surfaces.getSavedFragment/putSavedFragment for cross-surface DOM sharing
  • workspace.file.changed event emission from stream_loop.go after tool calls
  • Event route table: workspace.file. → DirToClient
  • editor-mode.css: dark theme, mobile responsive (stacked at ≤768px)
  • Auto-registration: surface registers when channel/project has workspace binding
  • Workspace management UI: create, list, bind from project settings + chat context menu
  • GET /workspaces list endpoint, workspace_id in channel API (update, get, list)
  • Git status indicators in file tree (M/A/U badges + colors) — closed in v0.21.6
  • Drag-drop file reorder — moved to v0.22+
  • Ctrl+Shift+F workspace search (maps to quick open) — closed in v0.21.6
  • Auto-save on tab/mode switch — closed in v0.21.6

v0.21.6 — Article Surface + Document Output Pipeline

Writing-focused surface. Document is artifact, conversation is secondary.

  • Layout: outline (sidebar top), AI assistant (sidebar bottom), rich text editor (CM6 markdown, main)
  • Outline: auto-generated from headings, click-to-scroll (navigates CM6 cursor)
  • Rich text editor: CM6 codeEditor() markdown mode, line wrapping, centered content (720px max), textarea fallback
  • AI assistant: chat panel embedded in sidebar via Surfaces DOM borrowing
  • Export dropdown: Download Markdown, Download HTML (via marked.js + DOMPurify), Copy to clipboard
  • Document picker: lists .md/.mdx/.txt files from workspace, create new document inline
  • Focus mode: dims non-active lines in editor (CM6 opacity-based, toggle button)
  • Word count + reading time in status bar (~230 wpm)
  • Auto-save: debounced (2s) on content change, save on tab switch / surface deactivate
  • Live update on workspace.file.changed event (reloads if file unmodified)
  • Surface registration: registers as "Article" with file-text icon when workspace bound
  • article-mode.css: outline styling, export menu, focus mode, mobile responsive
  • (from v0.21.5 deferred) Git status indicators in editor file tree (M/A/U badges + colors)
  • (from v0.21.5 deferred) Auto-save on tab/mode switch in editor surface
  • (from v0.21.5 deferred) Ctrl+Shift+F workspace search (maps to quick open)
  • Article-specific AI tools (suggest_outline, expand_section, check_citations) — moved to v0.22+
  • Drag-to-reorder outline sections — moved to v0.22+
  • PDF export via pandoc — moved to v0.22+

Bugfixes & DX (added post-initial)

  • Hash Router (router.js): URL-driven direct-to-surface navigation (#/chat, #/editor, #/article). Bookmarkable, back/forward works. Precursor to Go html/template server-rendered pages (v0.25.0) for public/workflow entry points.
  • openDirect(wsId) on EditorMode + ArticleMode: bypass check(), register directly with workspace ID
  • Workspace picker overlay: shown when navigating to surface without workspace, auto-selects if only one
  • SEED_PROVIDERS env var: auto-creates global providers on startup (dev/test only, idempotent)
  • Mode selector labels: icon + text, labels hide when sidebar collapsed
  • chat.switched / chat.created events: wired into selectChat(), newChat(), sendMessage()
  • workspace_id in channel API responses: added to list/get/create for both SQLite and Postgres
  • Frontend chat + project objects: now carry workspace_id for Router and surface check() optimization

v0.21.7 — Bugfix: scanJSON Driver Buffer Aliasing

Critical fix for intermittent 500 errors on paginated list endpoints.

  • scanJSON []byte path: copy driver buffer instead of aliasing (json.RawMessage(v)make+copy)
  • CreateChannel Postgres RETURNING path: use scanJSON(&ch.Settings) instead of bare &ch.Settings
  • UpdateChannel settings write: validate json.Valid() before JSONB merge, reject with 400
  • New tests: TestScanJSON_ByteSliceNotAliased, TestScanJSON_ByteSliceIsolation_MultiRow

Root cause: json.RawMessage(v) is a type conversion, not a copy — it aliases the []byte buffer owned by database/sql. Per the Scanner interface docs, []byte values are only valid until the next rows.Scan() call. In paginated list endpoints, row N's Settings slice header pointed into memory that row N+1's scan overwrote, producing corrupt JSON that passed json.Valid() at scan time but failed at marshal time when SafeJSON serialized the full response.


v0.22.0 — Provider Health + Capability Overrides

Smallest shippable unit that unblocks routing. Track provider error rates and latency, add admin override layer for model capabilities.

Depends on: usage tracking (v0.10.0), capabilities resolver (v0.9.1).

Provider Health Tracking

  • provider_health table: provider_config_id FK, window_start (hourly buckets), request_count, error_count, timeout_count, total_latency_ms, max_latency_ms, last_error, last_error_at
  • In-memory health accumulator: goroutine-safe counters per provider, flushed to DB on interval (60s)
  • RecordSuccess/RecordError/RecordTimeout called from completion handler after every provider call (sync + streaming)
  • Provider status derivation: healthy / degraded / down based on error rate thresholds (5% = degraded, 25% = down)
  • GET /api/v1/admin/providers/:id/health endpoint: current status + hourly windows (configurable hours param)
  • GET /api/v1/admin/providers/health endpoint: summary status for all providers
  • (shipped v0.22.3) Health status included in GET /api/v1/models/enabled response (new provider_status field)
  • (deferred → v0.22.2) Auto-disable: optional policy to mark provider inactive when down for N consecutive windows
  • Background cleanup: prune health rows older than 7 days (6-hour cycle)

Capability Admin Overrides

  • capability_overrides table: provider_config_id (nullable for global), model_id, field, value, set_by, created_at
  • Three-tier resolution in ResolveIntrinsic(): catalog → heuristic → admin override (highest priority)
  • PUT /api/v1/admin/models/:id/capabilities endpoint: set individual capability overrides
  • DELETE /api/v1/admin/models/:id/capabilities/:overrideId endpoint: remove override
  • GET /api/v1/admin/models/:id/capabilities endpoint: show resolved caps with source annotation (catalog/heuristic/override)
  • GET /api/v1/admin/capability-overrides endpoint: list all overrides
  • Postgres + SQLite migrations (013_v0220_health.sql / sqlite 012_v0220_health.sql)

Search Provider Health (deferred → v0.22.1)

  • RecordOutcome for web_search and url_fetch tool calls (search provider tracking)
  • Rate limit tracking per provider config (requests/minute counter in health accumulator)

Workspace Pane Refactor (frontend)

  • .workspace flex container replaces .chat-area + .side-panel layout
  • .workspace-primary / .workspace-secondary independent pane model
  • .workspace-handle drag-resize between panes (replaces old side-panel-resize)
  • Surface layout declarations: primary, secondary, secondaryOpts per surface
  • Dual-view mode removed (was _dualMode, _splitRatio, _secondary in PanelRegistry)
  • Zoom selector updated for new class names

Bugfix (v0.21.7)

  • scanJSON driver buffer aliasing — copy []byte from database driver instead of aliasing slice header
  • CreateChannel RETURNING path uses scanJSON(&ch.Settings) instead of bare scan
  • UpdateChannel validates json.Valid() on settings before JSONB merge

v0.22.1 — Provider Extensions (Declarative Config)

Replace hardcoded provider-specific behavior with data-driven configuration. Same model, different provider → different parameters.

Depends on: provider health (v0.22.0).

Provider Profiles

  • Profile schema per provider type: defines available settings, types, defaults, validation, dependency rules (providers/profile.go)
  • Built-in profile schemas for: openai, anthropic, venice, openrouter (unknown types fall back to openai-compatible)
  • System prompt injection: system_prompt_prefix setting prepended to first system message (all providers)
  • Thinking mode toggle: Anthropic extended_thinking + thinking_budget → injects thinking.type=enabled; Venice enable_thinking → disables Venice system prompt
  • (not needed) provider_profiles JSONB column — existing provider_configs.settings JSONB already serves this purpose

Request/Response Transforms

  • Hooks.PreRequest(cfg, req) — decorate request before dispatch (system prompt prefix, ExtraBody injection)
  • Hooks.PostStreamEvent(cfg, event) — normalize provider-specific response fields (Venice thinking extraction)
  • Hook implementations: Venice web search + thinking, Anthropic extended thinking + beta header, OpenRouter routing preferences + require_parameters, OpenAI frequency/presence penalty
  • Preset-level overrides: MergePresetSettings() merges persona overrides onto provider settings, respecting ProviderOnly fields
  • ProviderConfig.Settings fully utilized: hooks read from config, not hardcoded switch statements
  • CompletionRequest.ExtraBody field + mergeExtraBody() for provider-specific wire fields
  • Anthropic extended thinking stream support: thinking_delta content blocks → Reasoning field

Provider Type Registry

  • providers.Init() uses RegisterType() with full metadata (name, description, default endpoint, profile schema)
  • ProviderTypeMeta struct: ID, name, description, default_endpoint, profile_schema
  • GET /api/v1/admin/provider-types endpoint: list available provider types with their profile schemas
  • (deferred → v0.23) New provider types registrable via config file (openai-compatible endpoint + custom profile schema)

Search Provider Health (carried from v0.22.0)

  • (deferred → v0.22.2) RecordOutcome for web_search and url_fetch tool calls
  • (deferred → v0.22.2) Rate limit tracking per provider config

v0.22.2 — Routing Policies + Fallback Chains

Rules-based routing engine — policy, not ML. Evaluated after resolveConfig() between "what model was requested" and "which provider serves it."

Depends on: provider health (v0.22.0), provider extensions (v0.22.1).

Routing Policies

  • routing_policies table: id, name, scope (global/team), team_id, priority (lower wins), policy_type, config (JSONB), is_active
  • Policy types: provider_prefer (ordered fallback list), team_route (restrict team to providers), cost_limit (heuristic cost cap), model_alias (alias → provider+model rewrite)
  • Policy evaluator: takes requested model + user context → returns ranked list of (providerConfigID, modelID) candidates
  • Integration point: evaluateRouting() called after resolveConfig, before provider dispatch — reloads winning config credentials on switch
  • (deferred → v0.23) capability_match policy type ("cheapest model with tool_calling")

Fallback Chains

  • Fallback runner: RunWithFallback() tries candidates in order with configurable max retries
  • Health-aware: skip providers with status down, prefer healthy over degraded
  • All-down graceful degradation: keeps candidates rather than failing
  • (deferred → v0.23) Latency-aware preference: track response time percentiles, prefer faster providers
  • (deferred → v0.23) Cost-aware preference with budget ceiling per request

Routing Metadata

  • X-Switchboard-Provider response header: providerID/configID on every completion
  • X-Switchboard-Route header: policy name when routing is active
  • routing_decision JSONB column on usage_log
  • POST /api/v1/admin/routing/test endpoint: dry-run policy evaluation with live health status
  • Admin CRUD: GET/POST/PUT/DELETE /api/v1/admin/routing/policies

v0.22.3 — Provider Admin UI + Deferred Polish

Admin interfaces for v0.22.0v0.22.2 features, plus deferred items from v0.21.x.

Depends on: routing policies (v0.22.2).

Admin UI

  • Provider health dashboard: status badges, error rate/latency/timeout metrics per provider, refresh button (loadAdminHealth)
  • Capability override editor: table listing all overrides with model, config, field, value; delete button per row (loadAdminCapabilities)
  • Routing policy builder: CRUD with name/priority/type/scope/team/config(JSON)/active toggle, dry-run test panel with candidate ranking and health status (loadAdminRouting, _showRoutingForm, _runRoutingTest)
  • New "Routing" admin category with Health, Routing, and Capabilities sections
  • Health status included in GET /api/v1/models/enabled response (provider_status field on UserModel)
  • (deferred → v0.23) Provider profile editor: key-value config per provider type, preview of effective settings
  • (deferred → v0.23) Fallback chain visualizer: drag-to-reorder provider priority per model family

Deferred from v0.22.0v0.22.2

  • (deferred → v0.23) capability_match policy type ("cheapest model with tool_calling")
  • (deferred → v0.23) Latency-aware preference: track response time percentiles, prefer faster providers
  • (deferred → v0.23) Cost-aware preference with budget ceiling per request
  • (deferred → v0.23) New provider types registrable via config file (openai-compatible endpoint + custom profile schema)

Deferred from v0.21.x

  • (deferred → v0.23) Mobile: mode selector collapses to hamburger/bottom nav
  • (deferred → v0.23) Integration with extension loader (surfaces from manifest.json)
  • (deferred → v0.23) Workspace settings UI: git config section in channel/project settings
  • (deferred → v0.23) User Settings: git credentials management UI
  • (deferred → v0.23) .gitignore respect in workspace indexing
  • (deferred → v0.23) Drag-drop file reorder in editor file tree
  • (deferred → v0.23) Article tools: suggest_outline, expand_section, check_citations (AI-powered)
  • (deferred → v0.23) Drag-to-reorder outline sections in article mode
  • (shipped v0.22.4) PDF/DOCX export via pandoc in article mode
  • (shipped v0.22.4) Project-specific file uploads (full project-level upload architecture)

v0.22.4 — Provider Health UX + Project Files + Export

Closes remaining provider infrastructure gaps and delivers UX features deferred from earlier releases.

Provider Infrastructure:

  • Rate limit tracking per provider config (HTTP 429 classified separately)
  • Tool health tracking (web_search, url_fetch → tool_health table)
  • Auto-disable policy (N consecutive "down" windows → deactivate provider)
  • Search provider health tracking via tool health pipeline
  • SQLite health store parity (rate_limit_count, tool health, auto-disable)
  • Config: PROVIDER_AUTO_DISABLE_THRESHOLD env var (default: 3)

UX:

  • Workspace settings UI: git config section in project panel (remote URL, branch, credential)
  • Project-specific file uploads (POST/GET endpoints, project panel UI)
  • PDF/DOCX export via pandoc (POST /api/v1/export, editor menu integration)
  • Admin health dashboard: rate limit count display with warning color

Frontend:

  • 5 new API client methods (projectUploadFile, projectListFiles, exportDocument, updateWorkspace, listGitCredentials)
  • Health cards show rate limit counts
  • Git settings section appears when workspace is bound

v0.22.5 — Surfaces + Go Templates + Admin Bug Fixes

Replaces monolithic client-side rendering with server-rendered Go templates organized as composable surfaces. Each page route renders a dedicated surface that owns its full layout below the classification banner. Reusable template components (model-select, team-select, file-upload) eliminate duplicated form-building logic and fix data population bugs by construction.

Template Engine:

  • html/template with //go:embed for compiled-in templates
  • Custom FuncMap (roleFilterType, toJSON, hasPrefix, dict)
  • CSP nonce generation per request
  • Data loaders per surface (pre-fetch exactly what templates need)
  • Base template with banner system (top + bottom, CSS custom properties)

Surfaces:

  • Chat surface (/, /chat/:chatID) — bridge to existing JS
  • Editor surface (/editor/:wsId) — server-rendered layout shell, fixes bugs #4 + #5
  • Notes surface (/notes/:noteId) — standalone with assist pane
  • Settings surface (/settings/:section) — full-page replaces modal
  • Admin surface (/admin/:section) — 5 categories, 10+ sections
  • Login page (/login) — standalone, cookie-based auth

Reusable Components:

  • model-select — type-filtered model dropdown (fixes bug #1)
  • team-select — pre-populated team picker (fixes bug #2)
  • file-upload — drop zone + file picker

Auth:

  • AuthOrRedirect middleware — cookie JWT, redirect to /login
  • RequireAdminPage() — role gate for admin pages
  • sb_token cookie sync on every token save/refresh

Editor File Upload (bug #3):

  • Upload button in toolbar + drag-and-drop on file tree
  • uploadWorkspaceFile() API method (binary-safe, respects quota)
  • Auto-open single uploaded text files

Cleanup:

  • Removed /legacy SPA fallback route
  • nginx.conf: page route proxy blocks before SPA fallback

v0.23.0 — Multi-Participant Channels + Presence

The channel foundation for workflows and real-time collaboration. Today channels are single-user direct chats. This release makes channels a shared space that multiple actors can inhabit.

Depends on: extension surfaces (v0.21.3), WebSocket infrastructure (already exists).

(Shifted from v0.20.0 — no content changes)

Channel Participants

  • channel_participants table: channel_id, participant_type (user, session, persona), participant_id, role (owner, member, observer), joined_at
  • Channel access queries rewritten: WHERE user_id = $1 → participant-based lookup
  • Backward compatible: existing single-user channels auto-have one participant (owner)
  • Channel type column: direct (current), group (multi-user), workflow (staged)

Presence + Real-time

  • Typing indicators and presence (who's online, who's in this channel)
  • Participant join/leave events via WebSocket
  • Co-editing for extension surfaces (editor mode, article mode)
  • Cursor sharing, selection highlighting

Channel-Scoped Notes

  • Optional channel_id FK on notes (NULL = personal notebook, set = channel-attached)
  • Notes created by tool calls within a channel auto-attach to that channel
  • Channel notes visible to all participants (not just creator)

v0.24.0 — Auth Strategy (mTLS/OIDC) + Full RBAC

Enterprise auth modes on top of the teams + groups foundation. Depends on: vault passphrase support from v0.9.3 (conditional unlock for personal providers under external auth), user groups (v0.16.0).

(Shifted from v0.21.0 — enhanced with group-based permissions)

  • AUTH_MODE env var: builtin | mtls | oidc
  • All three resolve to the same internal user model
  • auth_source + external_id columns on users table
  • mTLS: header trust, auto-provision from cert DN
  • OIDC: Keycloak/Okta token validation, claim extraction, role mapping
  • Per-source auto-activate policy: auto_activate, default_team, default_role
  • Vault passphrase prompt for mTLS/OIDC users (v0.9.3 conditional unlock)
  • Fine-grained permissions: model access, KB write, task create, token budgets
  • Group-based permission sets: assign permissions to groups, users inherit from membership
  • OIDC claim → group mapping: external IdP groups auto-sync to internal groups

Anonymous / Session Participants

  • Session-based identity for unauthenticated visitors (workflow intake)
  • mTLS: cert CN/fingerprint as stable session identity (no users row required)
  • Non-mTLS: ephemeral session token (cookie or URL token)
  • Session participants can send messages and trigger tool calls within workflow channels
  • Session identity visible to assigned team members (cert DN, display name, or "Anonymous #N")

v0.25.0 — Workflow Engine

Team-owned, stage-based process execution. The channel is the runtime, personas drive each stage, and the existing tool/notes infrastructure handles structured data collection. See ARCHITECTURE.md — Workflow Architecture for the conceptual model.

Depends on: multi-participant channels (v0.23.0), anonymous identity (v0.24.0).

(Shifted from v0.22.0 — no content changes, dependency refs updated)

Workflow Definitions

  • workflows table: team_id (owner), name, description, is_active, entry_mode (public_link, team_internal, api)
  • workflow_stages table: workflow_id, ordinal, persona_id, assignment_team_id, form_template (JSONB, structured note schema), transition_rules (JSONB)
  • Team-admin CRUD: create/edit/delete workflows and stages
  • Workflow versioning: edits create new version, active instances continue on their version

Workflow Instances (Channels)

  • Workflow-typed channels: type='workflow', workflow_id, current_stage, stage_version
  • Entry point: public link generates workflow channel, adds anonymous participant, binds stage 1 persona
  • Stage transitions: triggered by AI (tool call), team member (button), or rule (form complete)
  • On transition: swap active persona, notify assignment team, update channel metadata

Go Template Pages (server-rendered entry points)

Server-rendered HTML via Go html/template for pages that need SEO, OG tags, pre-auth rendering, or standalone access outside the SPA shell. Same binary, same auth middleware, same deployment — no second language runtime.

  • Template engine: Go html/template with shared base layout, partials for nav/footer
  • Route layer: /w/:slug for public workflow pages, /p/:id for shared project views
  • Workflow landing page: branded intake form, persona avatar, description — rendered server-side
  • Shared/embedded surface pages: /s/editor/:wsId, /s/article/:wsId/:path — server renders minimal shell, JS hydrates surface directly (replaces hash routing for public links)
  • Template data: workflow metadata, branding, environment banner — injected server-side
  • Auth gating: public pages skip auth, team pages require session, admin pages require role
  • Precursor: hash router (v0.21.6) handles in-app navigation; Go templates handle external/public entry points

AI Intake

  • Stage persona drives structured collection via system prompt
  • Form template → persona system prompt injection ("collect these fields: ...")
  • Tool calls create channel-scoped notes as form responses
  • AI determines readiness: "I have everything needed" → trigger transition

Assignment + Queue

  • Assignment queue: unassigned workflow channels visible to team members
  • Claim model: team member claims channel (becomes participant with member role)
  • Round-robin / rule-based auto-assignment (optional, per-stage config)
  • Assignment notifications via WebSocket + optional webhook

Team Member Collaboration

  • Assigned member sees full history (AI intake + artifacts + notes)
  • Persona remains active — assists both visitor and team member
  • Member can trigger stage transitions, add notes, invoke tools
  • Member can reassign to different team member or escalate to different team

Frontend

  • Workflow builder UI in team admin panel (stage list, persona picker, form template editor)
  • Queue view: sidebar section showing unassigned workflow channels for user's teams
  • Channel header: workflow stage indicator, transition controls
  • Anonymous visitor view: minimal UI, persona-driven conversation (public link entry)

v0.26.0 — Tasks / Autonomous Agents

Unattended execution — workflows without a human in the loop. Depends on: workflow engine (v0.25.0).

(Shifted from v0.23.0 — no content changes, dependency refs updated)

  • Scheduler + task runner (cron-like triggers for workflow instantiation)
  • task_create tool (AI can spawn sub-workflows)
  • type: 'service' channels: workflow instances with no human participants
  • Execution budgets: max tokens, max tool calls, max wall-clock per stage
  • Admin controls for resource limits and kill switches
  • Completion webhooks (notify external systems when workflow completes)

TBD (unscheduled — real features, no immediate need)

Items that are real but don't yet have a version assignment. Pull left based on need.

Extension System — Additional Extensions

  • Image generation tool (browser or sidecar, provider-agnostic)
  • STT/TTS (browser extension, Web Speech API — personal use case)
  • Code execution sandbox (server-side, container isolation)

Extension System — Server Tiers

  • Starlark runtime integration (Tier 1 — server sandbox)
  • Sidecar HTTP tool protocol (Tier 2 — container isolation)
  • Server-side tool execution in completion handler

Desktop + Mobile

  • Desktop app (Tauri)
  • Full PWA with offline capability
  • Mobile-optimized layouts (beyond current responsive)

Data + Portability

  • Bulk export/import (account data, conversations, settings)
  • ChatGPT/other tool import
  • GDPR-style "download my data"
  • Backup/restore CronJob manifests

UX / Multi-Seat

  • "Group" scope badge on model selector / KB list: show access-source annotation so users see why they have access (global, team, group grant). Requires backend to include access_source in list responses.
  • Per-provider model preferences: user_model_settings unique key is (user_id, model_id) — same model from different providers shares one visibility toggle. Needs provider_config_id dimension in DB constraint, store, API, and frontend hiddenModels keying. Frontend composite key (configId:modelId) already exists in App.models[].id.

Projects — Future

  • Project-specific files: full project-level upload (own endpoint, storage path, UI surface — not just channel attachment re-linking)
  • Project templates: create new projects from predefined configurations (persona, KBs, system prompt)
  • Project creation dialog: replace prompt() with proper modal (name, description, persona, KB picker)
  • Admin-level project management: cross-instance visibility, reassign ownership, enforce team policies (scope: enterprise only, BYOK personal projects stay private)
  • Sub-projects / nested hierarchy: child inherits parent KBs, system prompt, persona (deferred until usage patterns clarify need vs tags/labels)

Knowledge Bases — Future

  • KB auto-injection: top-K chunk prepend to system prompt, context budget aware, per-channel toggle (latency budgeting required)
  • Hybrid search: combine vector similarity with full-text tsvector, re-rank
  • Semantic chunking: embedding-based boundary detection for smarter splits
  • HNSW index: better query performance than IVFFlat for large datasets
  • Web scraping source: ingest URLs as KB documents (extends url_fetch)
  • Scheduled re-indexing: periodic rebuild when source documents update
  • Store cleanup: add UpdateDocumentStorageKey() to KnowledgeBaseStore interface (done in v0.17.0) (currently uses direct database.DB.ExecContext in handler — works but bypasses store layer)

Memory — Future

  • Memory compaction: summarize old memories to save context tokens
  • Memory confidence decay: reduce confidence over time, prune low-confidence entries
  • Memory export/import: portable memory format across instances
  • Cross-persona memory sharing (opt-in): e.g. "coding assistant" can read facts from "project manager"
  • Memory analytics: dashboard showing what Personas are learning, memory growth trends

Platform

  • Rate limiting per user/team/tier (token budgets)
  • Provider health monitoring → v0.22.0 + key rotation
  • Multi-tenant SaaS mode
  • Plugin/extension marketplace
  • Virtual scroll for long conversations
  • SQLite backend option (single-user / dev) → v0.17.1

Pane Architecture (Workspace Container)

The main area to the right of the sidebar is a workspace container that holds 1+ panes. Each pane is a self-contained unit (chat, editor, notes, preview, etc.) with its own lifecycle. Panes compose side-by-side rather than replacing each other (current surface model).

  • Default layout: Left nav + single Chat pane (what exists today, zero cognitive overhead)
  • Power user: Left nav + Editor pane + Chat pane side by side. Or Editor + Notes. Or all three.
  • Workflow mode: Left nav hidden. Team member gets Chat + History. Customer gets just Chat.
  • User-resizable splits between panes, layout persists per-project or per-user preference
  • The current surface system was a stepping stone — proved out the region/activate/deactivate lifecycle. Panes reuse that contract but drop the "only one active at a time" constraint.
  • Sidebar tabs (Chats / Files) already decouple navigation from center content (v0.21.6)

Surfaces as Extensions

  • Surfaces (future: panes) become first-class extension types alongside block renderers and tool bridges
  • Surfaces.register() already defines the contract: name, regions, activate/deactivate callbacks — swap hardcoded JS modules for manifest + dynamically loaded bundles
  • Admin-installable surfaces: upload or enable from extension registry, scoped to team or global
  • Surface manifest in manifest.json: declares required regions, capabilities, dependencies
  • Surface IDE: built-in surface for building surfaces — Go template editor for server-rendered shells, JS/CSS editor for client behavior, live preview in sandboxed region. Eats its own dog food.
  • Surface marketplace: share custom surfaces across instances (presentation mode, kanban, form builder, dashboard, etc.)
  • Project-bound surface/pane defaults: project config specifies which panes are available and default layout