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

4 Commits

Author SHA1 Message Date
glm5.2 agent 180
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.
2026-06-20 15:54:23 +03:00
glm5.2 agent 180
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.
2026-06-20 15:43:44 +03:00
glm5.2 agent 180
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.
2026-06-20 15:43:23 +03:00
glm5.2 agent 180
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.
2026-06-20 15:41:54 +03:00