* release: v3.2.4
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* release: add detailed changelog to v3.2.4 notes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* ci: add automated release workflow on PR merge
Triggers when a PR targeting master is merged with a strict semver title
(e.g., "3.2.3"). Builds the plugin and creates a GitHub Release with
main.js, manifest.json, and styles.css attached. Non-semver PR titles
are silently skipped. Includes manifest.json version match validation
and shell injection protection for release notes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: pin release checkout to merge commit SHA
Avoids race condition where a concurrent PR merge could cause the release
workflow to build a different commit than the one that triggered it.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: prevent shell injection in release workflow and add idempotency check
Use env vars instead of inline ${{ }} expansion for PR title and version
outputs to prevent shell injection. Add pre-check to skip release creation
if the version tag already exists.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: pin release tag to merge commit SHA via --target flag
Ensures gh release create tags exactly the merge commit, preventing
a source/binary mismatch if concurrent merges land before the step runs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Browser's native fetch() fails with net::ERR_CERT_AUTHORITY_INVALID when
calling models.brevilabs.com. Using safeFetch routes through Obsidian's
requestUrl (Node/Electron layer), bypassing browser-level TLS validation.
Fixes#2250
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
- Renamed docs/ to designdocs/
- Moved draft/todo docs into designdocs/todo/ subfolder
- Added OBSIDIAN_CLI_INTEGRATION.md from master's todo/ folder
- Updated all docs/ path references in CLAUDE.md, AGENTS.md, and designdocs
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(chat-history): add infinite scroll pagination to ChatHistoryPopover
Progressively loads chat history in batches of 50 via IntersectionObserver
on a sentinel div, preventing UI lag for users with 1000+ conversations.
- Observer is created once on mount with empty deps; reads live pagination
state via paginationStateRef — no disconnect/reconnect as pages load or
search filters change
- Reset only fires when popover opens or search changes while open (not close)
- Status indicator is plain text (data is local/synchronous, not async)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(chat-history): use useLayoutEffect to prevent pagination reset render spike
Replace useEffect with useLayoutEffect for the displayCount reset so it
runs synchronously before the browser paints, eliminating the one-frame
render spike that occurred when reopening the popover with a large
displayCount from a previous scroll session.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: add UI rendering performance audit report
Comprehensive audit of React rendering inefficiencies across the chat UI,
state management, and streaming paths. 14 findings ranked by severity
(Critical/High/Medium/Low) with recommended fix priorities.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Load the Component used for MarkdownRenderer so that post-processor
children (e.g. advanced-table-xt SheetElement) can execute their
onload() lifecycle. Also add table border/padding styles to
.message-content to match Obsidian's native table appearance.
Upgrade @langchain/google-genai from 1.0.2 to 2.1.23 to fix a crash
where `candidateContent.parts.reduce()` throws when `parts` is undefined
during Gemini streaming responses. The new version uses optional chaining
(`candidateContent.parts?.reduce()`).
Also upgrades @langchain/core (1.1.13 → 1.1.29) and langchain (1.2.8 →
1.2.28) to align peer dependencies.
Fixes#2248
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Root cause: Gemini's @langchain/google-genai nests tool call names inside
`functionCall.name` instead of at the top-level `name` property. Our
streaming accumulator only checked `tc.name`, so all Gemini tool call
names were silently dropped. `buildToolCallsFromChunks` then skipped
nameless entries, causing the agent loop to treat the response as
"no tool calls" and return empty content (thinking tokens were filtered).
Changes:
- Extract `accumulateToolCallChunk` into nativeToolCalling.ts with
`functionCall.name` fallback via nullish coalescing
- Add empty response detection with diagnostic logging
- Rewrite tests to cover real code paths with both Gemini-format and
OpenAI-format tool call chunks
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Add placeholder text and syntax instructions to clarify that note
references in system prompts require {[[Note Name]]} syntax (curly
braces), since bare [[Note Name]] wikilinks are not resolved.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Add a "None (use built-in prompt)" option to the system prompt dropdown
so users can deselect a custom system prompt and revert to the default.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Qwen 3.5 (LM Studio) emits returnAll as string "True"/"False" instead
of boolean, causing Zod schema validation to fail. Add z.preprocess()
to coerce string booleans to actual booleans before validation.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add custom Miyo server URL setting for remote deployments
Introduces a miyoServerUrl setting that lets advanced users point Copilot
at a Miyo instance running on a remote machine. When blank, automatic local
service discovery is used as before. Also fixes a crash (undefined.trim())
that occurred when existing saved settings lacked the new field.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: update Miyo test mocks to use miyoServerUrl and getMiyoCustomUrl
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
The Miyo toggle is only visible when Self-Host Mode is already enabled,
which requires passing validateSelfHostMode() first. Calling it again
when enabling Miyo was redundant and could incorrectly block users who
are offline after their grace period expires mid-session.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
The keyboard shortcuts for Replace (Ctrl/Cmd+Enter) and Insert
(Ctrl/Cmd+Shift+Enter) were lost during the Quick Ask refactor. This
restores them with proper popout-window and multi-modal scoping.
Fixes#2219
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
When "Auto-Add Selection to Context" was used, changing the selection
and sending a new message would still show the old selection to the LLM.
The root cause was that selected_text / web_selected_text segments from
previous turns were promoted into L2 context verbatim, shadowing the
current turn's fresh context.
Fix: in compactSegmentForL2(), strip non-recoverable blocks (derived
from contextBlockRegistry) and prior_context_note before compacting.
Uses a "remove blacklist" approach that preserves unknown tags and
inter-block text. Also tighten hasCompactedContent detection to require
the source= attribute, avoiding false-positives from literal XML in
user note content.
* fix: close Radix portaled layers when mobile drawer hides
Radix popover/dropdown portals to document.body, causing them to jump
to (0,0) when the mobile drawer hides and the anchor element disappears.
- Add hideWhenDetached on PopoverContent and DropdownMenuContent for mobile
- Observe drawer is-hidden class change and dispatch Escape to close state
- Add cancelable: true to synthetic Escape for correct preventDefault semantics
* fix: reattach drawer hide observer when view moves between containers
The drawer hide observer was bound once during onOpen, but the view can
move between containers without onOpen firing again. Listen for
layout-change events to re-bind the observer to the current drawer.
Buffer.byteLength is unavailable on Obsidian mobile (Android/iOS WebView),
causing indexing to fail with "Buffer is not defined". Use the existing
MemoryManager.getByteSize() helper which uses TextEncoder instead.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* 3.2.3
* fix: rename "Copy command" to "Duplicate command" with copy-plus icon (#2150)
Improves UX clarity by distinguishing the duplicate action from clipboard copy.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Replace native fetch() with safeFetchNoThrow() in makeRequest() so API
calls route through Obsidian's requestUrl (Electron/Node layer) instead
of Chromium's network stack, eliminating intermittent CORS failures.
makeFormDataRequest retains native fetch() because safeFetch hardcodes
Content-Type: application/json and calls body.toString(), which breaks
FormData payloads.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Add a user-configurable setting to save converted document markdown
(from pdf4llm/docs4llm) to a specified vault folder. Previously,
converted content was only stored in internal cache with opaque names.
Also routes PDFs through Miyo in project mode when self-host is enabled,
preserving privacy by never falling back to cloud APIs in self-host mode.
Changes:
- New "Store converted markdown at" setting under Document Processor
(empty = disabled, suggested: copilot/converteddocs)
- Flat file naming ({basename}.md) with double-underscore disambiguation
for collisions, source-header traceability, and content-dedup to avoid
mtime churn
- Export on cache hits so enabling the setting retroactively works
- SelfHostPdfParser in Docs4LLMParser for Miyo-first PDF parsing in
project mode
- MiyoParseResult type (null | {content} | {error}) propagates detailed
Miyo errors to the user instead of generic failure messages
- Self-host privacy: PDF parse failures return an error with details
rather than silently uploading to cloud API
- TODO comments for request timeout, batch progress feedback
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
When Miyo returns a non-"ok" status (e.g. "degraded"), isBackendAvailable
silently returned false with no log, making users think the health check
passed while still seeing the "Miyo app is not available" notification.
Now logs the actual status unconditionally so users can diagnose why
availability check failed. Also removes the debug-only guard on the
catch block so network errors are always surfaced.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
When defaultSaveFolder is a hidden directory (e.g. .copilot/conversations),
Obsidian's metadata cache doesn't index it, causing saves to fail and
chat history to show empty.
Add adapter-level fallback utilities in src/utils/vaultAdapterUtils.ts:
- resolveFileByPath: vault lookup with synthetic TFile fallback
- listMarkdownFiles: folder listing with adapter fallback
- patchFrontmatter: processFrontMatter with adapter YAML patching fallback
- readFrontmatterViaAdapter: parse frontmatter from raw file content
Apply fallbacks across ChatPersistenceManager (save, load, list, find)
and main.ts (history picker, load, delete, title update, lastAccessedAt).
Also removes obsolete design docs from docs/.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address Miyo PR review findings (privacy, dedup, architecture)
- Gate request body logging behind debug flag to prevent note content
leaking to vault log files on every upsert (P1 privacy fix)
- Replace duplicated grace period logic in findRelevantNotes with
isSelfHostAccessValid() from plusUtils (P1 dedup fix)
- Spread-copy metadata in fromMiyoDocument to avoid mutating API response
- Remove dead if(enabled) branch in CopilotPlusSettings toggle handler
- Gate per-document upsert log lines behind debug to prevent log churn
- Rename misleading test; add no-embeddings fallback path test coverage
- Add TODO for cross-platform service discovery paths (Windows/Linux)
- Use this.client directly in MiyoSemanticRetriever.getExplicitChunks
instead of VectorStoreManager singleton to avoid backend mismatch
- Remove unused apiKeyOverride param from buildHeaders; document dual auth
- Read pagination total only from first response in getIndexedFiles
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: address PR review comments on Miyo retriever and index backend
- Guard response.documents with ?? [] in getExplicitChunks to prevent
crash when backend omits the field or parseResponseJson returns {}
- Remove redundant getSettings().debug guard around logInfo calls since
the logging utilities already respect the debug flag internally
- Document the debug-flag behavior in AGENTS.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor: remove explicit path reads from Miyo semantic retriever
* fix: rename remaining enableMiyoSearch references
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- Rename `enableMiyoSearch` → `enableMiyo` across settings, UI, and all consumers
- Skip fulltext search in performLexicalSearch (Plus/agent path) when Miyo is active;
filterRetriever still runs for tag/title matching
- VaultQA chain now passes `{ enableMiyo: false }` to RetrieverFactory so it never
routes through Miyo
- Expose `RetrieverFactory.isMiyoActive()` for clean Miyo-state checks
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: upgrade @langchain/google-genai to fix Gemini 3 preview image support (#2178)
The @langchain/google-genai v1.0.0 had a hardcoded _isMultimodalModel check
that only recognized gemini-1.5, gemini-2, and "vision" model names. Gemini 3
preview models (gemini-3-flash-preview, gemini-3-pro-preview) were rejected
with "This model does not support images" when sending image content.
Upgrade to v1.0.2 which adds gemini-3 to the multimodal model pattern.
Also bump @google/generative-ai from ^0.21.0 to ^0.24.0 (required dep).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: update built-in Gemini Pro to 3.1 preview model IDs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add Firecrawl and Supadata self-host support for web search and YouTube
Self-host mode users can now use their own API keys for web search (Firecrawl)
and YouTube transcripts (Supadata) instead of routing through the Brevilabs
backend. Tools gracefully fall back to Brevilabs when no self-host key is set.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add Perplexity Sonar as alternative self-host web search provider
Add provider dispatch pattern for self-host web search, supporting both
Firecrawl and Perplexity Sonar via a dropdown selector in settings UI.
Changes:
- Refactor selfHostWebSearch into provider dispatch (firecrawl/perplexity)
- Add hasSelfHostSearchKey() helper for provider-agnostic key checks
- Add settings UI dropdown for web search provider selection
- Fix Mention.ts YouTube transcription to route via Supadata in self-host mode
- Fix cached failed URL results never being retried (retry-on-error)
- Clear Miyo Search state when self-host mode is disabled
- Add 19 unit tests for selfHostServices
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add Firecrawl and Supadata self-host support for web search and YouTube
Self-host mode users can now use their own API keys for web search (Firecrawl)
and YouTube transcripts (Supadata) instead of routing through the Brevilabs
backend. Tools gracefully fall back to Brevilabs when no self-host key is set.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: drop legacy Perplexity wrapper from self-host web search
selfHostWebSearch now returns { content, citations } directly instead of
wrapping results in the OpenAI-chat-completion shaped WebSearchResponse
that Brevilabs inherited from Perplexity. SearchTools unwraps each path
independently.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: move Miyo Search under self-host conditional and improve description
Miyo Search toggle is now only visible when self-host mode is enabled,
grouped alongside Firecrawl and Supadata settings. Updated description
to be less technical and highlight vault size advantage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* docs: add token budget enforcement analysis and fix plan
Document root cause of context window overflow (2.7M tokens sent to 1M
model): L4 chat history bypasses all compaction systems. Add fix plan
with phased approach to enforce token budget at message assembly point.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: correct root cause analysis — L1 unbudgeted, not L4
Previous analysis incorrectly blamed L4 chat history as the primary
culprit. Investigation shows L4 stores only bare L5 text + compacted
responses. The real issue is systemic: no total payload enforcement,
with L1 (project context) being the largest unbudgeted layer and
PROJECT_COMPACT_THRESHOLD being blind to L1 size.
Updated fix plan to be model-agnostic (use autoCompactThreshold as
single budget, no model-specific lookup tables), added contextTurns
deprecation, and history guarantee principle.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: hard cap L1 project context at 600k tokens + payload diagnostics
- Truncate project context at 2.4M chars (~600k tokens) to prevent
total payload from exceeding model context windows (temporary fix
until full token budget enforcement is implemented)
- Add per-layer token estimate logging when payload exceeds 2M chars
to help diagnose context window overflow reports
- Document Obsidian CLI dev tools in CLAUDE.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Fix "Cannot set properties of undefined (setting 'embedding')" during indexing
Rebuild internalDocumentIDStore after loading partitioned chunks to eliminate
ghost IDs from removed documents that cause position mismatches. Also add
missing nchars field to Orama schema to match the OramaDocument interface.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Remove Notice popup spam for per-file indexing errors
Errors during indexing (e.g. "Semantic index database not found" when DB
isn't loaded yet) were triggering a Notice popup for every file event,
flooding the user with popups. Errors are already logged to console and
surfaced in the in-chat IndexingProgressCard.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Fix unwanted indexing when semantic search disabled + editor lag during indexing
Add defense-in-depth enableSemanticSearchV3 guard to both VectorStoreManager
and IndexOperations to prevent indexing when semantic search is off (fixes
plusUtils.applyPlusSettings bypass). Throttle Jotai atom updates to 500ms
intervals during indexing to reduce React re-renders. Yield to main thread
between batches to keep the editor responsive.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Remove yieldToMainThread — existing awaits already yield to the event loop
The batch loop already has multiple await points (rateLimiter.wait,
embedDocuments, upsert, save) that yield to the main thread. The explicit
setTimeout(0) was redundant and added no user-perceived benefit.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Guard throttled callback against stale indexing sessions
Cancel pending throttle timer in resetIndexingProgressState() so a
trailing write from a previous (failed/aborted) run cannot overwrite
the freshly-reset atom state of a new run. Also reorder declarations
so throttle variables are defined before the function that references them.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: move badge close button to left icon overlay on hover
Centralize the icon + X overlay pattern in ContextBadgeWrapper instead of
copy-pasting the X button across all 8 badge types. The X now appears on
hover over the left icon position using invisible/visible toggling for
zero layout shift.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* support mobile
* fix keyboard control
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Add optional streamUsage setting to CustomModel interface that allows users
to control whether stream_options is sent to the API. This fixes compatibility
with 3rd party providers (e.g., Databricks, MLFlow) that do not support the
stream_options parameter.
- Add streamUsage?: boolean to CustomModel interface in aiParams.ts
- Pass streamUsage to ChatOpenAI config for OPENAI_FORMAT and LM_STUDIO providers
- Add UI toggle in ModelAddDialog and ModelEditDialog with helpful tooltip
- Default to false to maintain compatibility with providers that error on stream_options
Closes#2046
Sanitize LLM-generated file paths before creating files to prevent
ENAMETOOLONG crashes on Linux (ext4 255-byte filename limit). Truncates
overlong basenames while preserving file extensions and multi-byte
character boundaries. Sanitization happens at the tool entry point so
the preview flow (ApplyView) also receives the safe path.
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
* Replace indexing Notice with in-chat progress card & fix phantom re-indexing
Replace the Obsidian Notice popup for indexing progress with a persistent
in-chat progress card using the same Jotai atom pattern as project mode.
Key changes:
- New IndexingProgressCard component with progress bar, pause/resume/stop,
error display, and auto-close on completion
- Jotai atom (indexingProgressAtom) driven by IndexOperations, read by React
- Card only appears when files actually need indexing (no flash on mode switch)
- User-initiated actions (refresh/reindex buttons) show "Index Up to Date"
feedback with green check icon
- Fix phantom re-indexing: checkIndexIntegrity() is now diagnostic-only
- Fix progress bar accuracy: totalFiles counted after chunk preparation
- Remove duplicate completion Notices from ChatControls and RelevantNotes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Fix insert/replace at cursor including agent reasoning blocks (#2174)
Strip <!--AGENT_REASONING:...--> markers in cleanMessageForCopy() so
that "Insert / Replace at cursor" only inserts the actual AI response,
not the internal agent reasoning metadata.
Uses greedy .* (single-line) so the regex matches to the real closing
--> even if the JSON payload contains that sequence.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Restore integrity check marking files for re-indexing
checkIndexIntegrity() was made diagnostic-only to fix phantom
re-indexing, but this left files with missing embeddings permanently
skipped. The real safeguard is clearFilesMissingEmbeddings() at the
start of each indexing run, which prevents the repeat cycle. Restore
the mark so the next indexing trigger picks up integrity failures.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Strip <!--AGENT_REASONING:...--> markers in cleanMessageForCopy() so
that "Insert / Replace at cursor" only inserts the actual AI response,
not the internal agent reasoning metadata.
Uses greedy .* (single-line) so the regex matches to the real closing
--> even if the JSON payload contains that sequence.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>