Drag the floating AI-chat window onto the sidebar and release over it to DOCK it
— the window pins to the live navbar rect, overlaying the page tree; a drop-zone
highlight shows while dragging over it. Closing the chat re-shows the tree.
Undock via a header button or by dragging the docked window back onto content
(pops out floating at the drop point). The docked/floating mode persists in
localStorage and the docked window follows the navbar width (manual resize,
space<->shared route change) via a ResizeObserver + sidebar-toggle/transitionend
re-sync; when the navbar is collapsed/absent the window falls back to floating
instead of vanishing. Dock/undock only flips a mode atom + geometry — ChatThread
is never remounted, so an in-flight response stream is not interrupted.
Frontend only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Batch of fixes from the automated QA pass on develop. Each was reproduced and
then verified fixed live (browser/curl); logic-bearing fixes have unit tests.
Functional bugs:
- #122 collab-token was capped by the anonymous public-share-AI throttler (5/min);
skip all non-AUTH named throttlers on this auth-guarded, client-cached route.
- #123 editor onAuthenticationFailed threw `jwtDecode(undefined)` and never
reconnected; read the token via a ref, guard the decode (incl. missing exp),
and refetch+reconnect on any auth failure.
- #124 a slash command containing a space ("/Heading 1") inserted literal text;
enable allowSpaces and close the menu when the query matches no items.
- #125 space slug auto-gen produced uppercase initials for multi-word names;
computeSpaceSlug now yields a lowercase alphanumeric slug.
- #126 AI chat window position/size now persisted (atomWithStorage) across reload;
also fixes a latent ResizeObserver-attach bug on first open.
- #127 workspace name update accepted URLs; add @NoUrls (parity with setup).
- #132 icon-columns 4/5 passed calc() into SVG width/height attrs (console spam);
size via style. share-for-page query returns null instead of undefined.
- #134 "Reindex now" counter looked stuck: reindex runs async; the client now
polls coverage (bounded) so the counter climbs live; misleading server comment
reworded.
UX / consistency:
- #128 add success toasts to favorite/label/avatar/member-(de)activate.
- #129 "1 result found" pluralization; hide the single-option Type filter.
- #130 replace raw Zod strings with friendly messages (name/password/group).
- #131 unify "Untitled" casing in tree/breadcrumb/tab; stop force-uppercasing
space-name chips; fix confirm-dialog labels (Cancel / Remove), invite
placeholder typo, Export/Move-to-space labels.
- #133 disable profile Save when clean; toast on unsupported avatar image;
style the invalid-invitation page with a CTA; hide Share for read-only users;
align the dictation "not configured" message; "Go to login page" typo.
Tests: computeSpaceSlug, workspace-name NoUrls DTO, share-query null
normalization, slash getSuggestionItems empty-close.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reusable, workspace-shared agent roles for the built-in AI chat. A role is
a named persona (system-prompt instructions) + optional model override; a
chat is bound to a role at creation and applies it every turn.
Backend:
- migration 20260620T120000: ai_agent_roles table + ai_chats.role_id
(FK ON DELETE SET NULL); hand-merged types into db.d.ts/entity.types.ts
(db.d.ts is hand-curated here, full codegen would clobber it).
- core/ai-chat/roles: CRUD module. list = any workspace member; create/
update/delete = admin (Manage Settings ability, like ai-settings/mcp).
All repo queries scoped by workspace_id; soft-delete (deleted_at).
- buildSystemPrompt gains roleInstructions: role REPLACES the persona base
(admin prompt / DEFAULT_PROMPT) but SAFETY_FRAMEWORK + context are always
still appended.
- stream(): role resolved from ai_chats.role_id for existing chats (never
the request body -> no per-turn role swap); body.roleId only on creation.
Disabled (enabled=false) and soft-deleted roles fall back to universal.
- getChatModel(workspaceId, override): role model_config can swap model id /
driver; a driver without configured creds throws 503 with a clear message
naming the driver+role, resolved BEFORE response hijack.
Client:
- new-chat role picker (enabled roles only, default Universal assistant),
roleId sent only on the first message; role badge (emoji+name) in the chat
header and conversation list; admin Agent-roles management section in
Settings -> AI (add/edit/delete, MCP-form pattern).
Tests: ai-chat.prompt.spec (role layering + safety always present, incl.
jailbreak); ai.service.spec (override on unconfigured driver -> 503).
Implements docs/ai-agent-roles-plan.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Typing into the composer while the agent was streaming lost the draft once
the turn finished: on a brand-new chat, adopting the freshly created chat id
changes ChatThread's key and remounts it, wiping ChatInput's local state.
Lift the composer draft into a module-level jotai atom (aiChatDraftAtom) so it
survives the remount. Reset it only on deliberate chat switches — startNewChat,
selectChat, and the page-history "AI agent" badge deep-link — so a draft never
leaks between conversations, while adoption (which goes through a useEffect)
preserves it.
Replace the docked right-aside AI chat with a floating, draggable,
resizable, minimizable window per the GitmostAgent design. The "AI chat"
entry points (page header menu, page-history item) now open the window
instead of the aside tab.
- Add ai-chat-window.tsx + ai-chat-window.module.css: fixed-position
window with viewport-clamped drag, CSS resize, minimize (hides body
via CSS so ChatThread/useChat stays mounted and streaming is not
aborted), and geometry kept in state (survives close/reopen, re-clamped
on open via useLayoutEffect, size tracked with a ResizeObserver).
- Reuse ChatThread, ConversationList and the transcript components
unchanged; move all orchestration (active chat, adopt-new-chat,
openPage, queries) into the window.
- Header shows a tokens-only badge: sum of persisted metadata.usage for
the active chat (no cost/context-total data available), hidden at 0.
- Add aiChatWindowOpenAtom; mount the window once in global-app-shell.
- Remove the aside "ai-chat" tab handling and delete ai-chat-panel.tsx.
- Type IAiChatMessageRow.metadata.usage; add "Minimize" and
"Tokens used in this chat" i18n keys.
- Add reversible write tools to the per-user agent toolset (page create/update/
move/soft-delete; comment reply + resolve), exposed under the user's JWT and
enforced by Docmost CASL; no permanent/force delete (D3).
- Non-spoofable agent provenance: sign actor/aiChatId into the access and collab
tokens (TokenService), propagate via jwt.strategy onto the request, and set
pages.last_updated_source/last_updated_ai_chat_id on REST create/update/move and
comments.created_source/resolved_source/ai_chat_id.
- packages/mcp: add an optional getCollabToken provider (content-edit provenance)
and guard against empty tokens; service-account /mcp path unchanged.
Frontend:
- Admin 'AI / Models' settings section: provider/model/embedding/base URL, a
write-only API key field, system prompt, and Test connection.
- AI chat panel (useChat + DefaultChatTransport): conversation list, streamed
messages, tool-call action log and page citations; header entry point gated on
settings.ai.chat.
Compile-verified (server nest build + client tsc/vite); not yet live-tested.
Known gaps: history 'AI agent' badge (C3), vector RAG (D), external MCP (E);
chat tool-card citation links pending a fix.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>