Commit Graph

1683 Commits

Author SHA1 Message Date
claude_code
93d8c1f775 fix(home): show "New note" button by resolving writability from role
The button never rendered: it filtered spaces to writable ones via a
CASL ability built from membership.permissions, but the /spaces list
endpoint returns membership.role only (permissions come from
/spaces/info). The empty ability hid the button for everyone.

Resolve writability from membership.role instead, mirroring the server
space-ability mapping (ADMIN and WRITER can manage pages, READER is
read-only). Drop the now-unused CASL imports.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 02:49:17 +03:00
claude_code
ef74058301 style(editor): align byline dictation mic with the info icon
The byline mic rendered blue and with a smaller (16px) glyph next to the
gray 20px info icon, so it looked misaligned with an uneven gap. Add
optional color/iconSize props to MicButton (forwarded through
DictationGroup) and render the byline mic gray at 20px, wrapping it and
the info icon in a tight nowrap group so they read as a snug, aligned
pair. The AI chat mic is unchanged (passes neither prop).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 02:48:50 +03:00
claude_code
3a3d22ac55 refactor(editor): remove duplicate dictation mic from the fixed toolbar
The dictation button now lives in the always-rendered page byline, so the
copy in the fixed toolbar was redundant: with the toolbar enabled the mic
showed up twice. Drop the DictationGroup render, its isDictationEnabled
guard, and the unused import from the toolbar.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 02:33:07 +03:00
claude_code
4281a370b1 Merge branch 'feat/stt-language' into develop
Add dictation language selection to STT settings.
2026-06-22 02:29:13 +03:00
claude_code
a16ef2346f feat(ai/stt): add dictation language selection to STT settings
Add a per-workspace `sttLanguage` setting (ISO-639-1 hint; empty =
auto-detect) and a searchable language picker in the Voice / STT settings
card. The hint is forwarded to the transcription endpoint:
- multipart path via the AI SDK `providerOptions.openai.language`
- JSON (OpenRouter) path via a top-level `language` body field
only when non-empty, so auto-detect behaves exactly as before.

Threaded through the whole stack: ai.types, update DTO, AiSettingsService
(resolve/getMasked/update), the workspace.repo SQL allowlist, the client
ai-settings service types, and the provider-settings form. Adds en-US
source keys and ru-RU translations.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 02:29:07 +03:00
claude_code
01d7c2b465 Merge branch 'feature/home-new-note' into develop
feat(home): prominent "New note" button on the dashboard
2026-06-22 02:19:07 +03:00
claude_code
6d0ee6c61f feat(home): add prominent "New note" button to dashboard
Add a big "New note" action to the Home screen that creates a new page
and opens it. Since the home screen has no active space, the target
space is resolved from the user's writable spaces (CASL Manage/Page
gate, mirroring the space sidebar): created directly when there is one
writable space, picked from a dropdown when there are several, hidden
when there are none. Menu items are disabled while a create is in
flight to avoid duplicate pages.

- New component features/home/components/new-note-button.tsx
- Render it at the top of pages/dashboard/home.tsx (above the carousel)
- Add i18n keys "New note" / "Create in space" to en-US and ru-RU

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 02:18:31 +03:00
claude_code
6347708605 Merge branch 'feature/byline-dictation' into develop
Add dictation mic button to the page byline next to the info icon.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 02:15:01 +03:00
claude_code
fae8418fa2 feat(editor): add dictation mic to page byline next to the info icon
The editor dictation button previously lived only in the fixed toolbar,
which is hidden by default (gated by the per-user editorToolbar
preference), so dictation was effectively unavailable in the editor. Add
the same dictation control to the always-rendered page byline row, right
next to the Details "i" icon, so voice input stays reachable.

It is shown only when workspace dictation is enabled, the page is
editable, and the editor is in edit mode. Reuses the existing
DictationGroup/MicButton and its caret-insertion logic.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 02:14:46 +03:00
claude_code
8f994460ad refactor(comments): tidy comments panel header and quote layout
- Merge the comments side-panel header into the Open/Resolved tab row,
  then drop the now-redundant "Comments" title; the panel keeps its
  accessible name via the AppShell.Aside aria-label.
- Overlay the close (X) button on the right of the tab row and nudge it
  up 4px to align with the tab labels; the tab list stays full-width so
  its bottom border line is preserved. The toc/details tabs keep their
  existing shared header and scroll area unchanged.
- Quote block (.textSelection): increase top margin (2px -> 8px) so it
  no longer sticks to the timestamp when it is the first block, and add
  margin-left: 6px so the quote's left bar lines up with the comment
  body text left edge.
2026-06-22 02:12:13 +03:00
claude_code
c83343d3a3 refactor(comments): move panel title and close button into the tabs row
Merge the comments side-panel header into the Open/Resolved tab row to
save vertical space: title on the left, tabs centered, close button on
the right.

- comment-list-with-tabs: add optional `title`/`onClose` props; render
  the title and close button as absolutely-positioned overlays around a
  full-width centered Tabs.List. Keeping them outside Tabs.List preserves
  the tablist ARIA contract (only role="tab" children) while the tab
  list's full-width bottom border line is retained.
- aside: pass `title`/`onClose` to CommentListWithTabs for the comments
  tab and drop the shared header for that tab; the toc/details tabs keep
  their existing shared header and scroll area unchanged.
2026-06-22 00:37:53 +03:00
claude_code
4f035b8e19 feat(client): declutter space sidebar and global header
- Remove the large active-space name header in the space sidebar;
  the active space stays highlighted in the spaces grid below.
- Move "Space settings" into the user avatar (top) menu next to
  "Workspace settings"; it shows only while viewing a space and is
  detected via useMatch("/s/:spaceSlug/*").
- Make the brand logo non-selectable/non-draggable (user-select:none
  on .brand, draggable=false on the img).
- Remove the redundant "Home" button next to the logo (the logo
  already links to /home).
- Remove the version label under the Settings sidebar menu.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 23:50:30 +03:00
claude_code
0deded342d fix(dictation): drive the recording halo from mic level under reduced-motion
The live mic-level halo around the stop button was frozen at a constant
scale (1.15) whenever the OS "Reduce motion" setting was on, so it never
reacted to the voice while dictating. Make haloScale unconditional so it
always follows audioLevel (amplitude 0.9), and drop the now-unused
useReducedMotion import and reduceMotion local.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 23:34:07 +03:00
claude_code
ebfb947ba2 style(comments): tighten aside panel spacing and widen it
- Widen the comments/aside panel from 350 to 420 (~20% wider)
- Remove double padding around the panel: AppShell.Aside p="md"->"sm"
  and inner Box p="md"->p={0}; reduce header-to-tabs gap mb="md"->"sm"
- Reduce empty space below the add-comment input (paddingBottom 25->10),
  align the avatar with the input box (marginTop 10->2) and re-anchor the
  send button (bottom 30->15)
- Pull the timestamp closer to the nickname via tighter line-height
  (lh 1.2 on the name, 1.1 on the "… ago" text)
2026-06-21 23:09:11 +03:00
claude_code
43f8c9ab99 Merge branch 'feat/ai-comments-inline-anchor' into develop
Make AI-created comments inline-only and reliably anchored: forbid
page-type comments for the agent, throw + roll back when a selection
cannot be anchored, and add robust text matching (normalization +
cross-text-node anchoring within a block).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 23:07:01 +03:00
claude_code
03e2f444ae ix(ai-chat): keep typing dots bouncing under reduced-motion
The "Thinking…" indicator's bounce was fully disabled by the
prefers-reduced-motion rule (animation: none), leaving the dots
frozen for users with "Reduce motion" enabled. Drive the bounce
height with a --bounce custom property: -6px by default and a
smaller -3px under reduced-motion, so the indicator stays visibly
active everywhere instead of freezing.
2026-06-21 23:06:56 +03:00
claude_code
4201f0a313 feat(comments): make AI comments inline-only with robust anchoring
The in-app AI chat hardcoded type='page' and the shared createComment
swallowed anchoring failures silently, so agent comments never got a
text anchor/highlight.

- Forbid page-type comments for the agent: top-level comments are always
  inline and require an exact `selection`; replies inherit the parent
  anchor (stored as the historical `page` type).
- Throw and roll back the just-created comment when the selection cannot
  be anchored, instead of leaving an orphan unanchored comment.
- Add comment-anchor module: text normalization (smart quotes, dashes,
  nbsp, collapsed whitespace) and matching across adjacent text nodes
  within a block, so selections crossing inline-code/bold/link anchor.
- Update create_comment (MCP) and createComment (ai-chat) tool schemas
  and descriptions; add unit + mock-HTTP orchestration tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 23:06:49 +03:00
claude_code
47c4e547e7 docs(agents): restrict git worktrees to the .claude folder
Add a rule to the "Реализация" section of AGENTS.md stating that git
worktrees may only be created inside the .claude directory
(e.g. .claude/worktrees/<name>); creating them anywhere else is forbidden.
2026-06-21 22:17:03 +03:00
claude_code
eb1e233d46 fix(ai-chat): keep the live thread on new-chat adoption; log stream errors
A brand-new chat's first turn streamed and finished successfully, but the
whole assistant response vanished from the UI. On finish the window adopts
the server-created chat id, which changed the <ChatThread> key and remounted
it — discarding the live useChat store (the full answer) and re-seeding from
not-yet-persisted history, so only the user message remained.

- chat-thread: pin the useChat store id to a per-mount value so adopting the
  chatId prop no longer recreates the store and wipes the live turn.
- ai-chat-window: derive the thread mount key via setState-during-render and
  move the live-thread marker in lockstep with the adopted id, so in-place
  adoption keeps the same mounted thread while real chat switches still
  remount and re-seed; gate the history loader to a freshly opened chat.
- cancel a pending adoption on New chat / explicit chat selection.
- log the raw stream error to the browser console for debugging.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 22:14:32 +03:00
claude_code
69f385ccb7 docs(agents): note release tags must be pushed to the CI build remote
The back-merge alone does not fix the develop version: git describe names
a tag ref, and the :develop image is built on GitHub Actions, so the tag
must exist on the `github` remote. git push of a branch does not push
tags. Document the multi-remote (gitea + github) tag-push requirement and
a recovery checklist when develop still shows the previous version.
2026-06-21 21:48:21 +03:00
claude_code
ccbd3e1962 i18n(ai-chat): rename typing indicator to "Thinking…"
Replace the AI chat typing indicator text "AI is typing…" with
"Thinking…".

- typing-indicator.tsx: use t("Thinking…") instead of t("AI is typing…")
- en-US: drop the now-redundant "AI is typing…" key (the "Thinking…"
  key already existed and was unused)
- ru-RU: rename the key to "Thinking…" with value "Думаю…"
- update related comments in message-list.tsx and the test file
2026-06-21 21:35:29 +03:00
claude_code
18ef18fb6a docs(agents): document develop version lag and release back-merge step
The UI version comes from `git describe --tags`, which resolves the nearest
tag in the current commit's ancestry. Release tags are created on main's
merge commit, which is not in develop's history, so develop builds keep
reporting the previous tag (e.g. v0.91.0-NNN) until main is merged back.

Add step 7 (back-merge main -> develop) to the "Cutting a release"
checklist and a subsection explaining why develop lags and how to fix it.
2026-06-21 21:24:38 +03:00
claude_code
810228a3e2 Merge branch 'main' into develop 2026-06-21 21:22:25 +03:00
claude_code
9a9b61b9a3 feat(ai-chat): log aborted stream turns in onAbort
The onAbort terminal path persisted the partial turn but wrote nothing
to the log, so a turn killed by a client disconnect / proxy drop / stop()
was invisible in the logs (unlike onError and the controller catch, which
both log). Add a logger.warn with the chat id, completed step count and
partial-text length so an aborted turn is traceable.
2026-06-21 21:21:48 +03:00
claude_code
79c3c86b82 fix(ai-chat): show typing indicator while the agent thinks between tool calls
showTypingIndicator treated any tool part in the latest assistant message
as visible content, so the "AI is typing…" dots were suppressed for the
rest of the turn once the first tool call appeared. During the model's
"thinking" pauses after a completed tool call, the chat showed only static
tool cards and no activity.

Inspect the last part of the assistant message instead of any part: hide
the dots only while output is actively rendering (a non-empty streaming
text part, or a tool still in the "running" state — which shows its own
Loader). Finished/errored tools and empty trailing text now keep the dots
visible, so the indicator reappears while the model thinks between steps.

Add tests covering the post-tool thinking gap and the running-tool case.
2026-06-21 21:10:38 +03:00
claude_code
55625874c5 feat(dictation): show live mic level while recording
Add a pulsing halo behind the stop button that scales with the
microphone input level, giving real-time feedback that recording is
active and the mic is picking up sound.

- use-dictation: meter the captured MediaStream via AudioContext +
  AnalyserNode (analyser only, never connected to destination), compute
  a smoothed RMS audioLevel (0..1) in a requestAnimationFrame loop, and
  tear the meter down on every recording-end path (stop/cancel/auto-stop/
  unmount); meter failure is non-fatal to recording
- mic-button: render a translucent red halo whose scale follows
  audioLevel; honor prefers-reduced-motion with a static halo
- stop(): recover and release resources when no live recorder remains
- fix unhandled rejection from AudioContext.resume()
2026-06-21 21:04:22 +03:00
claude_code
71d908c6b5 docs(backlog): remove STT providers and async design doc
Delete the backlog markdown file that outlined additional STT providers and the future async transcription architecture, as the content is now superseded by newer implementation plans.
2026-06-21 20:58:08 +03:00
claude_code
d188c9e876 docs(backlog): add design for AI attribution of MCP-authored comments
Document Variant B for showing MCP-created comments (and pages) as AI
rather than as the service-account user, reusing the existing agent
provenance infrastructure (§15 C3).

- Root cause: MCP logs in via a plain service-account token, so
  provenance.actor stays 'user' and created_source defaults to 'user';
  the comment sidebar also renders no AI badge.
- B1 (backend): mark the MCP identity as agent via a new users.is_agent
  flag; jwt.strategy derives req.raw.actor from it (non-spoofable).
  Relax the provenance aiChatId type to string | null for external MCP.
- B2 (frontend): extend IComment with createdSource/aiChatId, extract a
  shared AiAgentBadge, render it in comment-list-item.
- Includes edge cases, tests, scope decisions, and acceptance criteria.
2026-06-21 20:58:02 +03:00
claude_code
59c2913d72 style(ai-chat): widen role cards to fill the chat window
Role cards in the new-chat empty state were capped at max-width 200px and
never grew, leaving large side gaps in a wide window. Make the cards flex
to fill each row (flex: 1 1 240px) and raise min/max width so they get
wider and use the available window width while still wrapping to ~2 columns
at the default window size.
2026-06-21 20:51:44 +03:00
claude_code
7171dfbdf0 fix(ai): classify AI provider error status in logs and UI
Provider auth failures were logged with the provider's opaque message only
(e.g. OpenRouter returns "401: User not found." for a bad/missing API key),
which reads like a missing wiki user rather than a credentials problem.

describeProviderError now prepends a clear, human-readable English label for
a small set of well-known HTTP statuses while keeping the original detail
(status + provider message + truncated response-body snippet):
  - 401/403 -> authentication failed (invalid or missing API key)
  - 402     -> insufficient credits or quota
  - 429     -> rate limit exceeded
Other statuses and status-less errors are formatted exactly as before. The
label is a static string and never contains the API key. Benefits every
caller (embedding processor, indexer, AI "Test endpoint" UI) at once.

Tests: switch the plain status+message case to a non-classified status (500);
add 401/403/402/429 cases; keep 502/503 as regression guards for the
unchanged path.
2026-06-21 19:55:45 +03:00
claude_code
4f8015b342 Merge branch 'develop' into test/coverage-refactor 2026-06-21 19:12:13 +03:00
claude_code
3d4ad664b3 test(refactor-tail): extract pure cores + cover collab/share/ai-chat/client gate
Batches 6-9: behaviour-preserving extractions of testable pure cores plus the
tests they unblock, and a fix for the broken client test environment.
Full suites green: server 113 suites / 1117 + 1 todo, client 30 files / 338.

client (R0 infra):
- vitest.setup.ts: in-memory localStorage/sessionStorage Storage stub wired via
  setupFiles. Unblocks menu-items.gating.test.ts (was 9 failing) -> client suite
  fully green. + menu-items.suggestions.test.ts (getSuggestionItems filter/sort).

share:
- extract buildShareMetaHtml (share-seo.util.ts) from the SEO controller; tests
  for reflected-XSS escaping in <title>/og/twitter meta, noindex, truncation;
  extractPageSlugId; updateAttachmentAttr; prepareContentForShare comment-strip
  (anonymous-viewer metadata-leak guard).

ai-chat (security extractions):
- selectAccessibleHits: CASL post-filter for semantic search (restricted page in
  an accessible space must NOT leak to the agent).
- validateResolvedAddresses: SSRF connect-time guard (block if ANY resolved
  address is private).
- resolveAudioFormat: mime whitelist (dead `?? 'webm'` fallback dropped, set
  unchanged). + mcp-servers toView header-leak guard, MCP tool namespacing.

collaboration (data-loss area):
- extract computeHistoryJob (pins the "agent delay MUST stay 0" invariant) and
  resolveSource. Integration: onAuthenticate read-only matrix (collab auth
  bypass), HistoryProcessor (contributor restore on save failure), onStoreDocument
  Approach-A boundary snapshot (human revision pinned before agent overwrite).

Reviewed (APPROVE WITH SUGGESTIONS): extractions behaviour-preserving, security
tests mutation-resistant.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 19:10:27 +03:00
claude_code
cdcf3c0639 Merge branch 'refactor/ai-tools-spec-registry' into develop
Shared zod-agnostic tool-spec registry for the 14 identical AI tools across
the standalone MCP server and the in-app AI-SDK chat (keeps execute/auth and
the ~17 intentionally-divergent guardrail tools per-layer), folds in the
edit_page_text drift-bug fix, and formalizes the integration-test db factory.
2026-06-21 18:57:10 +03:00
claude_code
f3fa15e746 refactor(ai-chat): shared tool-spec registry for identical tools; formalize integration db factory
Implements two architecture follow-ups from the multi-aspect review.

1. Shared, zod-agnostic tool-spec registry (packages/mcp/src/tool-specs.ts)
   for the 14 AI tools whose name + schema + model-facing description are
   genuinely identical across the standalone MCP server and the in-app
   AI-SDK chat. Both layers consume it (registerShared in index.ts;
   sharedTool in ai-chat-tools.service.ts) and keep their own execute/auth.
   - Zod-agnostic builders (z) => ZodRawShape bridge the zod v3 (mcp) vs
     zod v4 (server) split; the registry imports no zod.
   - Folds in the documented edit_page_text drift-bug fix: the stale
     "strip-and-retry tolerated" claim is gone; canonical wording states a
     formatting-only change is refused into failed[].
   - Sibling-tool references in shared descriptions are transport-neutral so
     one description is correct for both snake_case (MCP) and camelCase
     (in-app) tool names.
   - Loader fail-fast guard for a stale @docmost/mcp build.
   - The ~17 intentionally-divergent tools (security guardrails, tuned UX)
     stay per-layer, untouched.
   - Rebuilt committed mcp artifacts (also regenerates a previously stale
     build/lib/docmost-schema.js to match its already-committed source).

2. Formalize apps/server/test/integration/db.ts as the canonical
   integration-test seed factory (module doc + a shortId helper); the
   hand-written minimal seeders are kept on purpose, decoupled from the
   app service-layer side effects.

Verified: server tsc + lint clean, mcp build clean; mcp unit tests 261 pass,
ai-chat-tools.service 16 pass, public-share-chat-tools 8 pass, ai-chat suite
224 pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 18:57:00 +03:00
claude_code
0bbf94c154 feat(ai-chat): surface the real cause in the error banner
The AI chat error banner always showed a generic "Something went wrong"
with no reason. The server already forwards the provider cause into the
stream (e.g. "Cannot connect to API: read ECONNRESET"), but the client
hid it behind a static heading.

- describeChatError now returns { title, detail }: a short heading naming
  the cause category plus a one-line explanation.
- Add classifyProviderError: maps connection reset, timeout, rate limit,
  context-window overflow, quota and auth failures to clear categories;
  the 403/503 gating responses are preserved; unknown errors fall back to
  the verbatim provider text.
- Match HTTP status codes only as the leading token and textual signatures
  only against the message head (before "| response body:"), so a number
  or phrase in the response-body snippet never mislabels the cause.
- Use the new {title, detail} in all three banners: chat-thread,
  share-ai-widget and the persisted-error banner in message-item.
- Cover the classifier with 20 unit tests (categories + regressions).
2026-06-21 18:54:43 +03:00
claude_code
0cfc3c8f89 Merge branch 'develop' into test/coverage-batch1 2026-06-21 18:51:14 +03:00
claude_code
4df79aafd3 test(server): batch 5 authorization, transclusion, search & comment coverage
Test-only. Fills the authorization / data-integrity gaps from the strategy
report. Full server suite: 100 suites / 1031 passed + 1 todo, green.

Authorization (privilege-escalation catches):
- workspace/space ability factories: exact can/cannot per (action,subject) —
  admin cannot Manage Audit, writer/reader cannot Manage Settings/Member, etc.
- findHighestUserSpaceRole, isAdminActingOnOwner.
- WorkspaceService role guards: last-owner lockout, admin-over-owner, self-target.
- SpaceMemberService.validateLastAdmin: never orphan a space without an admin.
- GroupService: default-group immutability, name uniqueness.

Access / data integrity:
- PageAccessService: restriction-vs-space-ability branches for view/edit/comment.
- TransclusionService.unsyncReference: cross-workspace/NotFound boundary asserts
  NO attachment write or ref-row delete on rejection; lookupWithAccessSet
  positional status mapping; listReferences drops private/cross-ws/deleted refs;
  syncPageTransclusions/References diff (no-op on unchanged content).
- SearchService.searchPage: query-mode scoping; leakage modes return empty
  before executing the query.
- CommentService: reply-to-reply guard, agent provenance, self-mention filter,
  no double-notify.

Pure helpers:
- prosemirror extractors (mention dedup-key id-vs-entityId, attachment UUID
  validation, removeMarkTypeFromDoc), collaboration.util (getPageId,
  isEmptyParagraphDoc, stripUnknownNodes unwrap, prosemirrorNodeToYElement).

Reviewed (APPROVE WITH SUGGESTIONS): mutation-resistant, not vacuous.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 18:40:07 +03:00
claude_code
0b2af34029 test(integrations/client/packages): batch 2-4 unit coverage + zip-slip guard extraction
Batch 2-4 of the test-strategy rollout. Test-only except one minimal,
behaviour-preserving extraction in file.utils.ts. All suites green:
server 82 suites/836+1todo, editor-ext 86, mcp 270, client (new files) 86.

integrations (server):
- file.utils.ts: extract pure `isEntryPathSafe(entryName, targetDir)` from
  extractZipInternal so the zip-slip/path-traversal guard is unit-testable;
  call site rerouted, behaviour identical (only a warn-message string merged).
- file.utils.zip-safety.spec.ts: traversal/strip/__MACOSX/prefix-confusion
  cases (mutation-resistant: fails if containment loses the path.sep).
- import-formatter / import.utils / table-utils / export utils / import.service
  extractTitleAndRemoveHeading: pure import/export transforms, Notion/XWiki
  formatting, table colspan widths (idempotent), slug/link rewriting.

client:
- safeRedirectPath: open-redirect guard, every reject branch independently.
- buildChatMarkdown (fence anti-breakout), label-colors, normalize-label,
  share tree build, page URL builders, notification time-grouping (fake clock).

packages:
- editor-ext: deriveFootnoteId golden table, parseHtmlEmbedHeight crafted
  values, orphan footnote extraction.
- mcp: deriveFootnoteId parity (drift guard vs editor-ext), applyTextEdits
  idempotency + cross-block replaceAll, diffDocs/summarizeChange on reorder.

Reviewed (APPROVE): extraction behaviour-preserving, assertions mutation-resistant.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 18:22:15 +03:00
claude_code
74e2b7ad7f Merge branch 'fix/ai-chat-role-cards-fit' into develop
Fit full role-card description text in the AI chat empty state and show a
generic "AI is typing…" indicator (role name kept only as the dimmed
interlocutor label).
2026-06-21 17:11:56 +03:00
claude_code
a86d0c7c3b fix(ai-chat): always show generic "AI is typing…" indicator
The typing indicator rendered "<role name> is typing…". Show a generic
"AI is typing…" instead and keep the role/identity name only in the
dimmed interlocutor label above the typing dots.

- typing line now always renders t("AI is typing…")
- add the "AI is typing…" key to en-US and ru-RU locales
- sync stale doc comments that referenced the old text

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 17:11:21 +03:00
claude_code
569da822b6 fix(ai-chat): fit full role-card description text in new-chat empty state
The colored role cards in the AI chat empty state truncated their
admin-configured description with an ellipsis and could clip the top row
when the cards overflowed. Make the full text fit:

- drop the description lineClamp so the whole text renders
- add overflow-wrap: anywhere so long unbreakable tokens (URLs) wrap
- switch the cards container to align-content: flex-start so an
  overflowing top row stays reachable while scrolling (the parent
  Mantine Center still vertically centers the block when it fits)
- widen the card max-width 180px -> 200px for more text room

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 17:11:21 +03:00
claude_code
f8e8ada581 test(server): add behavioural unit tests for auth + common security helpers
Batch 1 of the test-strategy rollout. Fills the highest-value gaps where
existing specs were only `toBeDefined()` smoke tests or absent. Test-only,
no production source touched.

- token.service.behavior.spec.ts: verifyJwt type-mismatch rejection (confused
  deputy), generateAccessToken/generateCollabToken disabled-user -> Forbidden,
  agent `actor` claim only from signed provenance, correct expiry.
- auth.util.spec.ts: computeEmailSignature (stable HMAC, case-normalized),
  throwIfEmailNotVerified, validateSsoEnforcement, validateAllowedEmail;
  it.todo flags the unguarded `@`-less email TypeError.
- guards/setup.guard.spec.ts: cloud blocks setup, first-run allows, re-run on
  an initialised instance is forbidden (privilege escalation guard).
- security-headers.spec.ts: resolveFrameHeader clickjacking/CSP branches.
- utils.security.spec.ts: redactSensitiveUrl, extractBearerTokenFromHeader,
  parseRedisUrl, normalizePostgresUrl, diffAuditTrackedFields, isUserDisabled.

60 tests + 1 todo, all green. Reviewed for mutation resistance.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 17:00:09 +03:00
claude_code
4720705155 Merge branch 'docs/review-followups' into develop
Review follow-ups (docs only):
- CHANGELOG [Unreleased]: post-0.93.0 share-AI cap lowered 300->100 (#62)
- backlog: track deferred AiChatService.stream integration coverage
2026-06-21 16:57:13 +03:00
claude_code
ce60498a90 docs: track post-0.93.0 share-AI cap change + deferred stream-coverage debt
Follow-ups from the multi-aspect review of the e5bc82c7..d4658d4c range.

- CHANGELOG: document under [Unreleased] that the default per-workspace
  hourly public-share assistant cap was lowered 300 -> 100 after the
  v0.93.0 tag (#62). v0.93.0 shipped 300, so existing deployments that
  never set SHARE_AI_WORKSPACE_MAX_PER_HOUR drop to 100 on upgrade.
- Recreate the still-open Section 3 (AiChatService.stream integration
  coverage) of the deleted feature-test-coverage-deferred.md as a focused
  backlog doc so the test debt stays tracked; Sections 1-2 are already
  closed by the integration harness (PR #115).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 16:54:56 +03:00
4a22cc1955 Merge pull request 'feat(ai-chat): role cards start the chat and show role identity' (#121) from feat/ai-chat-role-cards-ux into develop
Reviewed-on: #121
2026-06-21 16:28:51 +03:00
claude_code
b83a5d4597 feat(ai-chat): role cards start the chat and show role identity
Rework the new-chat role-card empty state:
- Remove the "Universal assistant" card; universal assistant is now the
  implicit default the user gets by typing without picking a card.
- Show each role's description on its card (under the emoji and name).
- Clicking a card immediately starts the chat: it binds the role to the
  new chat and sends the default opening prompt "Take a look at the
  current document" (one click, no separate select step). roleIdRef is
  set synchronously before sendMessage so the create request carries the
  role.
- Show the current role's name in the window header badge and as the
  assistant's display name (transcript label + "… is typing…"), falling
  back to "AI agent" for a role-less chat. selectChat resets the picked
  role so it cannot leak into an unrelated existing chat.
- Add the "Take a look at the current document" i18n key (en-US, ru-RU).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 16:20:36 +03:00
claude_code
d4658d4cb3 Merge pull request '#114 refactor(ai-chat): shared parseNodeArg helper; keep duplication backlog doc' (#114) from refactor/ai-chat-tool-spec-registry into develop
# Conflicts:
#	apps/server/src/core/ai-chat/tools/ai-chat-tools.service.ts
2026-06-21 14:45:20 +03:00
claude_code
4105836a2d Merge pull request '#112 test(ai-chat): current-page coverage + getCurrentPage helper' (#112) from feat/ai-chat-current-page-robustness into develop 2026-06-21 14:31:12 +03:00
claude_code
f5a45d5453 Merge pull request '#115 test(server): integration harness + deferred coverage' (#115) from test/deferred-integration-coverage into develop 2026-06-21 14:31:12 +03:00
claude_code
9fad6ab73b Merge pull request '#113 feat(ai-chat): role-selection cards empty-state' (#113) from feat/ai-chat-role-cards into develop 2026-06-21 14:31:11 +03:00