feat(ai-chat): agent roles — parallel impl (agent 180 / model B) #22
Closed
Ghost
wants to merge 4 commits from
feat/ai-agent-roles-agent180 into develop
pull from: feat/ai-agent-roles-agent180
merge into: vvzvlad:develop
vvzvlad:main
vvzvlad:feat/184-autonomous-agent-runs
vvzvlad:feat/git-sync
vvzvlad:fix/embeddings-reindex-progress
vvzvlad:feature/offline-sync
vvzvlad:refactor/193-tool-spec-registry
vvzvlad:test/244-part-b
vvzvlad:fix/255-ws-redis-adapter-leak
vvzvlad:feat/251-intentional-clear
vvzvlad:fix/252-e2e-open-handles
vvzvlad:feat/221-image-captions
vvzvlad:fix/244-dataloss-bugs
vvzvlad:develop
vvzvlad:feat/229-catalog-yaml
vvzvlad:feat/243-blob-sandbox
vvzvlad:feat/228-inline-footnotes
vvzvlad:fix/qa-ui-bugs-216-218
vvzvlad:feature/agent-roles-catalog
vvzvlad:fix/share-alias-rename
vvzvlad:fix/ai-chat-empty-render
vvzvlad:feat/191-chat-doc-binding
vvzvlad:feat/201-temporary-notes
vvzvlad:feat/198-interrupt-agent
vvzvlad:feat/ai-chat-full-history
vvzvlad:feat/199-ai-generate-title
vvzvlad:feat/205-share-aliases
vvzvlad:batch/issues-189-187-170
vvzvlad:feat/170-mcp-test-button
vvzvlad:feat/189-context-badge
vvzvlad:feat/198-interrupt-agent-send-now
vvzvlad:fix/issues-190-159
vvzvlad:fix/ai-chat-new-chat-during-stream
vvzvlad:fix/ai-chat-stream-perf
vvzvlad:batch/issues-2026-06-25
vvzvlad:feat/ai-chat-persistent-history
vvzvlad:fix/ai-chat-copy-chat-wysiwyg
vvzvlad:fix/ai-stream-reset-resilience
vvzvlad:fix/ai-stream-undici-timeout
vvzvlad:fix/footnote-review-1227-followup
vvzvlad:fix/ai-chat-token-counter-realtime
vvzvlad:docs/manual-qa-test-plan
4 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
24bf0ab18f |
feat(ai-chat): add reusable agent roles (persona + optional model)
Roles are workspace-admin presets that customize the AI agent's system- prompt persona and, optionally, the model, attached to a chat at creation time. Examples: a 'Proofreader' that only touches grammar, a 'Fact- checker' that cites web sources. A role changes ONLY instructions and ( optional ) the model; the toolset stays full, so the security boundary (CASL via the per-user loopback token) is unchanged. Backend: - Migration 20260620T150000-ai-agent-roles: ai_agent_roles table (workspace-scoped, soft-delete, model_config jsonb) + ai_chats.role_id (ON DELETE SET NULL). - AiAgentRoleRepo / AiAgentRolesService / AiAgentRolesController at /workspace/ai-agent-roles. LIST (picker view) is open to all workspace members; create/update/delete are admin-only. The picker view omits instructions and model_config so they never leak to non-admins. - buildSystemPrompt: optional roleInstructions REPLACES the admin persona (priority order: role > admin > default). The non-removable SAFETY_FRAMEWORK is always appended - a role cannot strip it. - AiChatService.stream: persists roleId on first turn; subsequent turns read role_id from the chat row, never from the request body. The role's instructions are applied even if it was later disabled or soft-deleted (existing chats keep their persona). - AiService.getChatModel accepts an optional override. Same-driver overrides reuse the workspace key; cross-driver (openai/gemini) loads alternate creds from ai_provider_credentials and throws a clean 503 if they are missing (no silent fallback). Cross-driver ollama is rejected with a clear message (no per-driver ollama base URL exists yet). - Controller resolves the role model BEFORE res.hijack so misconfigured overrides return JSON 503, not a broken stream. Client: - New chat picker (Mantine Select) lists enabled roles, default 'Universal assistant' (roleId null). The roleId is sent only when starting a new chat; existing chats show the role as a fixed badge. - Role badge in the chat window header and conversation list. - Settings -> AI: new 'Agent roles' management section mirrors the external MCP servers UI (add/edit/delete + enable toggle + optional model override). Form fields: name, emoji, description, instructions, model override (driver + chatModel), with a reminder that the safety framework is always appended. Hardening after review: - Empty-string roleId coerced to null on both client and server (picker 'Universal assistant' option used to crash the uuid INSERT). - New-chat insert validates picker-eligibility (enabled + not soft-deleted + workspace-scoped); ineligible ids silently fall back to null. - findByCreator's role JOIN is workspace-scoped and every column ref is table-qualified (avoids Postgres ambiguous-column errors). - getChatModelForRole applies the same picker-eligibility gate as stream on the new-chat path, so model and persona resolve from one source. |
||
|
|
2936d16a43 |
docs: add history-diff performance redesign plan
Add docs/history-diff-perf-plan.md: deep-dive into the page-history inline diff performance problem and a phased redesign. - Root causes: O(K·D) recreateTransform (rfc6902 full-doc rebuild per op), full recompute on the "Highlight changes" toggle, a second full TipTap instance, all synchronous on the main thread. - Fix: drop recreateTransform; diff directly via prosemirror-changeset (getReplaceStep + ChangeSet.addSteps/computeDiff), keeping the existing decoration contract for visual parity. - Split the diff useEffect so the toggle no longer re-diffs. - Phased plan (P0 core, P1 large-doc guard + error handling, P2 worker), testing/parity strategy, risks and rollback. |
||
|
|
ddfccb30f3 |
docs(backlog): add dependency update & security audit snapshot
Record outdated-deps and security-audit findings for the fork as of 2026-06-20 (pnpm outdated -r + pnpm audit --prod): 162 outdated entries, 50 major-behind, 51 vulnerabilities (16 high). Key finding: pnpm.overrides pin several packages to versions flagged by the audit (ws, undici, tmp, hono, protobufjs, dompurify) — cheapest fix is bumping the pins. Also flags direct-dep highs (@nestjs/platform-fastify auth middleware bypass, nodemailer, form-data, react-router-dom), risky majors to schedule separately (Mantine9/React19, Hocuspocus 4, CASL 7, TypeScript 6, zod 4, stripe), the deprecated @types/form-data, and @types/node drift across the workspace. |
||
|
|
19e083596d |
docs: add search morphology + hybrid-search-exposure plans
Two feature plans grounded in the current develop: - search-language-morphology-plan.md: make the FTS text-search config selectable (env SEARCH_TS_CONFIG, whitelist english|russian|simple|ru_en; recommend ru_en for the RU/EN wiki). Documents every hardcoded 'english' touchpoint (pages.tsv trigger, page_embeddings.fts generated column, attachments.tsv, search.service.ts, hybrid lexical CTE), the DDL-baked config constraint, reindex strategy, and the regconfig SQL-injection guard. - hybrid-search-general-plan.md: expose the existing pgvector/RRF hybrid search (today agent-only via searchPages) on the user-facing /search, the UI, and the MCP search tool, reusing page-embedding.repo.hybridSearch with identical CASL/permission post-filtering and lexical fallback. |