Removes the `no-restricted-imports: off` override so the obsidianmd recommended
rule applies (bans axios/node-fetch/moment/etc.). Replaces the two `moment(...)`
call sites in `src/utils.ts` with `luxon`'s `DateTime` (already a dep) and adds
unit tests covering local/UTC formatting, zero-padding, immutability, and the
invalid-input fallback. Integration tests get a per-file override since they
need `node-fetch` to polyfill jsdom fetch. `no-restricted-globals` stays
disabled — banning the global `app` will land in a separate PR.
Removes the "off" overrides for depend/ban-dependencies, configures the
package.json check with an allowed-list for deps we deliberately keep
(crypto-js, lodash.debounce, eslint-plugin-react, lint-staged, npm-run-all),
and drops three unused direct deps: axios, builtin-modules, and node-fetch
(plus @types/node-fetch). The integration-test fetch polyfills are no longer
needed in Node 18+ jsdom.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Removes the deferred "off" overrides from eslint.config.mjs so the
obsidianmd recommended config's "error" severity applies. Scopes
import/no-nodejs-modules off for test setup, mocks, and Node-context
build scripts (e.g. jest.setup.js polyfills TextEncoder/TextDecoder
from node:util). import/no-extraneous-dependencies is now enabled
everywhere; declared js-yaml, dotenv, and @jest/globals as explicit
devDependencies so transitive uses pass.
Fixes the one real violation in src/utils.ts by migrating two moment
call sites (formatDateTime, stringToFormattedDateTime) to Luxon, which
is already in dependencies. Adds 10 unit tests covering UTC/local
formatting, zero-padding, round-tripping, and invalid-input fallback.
Remove the explicit "off" overrides in eslint.config.mjs so the rules
from obsidianmd.configs.recommended apply. Add a scoped disable for the
sole violation in scripts/printPromptDebug.js, where the dynamic import
targets a controlled path under os.tmpdir() produced by esbuild.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Group the 14 disabled @typescript-eslint rules into Heavy / Medium / Quick-win
buckets with inline violation counts measured against src/**/*.{ts,tsx}. Makes
it obvious which rules (e.g. restrict-template-expressions at 1, no-base-to-string
at 7) are cheap follow-ups vs. the no-unsafe-* family that dominates the block.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drops the `off` override so the rule (set to `error` by obsidianmd's
recommended config) is enforced. Fixes the one production violation in
markdown-preview by using `replaceChildren()` instead of clearing via
`innerHTML`. Rewrites test fixtures to use `DOMParser` and mock impls
to use `textContent` so the rule is on for tests too.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(lint): enable obsidianmd/rule-custom-message and fix violations
Turn on the obsidianmd/rule-custom-message ESLint rule (which wraps no-console
to enforce logInfo/logWarn/logError over console.log per AGENTS.md). Swap
console.log → logInfo across LLMProviders/ and search/, and delete console.log
noise from test files.
src/logger.ts, src/chainFactory.ts (circular import with constants), and
scripts/** are exempted via narrow file overrides.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(lint): enable obsidianmd/no-unsupported-api (#2414)
Removes the deferred "off" override so the rule runs at the recommended
severity on .ts/.tsx. With manifest.minAppVersion=1.4.0 and obsidian@1.2.5
(no @since tags) the rule fires on nothing today — it acts as a guard for
future obsidian type-stub bumps.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(lint): enable obsidianmd/object-assign rule (#2415)
Follow-up to #2410. The rule only flags `Object.assign(<ident-containing-default>, <non-object-literal>)` — the Obsidian anti-pattern of mutating DEFAULT_SETTINGS. No call sites in this repo match, so enabling produces zero new errors.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor: extract ChainType into src/chainType.ts to break import cycle
constants.ts → chainFactory.ts → logger.ts → settings/model.ts → constants.ts
formed a runtime cycle because chainFactory.ts (a heavy LangChain module) re-
exported the ChainType enum that constants.ts needed at module load. Move the
enum into a tiny standalone file, update all 26 importers, and restore
logInfo in chainFactory.ts. The eslint override for chainFactory.ts is no
longer needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Follow-up to #2410. The rule only flags `Object.assign(<ident-containing-default>, <non-object-literal>)` — the Obsidian anti-pattern of mutating DEFAULT_SETTINGS. No call sites in this repo match, so enabling produces zero new errors.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Removes the deferred "off" override so the rule runs at the recommended
severity on .ts/.tsx. With manifest.minAppVersion=1.4.0 and obsidian@1.2.5
(no @since tags) the rule fires on nothing today — it acts as a guard for
future obsidian type-stub bumps.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Removes the deferred `"off"` override and rewrites the 55 violations
(all in test/mocks/Node-script files) to use `window.*` instead of
`global.*`. Production code was already cleaned up in #2401.
- 18 test files + __mocks__/obsidian.js + jest.setup.js: mechanical
`global.X` → `window.X` (Jest runs under jsdom, so window === globalThis)
- scripts/printPromptDebugEntry.ts: inline eslint-disable — Node-only
debug script with no window available
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Flip the rule from "off" to "error" to catch accidental references to
the global `document` (which always points at the main window even when
the user is interacting with a popout).
The only flagged site was a `document` field on the `RelevantNoteEntry`
type — not a DOM reference, but the rule pattern-matches the identifier.
Rename it to `note` so `entry.note.path/title` reads naturally next to
`entry.metadata.*`, and remove the suppression.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace `app.vault.getFiles().find(f => f.path === id)` lookups in the
Project Context Manage modal with `getAbstractFileByPath` + `instanceof
TFile` narrowing — direct hash lookup vs full-vault scan.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- CONTRIBUTING.md: new "Commit Signing" section with the 4-step SSH
signing setup so future protection rules requiring signed commits
can be turned on with team-ready instructions in place.
- README.md: remove the abbreviated ToC, promote the comprehensive
one to the top, drop the self-referencing entry.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(vault): respect user trash preference via FileManager.trashFile (W7/9)
Eighth of nine workspaces splitting #2397. Intentional behavior change:
file deletions now honor the user's Settings -> Files and links ->
Deleted files preference (system trash / .trash / permanent), instead
of unconditionally calling vault.delete(file, true).
- src/utils/vaultAdapterUtils.ts: add trashFile(app, file) helper that
casts past the missing app.fileManager.trashFile typedef.
createSyntheticTFile now uses Object.create(TFile.prototype) so
instanceof TFile returns true.
- src/projects/ProjectFileManager.ts: constructor takes App instead
of Vault; all vault.delete(file, true) -> trashFile(app, file).
Project create rollback now trashes partial files (was permanent-
delete before).
- src/projects/projectRegister.ts: constructor takes App.
- src/projects/projectMigration.ts: rollbackCreatedFile(app, ...),
ensureProjectsMigratedIfNeeded(app), all delete sites use trashFile.
- src/main.ts: deleteChatHistory uses trashFile; ProjectRegister
constructed with this.app.
- src/system-prompts/systemPromptManager.ts: prompt delete via trashFile.
- src/commands/customCommandManager.ts: command delete via trashFile.
- src/LLMProviders/projectManager.ts, src/components/Chat.tsx,
src/components/chat-components/ProjectList.tsx: pass app (not vault)
to ProjectFileManager.getInstance.
- __mocks__/obsidian.js: app.fileManager.trashFile jest mock.
- Tests updated to expect trashFile + App constructor arg.
W0, W1, and #2402 already merged. Behavior change requires manual
verification (test plan section 1 + 6).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* chore(manifest): bump minAppVersion to 1.4.0
FileManager.trashFile (used by the vault deletion helper introduced in
W7/9) requires Obsidian 1.4+. Bumping minAppVersion ensures the plugin
store only offers this and future releases to compatible app versions;
users on older Obsidian builds stay on their current Copilot version
instead of receiving an update that would crash on delete.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Seventh of nine workspaces splitting #2397. Picks up provider-file
changes W1 deferred — primarily LangChain public-API renames that
aren't pure type changes.
- src/LLMProviders/BedrockChatModel.ts: _getType() -> getType()
(non-deprecated method, same behavior)
- src/LLMProviders/BedrockChatModel.test.ts: matching mock-message
rename across all streaming-decode tests
- src/LLMProviders/chainRunner/CopilotPlusChainRunner.ts: stringify
non-string response.content via JSON.stringify instead of String()
(avoids [object Object] when content is structured)
No semantic message-handling changes. All behavior preserved.
W0, W1, and #2402 already merged.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* chore(styles): replace element.style with Tailwind + CSS vars (W5/9)
Sixth of nine workspaces splitting #2397. Replaces direct DOM
inline-style writes with Tailwind utility classes (addClass) and
CSS custom properties for dynamic values.
- src/components/modals/SourcesModal.tsx — addClass + tw-* classes
- src/components/modals/AddImageModal.tsx — input.style.display -> tw-hidden
- src/components/modals/CachePreviewModal.ts — modalEl.addClass tw-w/tw-max-w
- src/components/modals/project/AddProjectModal.tsx — !tw-max-h-[85vh]
- src/components/modals/project/context-manage-modal.tsx — tw-min-w-[50vw]
- src/components/quick-ask/QuickAskOverlay.tsx — body cursor/select via tw + CSS var
- src/components/ui/textarea.tsx — --copilot-autosize-height + tw-h-[var(...)]
- src/components/chat-components/ChatViewLayout.ts — removeProperty fix
- src/components/command-ui/draggable-modal.tsx — tw-left/top-[var(--copilot-drag-x/y,0px)]
- src/settings/SettingsPage.tsx — tw-select-text
- src/settings/v2/components/ModelEditDialog.tsx — tw-h-4/5
- src/system-prompts/SystemPromptAddModal.tsx — tw-h-4/5
- src/hooks/use-draggable.ts and use-resizable.ts: write
--copilot-drag-x/y and --copilot-resize-cursor CSS variables instead
of inline left/top/cursor; consumers reference them via Tailwind
arbitrary-value classes
Behavior: visual-only changes; no logic changes. Reviewer should
walk through SCORECARD_WARNINGS_TEST_PLAN.md §3 (drag/resize, QuickAsk
positioning, draggable modal, typeahead positioning, inline pills).
W0 already merged.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* chore(styles): use Obsidian setCssProps API for CSS variable writes
Follow-up to the W5 commit. The Obsidian scorecard warning is
"Use the setCssProps function if the CSS properties need to change
dynamically", and the prior pass still wrote them via
element.style.setProperty / removeProperty. Routes the remaining 14
sites across 6 files through HTMLElement#setCssProps, which is the
Obsidian-sanctioned API for writing CSS custom properties.
- src/hooks/use-draggable.ts: --copilot-drag-x/y batched into one call
- src/hooks/use-resizable.ts: --copilot-resize-cursor set/clear
- src/components/quick-ask/QuickAskOverlay.tsx: same cursor pattern
- src/components/chat-components/ChatViewLayout.ts: --copilot-status-bar-clearance
- src/components/ui/textarea.tsx: --copilot-autosize-height
- src/utils/dom/dynamicStyleManager.ts: batches every set+clear into a single
setCssProps call per invocation (CSSOM defines setProperty(name, "") to
call removeProperty(name), so empty-string entries clear)
The single getPropertyValue read at src/components/CopilotView.tsx:112 is
a read, not a write, and is not flagged by the scorecard rule.
Verification: grep -rn "\.style\.setProperty\|\.style\.removeProperty" src
returns zero hits. npm run lint, npm run build, npm test (1962 tests) all
pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix(popout): document ownership and selection listener fixes (W4/9)
Fifth of nine workspaces splitting #2397. Real popout-window
correctness fixes — semantic choices about which window owns a DOM
operation, plus selection-listener captured-document fixes.
- document → activeDocument where a generic active-window lookup is
correct (SourcesModal, ChatViewLayout, ItemView/Notice mocks,
TabContext, ChatInput keydown listener, CopilotView keyboard
observer, TypeaheadMenuPortal, AddImageModal, and ?? document
fallbacks across draggable-modal, menu-command-modal,
CustomCommandChatModal, use-draggable, use-resizable, and
QuickAskOverlay).
- document → root.ownerDocument in ChatSingleMessage's
linkInlineCitations so DOM nodes end up in the citation's window;
same pattern in the processMessage streaming block via
contentRef.current.ownerDocument.
- New getEditorDocument(editor) helper in BasePillNode. createDOM and
exportDOM now accept the LexicalEditor; pills create DOM in the
editor's window (correct popout behavior). All 6 pill subclasses
updated to match the new signature.
- main.ts: selectionListenerDocument captured at listener registration;
remove uses the same document (fixes leak when activeDocument has
drifted before unload).
- chatSelectionHighlightController.ts: getActiveViewOfType(MarkdownView)
replaces the brittle activeLeaf type comparison.
- Test setup: (window as any).activeDocument = window.document in
ChatSingleMessage.test; window.document.createElement in
AgentPrompt.test Notice mock.
Behavior fix: chat in a popout window now creates DOM nodes in the
popout's document, not the focused window's document.
W0, W1, and #2402 already merged.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(popout): prefer element.doc/.win over ownerDocument and activeDocument
Switch popout-window-sensitive call sites to Obsidian's `.doc` / `.win`
augmentations so DOM ops, listeners, portals, and positioning follow the
element's actual owner window. Recreate Lexical root on view migration to
rebind input handling after a leaf moves between windows. Document the
decision order in AGENTS.md and polyfill `Node.doc` / `Node.win` in
jest.setup.js so tests still run under jsdom.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* improve image upload implementation
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Third of nine workspaces splitting #2397. Surfaces previously
swallowed promise rejections via .catch(logError) or `void` prefix.
No logic changes - only error-handling explicitness.
- ~67 floating promises now use void / .catch(logError)
- Promise-returning callbacks where void was expected are wrapped
- ConfirmModal accepts onConfirm/onCancel returning void | Promise<void>;
rejections logged via logError
- Custom command runners wrap `result instanceof Promise` paths
- ChatUIState.replaceMessages is now async to match its delegate
- ChainManager initialize / createChainWithNewModel are explicitly
voided with .catch(logError) at call sites
- dbOperations subscribeToSettingsChange uses void IIFE with try/catch
Behavior change: errors previously hidden by floating promises now
log via @/logger. No code paths changed.
W0, W1, and #2402 already merged.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace ChatCohere / ChatMistralAI / CohereEmbeddings with ChatOpenAI /
OpenAIEmbeddings pointed at each provider's OpenAI-compatible endpoint:
- Cohere: https://api.cohere.ai/compatibility/v1
- Mistral: https://api.mistral.ai/v1 (already OpenAI-shaped)
Dropping @langchain/cohere, @langchain/mistralai, cohere-ai, and
@mistralai/mistralai also eliminates the @aws-sdk/* and @smithy/* chain
that cohere-ai dragged in for Bedrock auth (a path this plugin doesn't use).
Production bundle: 5.21MB -> 3.39MB.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
First of nine workspaces splitting #2397 into focused PRs (see
designdocs/todo/SCORECARD_WARNINGS.md, landing in a later workspace).
This PR is deps-only so the source-touching workspaces (types,
promise safety, popout compat, styles, trash semantics, brevilabs
rewrite, eslint rules) can each land independently.
Dependencies:
- @codemirror/state, @codemirror/view: peer deps for newer @lexical/* usage
- @langchain/classic: required by langchain ^1 split layout
- openai: direct consumer in upcoming provider work
- uuid + @types/uuid: replaces ad-hoc id generation
- zod: schema validation used in upcoming type tightening
DevDependencies:
- eslint-plugin-obsidianmd: installed now but not wired into .eslintrc
until the final workspace (W9) once all flagged sites are cleaned up
No source changes; no behavior changes. npm run lint / build / test
all pass on this branch.
OpenAI:
- gpt-5.4 -> gpt-5.5 (new flagship, released 2026-04-24)
- gpt-5-mini -> gpt-5.4-mini
- gpt-5-nano -> gpt-5.4-nano (enum only)
Anthropic:
- claude-opus-4-6 -> claude-opus-4-7 (Opus 4.6 now legacy)
- Add claude-haiku-4-5 (disabled by default)
Google:
- gemini-3.1-flash-lite-preview -> gemini-3.1-flash-lite (GA 2026-05-07)
xAI:
- grok-4-1-fast -> grok-4.3 (grok-4-1-fast retiring 2026-05-15)
OpenRouter mirrors updated for all of the above.
ModelParametersEditor: gate Extra-High reasoning effort on
startsWith("gpt-5.5") instead of "gpt-5.4" so the option follows
the new flagship and does not accidentally surface on gpt-5.4-mini
or gpt-5.4-nano.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The modal and its REMOVE_FILES_FROM_COPILOT_INDEX command were orphaned in
the vault search v3 migration (#1703): the addCommand registration was
removed but the modal file and its constant entries were left behind.
Nothing in the codebase has instantiated it since. Deleting it also clears
the risk-report finding about contentEl.createEl("style", ...), which
Obsidian disallows at runtime.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
findRelevantNotes and MiyoIndexBackend.getDocumentsByPath were sending the
local absolute filesystem path as Miyo's file_path. On remote-vault setups
the indexed path is the server's absolute path, so the scroll filter
matched zero points and the related-notes endpoint always returned 404.
Replace getMiyoAbsolutePath with getMiyoFilePath, which returns
"<FolderName>/<vaultRelativePath>" — the inverse of
getVaultRelativeMiyoPath. Miyo's resolveFileInput remaps this format to
the registered folder's canonical absolute path, so it works identically
for local and remote setups.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: use /responses for GitHub Copilot codex models
Route Copilot codex models away from /chat/completions to avoid 400
errors, and keep ping/model verification aligned with runtime routing.
* refactor: deduplicate shouldUseGitHubCopilotResponsesApi calls and restore rationale comments
Cache the result in useCopilotResponses in both createModelInstance() and
tryPing() instead of calling the helper multiple times per flow.
Restore Reason comments removed in c96a9d8 that document non-obvious
constraints: why ChatOpenAICompletions is used over ChatOpenAI, the
delta.role fallback to avoid broken tool_call_chunks, defaultRole?: any
type choice, streamUsage/baseURL SDK behavior, and the typeof Request
guard for Obsidian mobile runtimes.
When thinking content ends with a 4-space-indented line (e.g. Gemma's
bullet-point reasoning), markdown's indented code block rule would consume
the immediately-following </div> closing tag and render it as literal
"</div>" text. Fix by always trimming section content and appending
\n before the closing tag so it lands on its own unindented line.
Also adds integration tests covering non-streaming, streaming-complete,
and streaming-unclosed think block paths.
Co-authored-by: H <trulyshelton@gmail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
The previous implementation used compositionstart/compositionend events
with a 100ms timeout to track IME state. This caused Korean (Hangul)
IME users to experience a noticeable delay when pressing Enter to send:
the composition-ending Enter was blocked, and subsequent Enters within
100ms were also blocked, requiring users to hold the key or wait.
In Chromium/Electron, the keydown event for IME-confirming Enter fires
with isComposing=true (before compositionend), so the explicit ref-based
tracking is redundant. Relying directly on event.isComposing and the
legacy keyCode 229 is sufficient and matches the simpler approach already
used in follow-up-input.tsx.
Co-authored-by: octo-patch <octo-patch@github.com>
* feat: migrate project storage from data.json to vault files
Migrate project definitions and metadata from data.json to individual
markdown files in the vault's projects directory.
- Project file manager with YAML frontmatter parsing and persistence
- Migration logic from legacy data.json format with unsupported/ backup
- Project UI components (AddProjectModal, ProjectList, ProgressCard)
- Chat history integration for the new storage model
- Status sorting, cache-first ordering, and URL removal in processing
- Atomic cache operations to prevent transient disappearance on rename
- Awaited context cache cleanup on all delete/rename/folder-switch paths
- Raw-content-first frontmatter parsing to avoid stale metadataCache
- BOM/CRLF support in frontmatter read and write helpers
- Accessibility: aria-labels on all icon-only buttons
* fix: address codex review feedback
- Defer vault listener registration from constructor to initialize()
to avoid premature cache mutations and frontmatter writes during
initial vault load (before onLayoutReady).
- Coerce numeric YAML project IDs to strings so bare numeric values
like `copilot-project-id: 123` are not silently dropped.
Miyo returns indexed file paths prefixed with the owning folder name
(the vault name), but getVaultRelativeMiyoPath was stripping the
absolute filesystem path prefix instead. As a result, the "List
Indexed Files" command rendered every entry as a broken Obsidian link
(e.g. [[MyVault/notes/foo.md]] instead of [[notes/foo.md]]).
Switch the strip logic to match what Miyo actually returns. Files from
other vaults pass through unchanged so cross-vault search results stay
intact. Always return a forward-slash-normalized path so callers see a
consistent separator. Drop the now-unused getVaultBasePath helper.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Other OpenAI-compatible providers (LM Studio, OpenAI-format, SiliconFlow,
etc.) already pass a fetch override so requests can go through Obsidian's
requestUrl API. The Ollama branch was missing this wiring, so toggling
"Enable CORS" on an Ollama model had no effect and mobile (WKWebView)
requests to http:// Ollama hosts failed with "Load failed" due to CORS /
mixed-content blocking.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(miyo): add toggle to search all indexed content across folders
Allow users to opt into searching everything in Miyo by omitting the
folder_name from search requests. This is enabled by default so Copilot
searches all indexed content. When disabled, searches are scoped to the
current vault folder only. Only affects the search endpoint — other Miyo
APIs (parseDoc, listFolderFiles, etc.) still send folder_name.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(miyo): default miyoSearchAll to false
Scope searches to the current vault folder by default, which is the
safer default. Users can opt into cross-folder search via the toggle.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Miyo server now supports folder_name, which better reflects the
semantic that we send a vault name rather than a filesystem path.
Also removes folder_path from response interfaces as the server
no longer returns it.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Miyo folder identifier is actually the vault name, not a filesystem
path. Rename getMiyoFolderPath -> getMiyoFolderName and all folderPath
params -> folderName across MiyoClient and callers to reflect this.
For parse-doc, send vault-relative file.path instead of absolute path,
and include folder_path in the request body for consistency with all
other Miyo endpoints. Wire-format keys (folder_path) are unchanged.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When using a Miyo remote server, the vault may be mounted at a
different path on the remote machine. This adds an optional
"Remote Vault Folder" setting that overrides the folder path sent
to Miyo when a custom server URL is configured.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(miyo): disable miyo on mobile without a remote server URL
Adds `shouldUseMiyo()` to `miyoUtils.ts` as the single source of truth
for whether Miyo should be active. On mobile, Miyo is silently ignored
unless an explicit remote server URL is configured, since local service
discovery is unavailable on mobile. The three previously duplicated
`shouldUseMiyo` checks in RetrieverFactory, findRelevantNotes, and
VectorStoreManager now delegate to this shared function.
Also drops the redundant `enableSemanticSearchV3` guard — the UI already
enforces that enabling Miyo enables semantic search and disabling semantic
search disables Miyo.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(miyo): align RelevantNotes UI check with shouldUseMiyo
Replaces the local `shouldUseMiyoIndex` predicate (which duplicated the
self-host grace-period logic and missed the mobile platform guard) with a
direct call to `shouldUseMiyo` from miyoUtils. This ensures the UI's
index-state check matches what the backend actually uses, so the
"Build Index"/"No relevant notes" state is always correct on mobile.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(miyo): update findRelevantNotes mock to use shouldUseMiyo
Replace the isSelfHostAccessValid mock with shouldUseMiyo, which is now
the single gate checked by findRelevantNotes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(miyo): rename vault name to remote vault path and align MiyoClient with server API
- Rename `miyoVaultName` setting to `miyoRemoteVaultPath` with migration from old key
- Rename UI field "Vault Name" → "Remote Vault Path (Optional)" and "Custom Miyo Server URL" → "Remote Miyo Server URL (Optional)"
- Always show Remote Vault Path field (not gated by server URL); both fields default to blank with no placeholder
- Show bot name indicator (remote vault path + local/remote label) under Enable Miyo when enabled
- Rename `sourceId` parameters in MiyoClient public methods to `vault` to match the actual server API field name
- Update `searchRelated` options property from `sourceId` to `vault`
- Update call sites in findRelevantNotes.ts accordingly
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(miyo): remote vault path saves silently without triggering re-index
It is a remote-only identifier — changing it has no effect on the
local index, so no confirmation modal or re-indexing is needed.
Also drops the now-unused logWarn import.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(miyo): remote vault path only overrides when remote server URL is also set
getMiyoSourceId now requires both miyoRemoteVaultPath and miyoServerUrl to be
set before using the remote path — if only the path is set (no server URL),
it falls back to the auto-detected local vault path. The settings indicator
applies the same logic.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(miyo): rename getMiyoSourceId → getMiyoVault, resolveMiyoSourceId → resolveMiyoVault
Removes the last "sourceId" terminology — all Miyo identifiers now consistently
use the "vault" name that the server API expects.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(miyo): only show remote vault path indicator when remote server URL is set
Avoids showing the auto-detected local vault path under a "Remote vault path"
label, which was misleading when Miyo was running locally.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(miyo): replace custom remote vault path input with plain text field
Removes the Apply button, pending state, useEffect, and handler — the field
now saves with the standard debounced onChange, matching the URL field above it.
Also drops the unused Input and useEffect imports.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(miyo): show effective vault path under Enable Miyo when enabled
Displays the auto-detected local vault path when no remote server is
configured, or the remote vault path override when both remote URL and
remote vault path are set.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(test): update findRelevantNotes test to use vault instead of sourceId
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
The release agent now fetches and reads every PR's description before
writing release notes, instead of relying on titles alone. This
produces more accurate, user-facing release notes.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(openrouter): add per-model toggle for prompt caching
OpenRouter's Zero Data Retention (ZDR) endpoints do not support
Anthropic's automatic prompt caching (cache_control), causing 404
errors. This adds an `enablePromptCaching` option (default: true)
so users can disable it per model in settings.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(openrouter): show question mark tooltip for prompt caching toggle
Display a visible HelpCircle icon next to the label instead of making
the label itself the hover target. Tooltip text: "Disable if your
OpenRouter endpoint uses Zero Data Retention (ZDR)..."
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: move prompt caching toggle to top-level model settings
The toggle was nested inside the advanced parameters section. Move it
to the top level alongside Base URL and API Key so it's immediately
visible to OpenRouter users.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): simplify prompt caching to single checkbox with tooltip
Remove the redundant FormField heading — now just a checkbox labeled
"Prompt Caching" with a ? tooltip explaining ZDR.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: prompt caching applies to multiple providers, not just Anthropic
OpenRouter's cache_control supports OpenAI, Anthropic, DeepSeek,
Gemini, Grok, and others. Remove "Anthropic" from tooltip and docs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ui): show enabled models without API keys as disabled in dropdown
Previously, enabled models without API keys were hidden from the chat
input model dropdown, making it confusing since "Enable" in settings
didn't mean the model would appear. Now they show as greyed-out disabled
items with a "Needs API key" label.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: reorder BUILTIN_CHAT_MODELS so enabled models are listed first
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>