Commit graph

218 commits

Author SHA1 Message Date
Zero Liu
8658282aa9
chore(lint): enable 4 type-aware quick-win rules and fix violations (#2424)
Turns on no-unsafe-enum-comparison, no-base-to-string,
no-redundant-type-constituents, and restrict-template-expressions.
Fixes all 24 violations across 13 source files.
2026-05-13 21:57:33 -07:00
Zero Liu
f901343583
chore(eslint): enable @typescript-eslint/no-floating-promises and fix violations (#2437)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 21:45:35 -07:00
Zero Liu
df6f455662
chore(eslint): enable @typescript-eslint/no-unsafe-argument and fix violations (#2440)
Removes the `off` override and fixes all 368 violations via type casts,
widened parameter types (unknown / string|null|undefined for functions
that defensively handle non-string inputs), and concrete types in place
of `any` for Lexical nodes, mock objects, and option handlers.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 21:40:26 -07:00
Zero Liu
8499b85a1b
chore(eslint): enable no-unnecessary-type-assertion and no-misused-promises (#2441)
* chore(eslint): enable no-unnecessary-type-assertion and no-misused-promises

Auto-fixed all 40 no-unnecessary-type-assertion violations via `eslint --fix`.

For no-misused-promises, configured `checksVoidReturn` with two relaxations
(documented inline):
- `attributes: false` — `onClick={async () => ...}` is the standard React pattern
- `inheritedMethods: false` — Obsidian's Plugin.onload/onunload are async

Fixed the remaining 10 violations by hand:
- Wrapped async subscribers/listeners with `void` (projectManager, useChatFileDrop,
  main.ts, webViewerServiceSelection)
- Widened ConfirmModal subclass callback types to `() => void | Promise<void>`
- Widened MobileCardDropdownAction.onClick to match parent prop types
- Made SettingsMainV2 handleReset sync (it never awaited anything)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(eslint): move no-misused-promises to TS-only config block

The @typescript-eslint plugin is registered only for .ts/.tsx files via
obsidianmd's recommendedTypeChecked. Enabling the rule in the
JS+TS-shared block caused CI to fail with "could not find plugin
@typescript-eslint" because the plugin isn't loaded for .js/.mjs files.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 21:27:35 -07:00
Zero Liu
08c14440dc
chore(lint): enable obsidianmd/rule-custom-message and fix violations (#2416)
* 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>
2026-05-13 01:39:35 -07:00
Zero Liu
17739cafa0
Enable basic obsidian eslint rules (#2410) 2026-05-13 00:49:49 -07:00
Zero Liu
858ce4c862
chore(styles): replace element.style with Tailwind + CSS vars (W5/9) (#2400)
* 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>
2026-05-12 22:50:05 -07:00
Zero Liu
6331631dd0
chore(popout): window-global API swaps — timers, globalThis, base64 (W3/9) (#2401)
Fourth of nine workspaces splitting #2397. Mechanical swaps to
window-namespaced globals for Obsidian popout window compatibility.
No semantic behavior change beyond "works correctly in a popout."

- setTimeout / clearTimeout / setInterval / clearInterval /
  requestAnimationFrame → window.* across all callsites
- globalThis → window (BedrockChatModel fetch.bind, scattered sites)
- atob / btoa → Buffer.from(..., 'base64') / Buffer.from(...).toString('base64')
  - src/encryptionService.ts (drops Node 'buffer' import; Buffer is global)
  - src/utils.ts safeFetch arrayBuffer() branch
  - src/LLMProviders/BedrockChatModel.ts decodeBase64ToUint8Array

Builds on top of #2398 (W0 — deps bump).

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 22:24:17 -07:00
Zero Liu
7619fb5578
chore(promises): explicit handling of floating promises (W2/9) (#2407)
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>
2026-05-12 17:42:22 -07:00
Zero Liu
e470b5c70c
chore(types): tighten types and replace TFile/TFolder casts (W1/9) (#2403) 2026-05-12 16:18:22 -07:00
Emt-lin
506de5477b
feat: migrate project storage from data.json to vault files (#2324)
* 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.
2026-05-09 12:21:04 -07:00
Wenzheng Jiang
2e3d6ba0d2
feat(miyo): add toggle to search all indexed content (#2353)
* 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>
2026-04-10 16:15:57 +09:00
Wenzheng Jiang
c350f37d98
refactor(miyo): rename folderPath to folderName and send relative path in parse-doc (#2348)
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>
2026-04-08 11:05:24 +09:00
Wenzheng Jiang
ccf687b5cb
Use vault name for Miyo requests (#2342) 2026-04-05 22:21:20 +09:00
Wenzheng Jiang
81fd4e5157
feat(miyo): add remote vault folder path override for remote servers (#2334)
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>
2026-03-30 09:17:17 +09:00
Wenzheng Jiang
47e61259d2
Refactor Miyo integration for folder API (#2331)
* refactor Miyo integration for folder API

* chore: remove Miyo related-search log

* docs: add Miyo Node Service API reference

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 09:05:34 +09:00
Wenzheng Jiang
d6283e3151
feat(miyo): rename vault name to remote vault path and align with server API (#2326)
* 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>
2026-03-26 16:24:29 +09:00
Logan Yang
79877e3ef5
feat(openrouter): add per-model toggle for prompt caching (#2318)
* 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>
2026-03-20 16:20:54 -07:00
Wenzheng Jiang
ddb16af59c
feat(miyo): add customizable vault name setting (#2311)
* feat(miyo): add customizable vault name setting for Miyo indexing

Allows users to set a custom vault name that Miyo uses as the source ID
for indexing, instead of the auto-detected vault path. When changed while
Miyo is enabled, clears the old index and triggers a full re-index under
the new name. Includes guidance for remote/multi-device setups to use a
consistent vault name across devices.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(miyo): compare resolved source IDs before clearing index on vault name change

Avoids unnecessary index clear and full re-index when the user types the
auto-detected vault path (the value that the blank field resolves to),
since the effective Miyo source ID is unchanged in that case.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(miyo): prevent applying an empty vault name

The Apply button is hidden when the input is blank, and the handler
guards against an empty string, so users cannot accidentally clear
the vault name and submit it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(miyo): allow clearing vault name to restore auto-detected vault path

An empty vault name is valid — it falls back to the auto-detected vault
path as the Miyo source ID. Removes the empty-string guard so users can
clear a previously set name and revert to the default behaviour.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 10:57:06 +09:00
Wenzheng Jiang
d308423315
Composer V2: Replace replaceInFile with editFile as the primary targeted-edit tool (#2305)
* feat(editFile): write-first with inline diff in chat

Replace the blocking accept/reject modal in editFile with a write-first
approach: changes are written to disk immediately, and a unified diff is
returned inline in the chat as a fenced \`\`\`diff block. Multiple
editFile calls in a single agent turn each produce their own diff
without requiring any user interaction.

Also cleans up associated rename/refactor of writeToFileTool →
writeFileTool across chain runner, streamer, and related tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(editFile): add Show Diff button and side-by-side diff view in ApplyView

- Pipe editFileDiffs through message types (ChatMessage, StoredMessage) and
  MessageRepository so writeFile/editFile diffs are stored as a typed field
  (same pattern as sources)
- Collect diffs in AutonomousAgentChainRunner.runReActLoop() by parsing the
  JSON result of successful writeFile/editFile tool calls, then pass through
  ReActLoopResult → handleResponse() → ChatMessage.editFileDiffs
- ChatSingleMessage reads message.editFileDiffs directly (instead of trying
  to parse TOOL_CALL markers which don't exist in agent mode)
- ChatButtons shows a "Show diff" button when editFileDiffs is present;
  clicking it opens ApplyView for each diff
- ApplyView redesigned: renders change blocks side-by-side (original left
  with red highlights, modified right with green highlights) using the
  SideBySideBlock component, unchanged blocks shown as plain text between
  change blocks — restores the classic diff block presentation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Revert "feat(editFile): add Show Diff button and side-by-side diff view in ApplyView"

This reverts commit 80945493b48d50c26b9a01bc40de5eced2fcfd18.

* feat(editFile): show ApplyView with simple mode (no per-block buttons)

- editFile now shows the Apply View preview and blocks until user accepts/rejects
- Respects autoAcceptEdits setting (bypasses preview when enabled)
- Adds `simple` flag to ApplyViewState to hide per-block accept/reject/revert buttons
- Removes unused createPatch import

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(tools): update writeFile description to "Create or rewrite files in your vault"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: address Codex review — GPT prompt, fuzzy match safety, tool ID migration

- modelAdapter: update GPT prompt to use editFile's oldText/newText params
  instead of the old SEARCH/REPLACE block syntax
- ComposerTools: fix fuzzy match to replace only the matched span in the
  original content; previously the entire file was rebuilt from the
  fuzzy-normalized string, inadvertently applying trimEnd and quote/dash
  normalization to text outside the matched span
- settings/model: migrate persisted autonomousAgentEnabledToolIds from
  writeToFile→writeFile and replaceInFile→editFile on upgrade

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(editFile): add unit tests for fuzzy match via applyEditToContent

Extract pure replacement logic into applyEditToContent() for testability,
then wire editFileTool.func to call it (removes duplicated logic).

Tests cover:
- Exact match: basic replacement, deletion, NOT_FOUND, AMBIGUOUS
- Fuzzy via smart quotes (single, double), en-dash, NBSP
- Fuzzy via trailing whitespace (spaces, tabs)
- Key invariant: fuzzy match only replaces the matched span — smart quotes
  and trailing spaces outside the span are left untouched
- Multiline fuzzy match with mixed artifacts
- CRLF and LF preservation
- BOM preservation

Also fixes mapFuzzyIndexToNormal to extend matchEnd to the full original
line length when the fuzzy match ends at end-of-line, preventing orphaned
trailing whitespace after replacement.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: address Codex review — NFKC position mapping and legacy tag compat

- ComposerTools: fix mapFuzzyIndexToNormal to handle NFKC expansion
  (e.g. Ⅳ → IV) by walking char-by-char within each line instead of
  assuming a 1:1 column mapping; adds two unit tests covering this case
- ChatSingleMessage: normalise legacy <writeToFile> tags to <writeFile>
  before rendering so saved chat history from before the tool rename
  continues to display correctly

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(modelAdapter): update assertions for editFile oldText/newText prompt

Replace stale SEARCH/REPLACE references with the new parameter names.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(editFile): replace sentinel strings with ApplyEditResult discriminated union

- `applyEditToContent` now returns `{ ok: true; content: string } | { ok: false; reason: 'NOT_FOUND' } | { ok: false; reason: 'AMBIGUOUS'; occurrences: number }` instead of raw sentinel strings, eliminating false positives when file content matches 'NOT_FOUND' or starts with 'AMBIGUOUS:'
- Update `editFileTool` to pattern-match on the discriminated union
- Update `cleanMessageForCopy` regexes to also strip legacy `<writeToFile>` tags from saved chat history
- Update all `applyEditToContent` test assertions to use the new structured return type

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(editFile): sanitize file path before vault lookup

Apply `sanitizeFilePath` in `editFileTool` to match the same treatment
`writeFileTool` already does, preventing "File not found" failures when
the path has a basename that exceeds 255 bytes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(editFile): guard against degenerate fuzzy match inside NFKC expansion

When `oldText` fuzzy-matches a span whose boundaries both map to the same
position in the original (e.g. searching "I" inside fuzzy "IV" derived from
"Ⅳ"), `matchStart >= matchEnd` and the replacement would be zero-width or
corrupt. Return NOT_FOUND instead of silently producing a wrong edit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(builtinTools): remove invalid daily:append/daily:prepend from prompt

The obsidianDailyNote tool schema only accepts 'daily', 'daily:read', and
'daily:path'. The prompt was incorrectly advertising non-existent
daily:append and daily:prepend commands, which would cause LLM-generated
tool calls to fail schema validation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(editFile): add Stage 3 to tolerate trailing newline mismatch at EOF

LLMs frequently append \n to the last line of oldText even when the file
has no final newline. Neither exact nor fuzzy match would find this, causing
avoidable NOT_FOUND failures. Stage 3 retries with one trailing \n stripped
from oldText (and mirrors the trim on newText to preserve the file's
no-trailing-newline format).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(tests): update editFile integration test to oldText/newText shape

The argumentValidator was still asserting the legacy args.diff SEARCH/REPLACE
payload. Updated to check args.oldText and args.newText to match the new
editFile tool contract.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(editFile): count overlapping matches to prevent wrong-location edits

countOccurrences was advancing by searchText.length, so overlapping patterns
like "aba" in "ababa" were counted as 1 (non-overlapping) instead of 2. This
caused an ambiguous match to be silently applied at the first position rather
than returning AMBIGUOUS. Advance by 1 to catch all overlapping occurrences.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(editFile): round-trip check guards partial NFKC-expansion fuzzy matches

The existing matchStart >= matchEnd guard only caught degenerate zero-width
spans (e.g. "I" in "Ⅳ"). A partial match like "V" against "Ⅳ" produces a
valid non-zero span [0,1) but would replace the entire source character even
though "V" doesn't exist standalone in the file.

Fix: after mapping indices back to the original, verify that
normalizeForFuzzyMatch(original[matchStart:matchEnd]) === workingSearch.
If not, the match straddled an NFKC expansion boundary → return NOT_FOUND.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(editFile): return structured failed result on edit errors

Replace plain string returns with `{ result: "failed", message }` objects
for all failure paths in editFileTool (file not found, text not found,
ambiguous match, and exceptions), matching writeFileTool's pattern so
the LLM and result formatters can reliably detect failures via the
`result` field. No-op case returns `result: "accepted"` since the edit
technically succeeded.

Also fix a stale tool name reference in builtinTools prompt instructions
(writeToFile → writeFile).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 18:51:39 +09:00
Logan Yang
e3d47adfa2
feat(lm-studio): use Responses API for LM Studio models (#2306)
Switch LM Studio from /v1/chat/completions to /v1/responses via a thin
ChatLMStudio wrapper that patches LangChain compatibility issues
(text.format requirement, strict:null in tool definitions).

- New ChatLMStudio class with fetch wrapper for tool sanitization
- Opt-out toggle in model settings (useResponsesApi)
- Ping uses ChatLMStudio to test the correct endpoint
- ThinkBlockStreamer: strip special tokens from text content

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 19:37:38 -07:00
Logan Yang
1f2539712d
feat(openrouter): enable prompt caching via cache_control (#2279)
* feat(openrouter): enable prompt caching via cache_control field

Add `cache_control: { type: "ephemeral" }` to all OpenRouter invocation
params so Anthropic models automatically detect cache breakpoints and
reduce token costs. Other providers (OpenAI, DeepSeek, Gemini, Grok,
Groq) either auto-cache without this field or safely ignore it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(openrouter): gate cache_control to OpenRouter endpoints only

ChatOpenRouter is shared by LM_STUDIO and COPILOT_PLUS providers which
use their own OpenAI-compatible base URLs. Sending cache_control to
strict OpenAI-format backends causes 4xx request failures. Detect
OpenRouter by checking the baseURL and only inject cache_control when
talking to openrouter.ai.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: update Claude Sonnet builtin model to claude-sonnet-4-6

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: update builtin models - add gemini-3.1-flash-lite-preview, reduce enabled defaults

- Update Claude Sonnet to claude-sonnet-4-6
- Add gemini-3.1-flash-lite-preview (enabled, projectEnabled)
- Disable most builtin models by default to reduce clutter
- Keep enabled: Copilot Plus Flash, OpenRouter Gemini 2.5 Flash,
  GPT-5.2, GPT-5 mini, Claude Sonnet 4.6, Gemini 2.5 Flash,
  Gemini 3.1 Flash Lite Preview
- Update Bedrock example placeholder to claude-sonnet-4-6

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 17:18:01 -07:00
Logan Yang
25e3faa088
feat: unify Azure OpenAI and Azure Foundry into single Azure provider (#2291)
* feat: unify Azure OpenAI and Azure Foundry into single Azure provider

Combines the two Azure providers into one. Users can now provide the
full endpoint URL as the base URL and skip the legacy instance name,
deployment name, and API version fields. The URL is automatically
normalized (strips /chat/completions, extracts api-version from query
params). Legacy field-based configuration still works for backward
compatibility.

Changes:
- Rename Azure OpenAI label to "Azure" with updated example host
- Remove azure-openai builtin model (users add models manually)
- Add normalizeAzureUrl() to handle full URLs pasted by users
- Make legacy Azure fields optional in ModelAddDialog when base URL set
- Update CURL generation to use base URL directly when provided
- Disable Responses API for Azure provider (not supported by Azure)
- Default api-version to 2024-05-01-preview when not specified

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address Codex review - Azure embedding validation and curl model field

- Require legacy Azure fields (instanceName, deploymentName, apiVersion) for
  embedding models even when baseUrl is provided, since EmbeddingManager reads
  those fields directly and never consumes baseUrl
- Include model name in Azure base-URL curl payloads for both chat and embedding
  requests so the copied curl command works with Azure Foundry /models endpoints

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: show Azure legacy fields in UI for embedding models even when baseUrl is set

The renderProviderSpecificFields early-return was hiding instanceName,
embeddingDeploymentName, and apiVersion for all Azure models when baseUrl
was present. But validateFields() requires those fields for embedding models
regardless of baseUrl. Now only chat models skip the legacy UI fields when
baseUrl is provided; embedding models always render them.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: trim baseUrl before validation and skip Responses API verbosity for Azure

- Use model.baseUrl?.trim() in validateFields() and renderProviderSpecificFields()
  so whitespace-only input cannot bypass required-field validation
- Skip text.verbosity in getOpenAISpecialConfig() for Azure models, since this
  PR intentionally excludes Azure from useResponsesApi and the Responses-API
  verbosity param would produce a broken payload for Azure GPT-5 with verbosity set

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 16:41:14 -07:00
Logan Yang
d3ee3f999b
fix: agent loop improvements, clickable citations, and UI fixes (#2287)
* fix: prevent agent ReAct loop from getting stuck with small local models

Small local models (e.g. qwen3.5-4b-mlx) get stuck calling localSearch
repeatedly with near-identical queries and never synthesizing an answer.

- Remove QueryExpander from agent loop to avoid model contention on
  single-connection local models (retriever still expands internally)
- Add query deduplication that pre-filters tool call batches using
  Jaccard word-overlap similarity before execution
- Force synthesis via raw model (without tools) after 2 consecutive
  iterations where all tool calls are duplicates
- Add agent-specific system prompt guidance for search limits
- Strip leaked chat template role tokens from intermediate content
- Add intermediate model output logging for debugging

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: make inline citation numbers clickable and restore Max Sources setting

Inline citation numbers like [1], [2] in chat responses are now clickable
links that open the corresponding source note. Also restores the Max Sources
slider in QA settings, reverting the hardcoded DEFAULT_MAX_SOURCE_CHUNKS.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove excessive 32px bottom padding from chat view on desktop

The padding-bottom on .view-content used max(safe-area + 4px, 32px),
forcing a 32px minimum even on desktop where safe-area-inset-bottom is 0.
Simplified to safe-area + 4px so desktop gets minimal padding while
devices with safe areas still get proper spacing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(agent): track dedup queries post-execution to keep failed searches retryable

Move previousSearchQueries tracking from before to after each localSearch tool
call executes. Previously, queries were marked as seen before any tool ran, so
if a call was aborted or the loop threw early, those queries were still blocked
from retrying on the next iteration. Now each query is added to
previousSearchQueries immediately after its tool call returns, keeping the dedup
effective for completed calls while leaving transient failures retryable.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(citations): preserve Obsidian internal-link attributes on inline citation anchors

When building clickable inline citation links (e.g. [1], [2]), the previous code
only copied the href from the source section anchor. Obsidian vault-note links have
additional attributes such as data-href and class="internal-link" that are required
for in-app note navigation. Without them, clicking an inline citation resolves the
link as a plain relative URL instead of opening the corresponding note.

Fix: store the source anchor element rather than just the href, then copy all
attributes onto the generated citation anchor before setting the citation-specific
class and aria-label overrides.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(agent): only mark localSearch queries as seen after successful execution

Change the dedup tracking condition from always tracking queries post-execution
to only tracking when the tool call succeeds (result.success is true). This
ensures transient failures (e.g. temporary index unavailability) leave the
query retryable: if a localSearch fails, the model can issue the same query
again on the next iteration. Previously, even a failed execution would mark
the query as seen, blocking recovery.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: deduplicate inline citations after source consolidation

When multiple chunks come from the same note, the LLM cites them
separately. After consolidation maps them to the same source number,
adjacent duplicates like [1][1] or [1] and [1] now collapse to [1].

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: update GPT-5.2 to GPT-5.4 in builtin models

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add dynamic status bar clearance for desktop chat view

The chat input was hidden behind Obsidian's status bar on desktop.
Adds ChatViewLayout which measures the geometric overlap between
the view-content and the status bar, setting a CSS variable to
provide the exact padding needed. Adapts to any theme: no clearance
for auto-hide themes (opacity: 0) or themes that already account
for the status bar in their layout; correct clearance for themes
where the bar is visible and overlapping. Re-checks on theme
switches via debounced css-change listener.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: suppress thinking tokens during streaming when reasoning is disabled

When a local model (e.g. Qwen 3.5 via LM Studio) emits text-level
<think> tags but the user has not enabled the reasoning capability,
the thinking content was visibly rendered during streaming and only
stripped once the </think> tag arrived. Now tracks the position of
excluded think blocks across chunks and truncates fullResponse to
the safe prefix on each chunk, so thinking content never appears.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 16:17:37 -07:00
Emt-lin
0ac60eb31e
fix: command UI improvements, LaTeX rendering, and Ollama numCtx config (#2276)
* feat: add Lucide icons to all commands for mobile toolbar display

* fix: restore reactive settings subscription in ApiKeyDialog

* fix: render LaTeX in Quick Ask/Command and fix text selection

- Extract shared preprocessAIResponse() for LaTeX delimiter normalization
  and executable code block escaping (dataview/tasks)
- LaTeX normalization now skips fenced/inline code blocks
- Quick Ask: preprocess content before MarkdownRenderer call
- Quick Command: add markdown preview mode with edit toggle
- ChatSingleMessage: refactor to use shared preprocessAIResponse()
- Fix text selection in Quick Ask overlay (user-select on CM6 DOM)
- Add Copy button to command modal action bar
- Remove SelectedContent preview (editor highlight is sufficient)

* feat: add configurable context window size for Ollama models (#2275)

Replace hardcoded numCtx=131072 with a per-model configurable slider
in the model parameters UI. Default remains 131072 for backward
compatibility. Users can adjust it to reduce VRAM usage on GPUs with
limited memory.

Also replace raw provider strings with ChatModelProviders enum in
ModelParametersEditor for type safety, and add LM_STUDIO enum check
alongside existing "lm_studio" to handle both provider formats.
2026-03-10 23:29:17 -07:00
Logan Yang
d849ade60a
Add Obsidian CLI daily/random read tools and reasoning summaries (#2181)
* Add Obsidian CLI daily/random read tools with fallback + reasoning summaries

* chore: move CLI design doc to designdocs/ and remove old TODO doc

- Move docs/OBSIDIAN_CLI_INTEGRATION_DESIGN.md to designdocs/OBSIDIAN_CLI_INTEGRATION.md
- Remove designdocs/todo/OBSIDIAN_CLI_INTEGRATION.md (replaced by PR's design doc)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: gate CLI tools behind Platform.isDesktopApp so they are invisible on mobile

- Move CLI tool registration from static BUILTIN_TOOLS array to registerCliTools()
- Only call registerCliTools() when Platform.isDesktopApp is true
- On mobile: tools are not registered, not shown in settings/UI/reasoning
- Update design doc with platform policy and CLI approach rationale

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: update CLI design doc with finalized category-based tool roadmap

- Replace generic v0/v1/v2 tiers with category-based tool grouping
- Define tools: obsidianDailyNote, obsidianProperties, obsidianTasks,
  obsidianFiles, obsidianLinks, obsidianTemplates, obsidianBases,
  obsidianBookmarks, obsidianTags
- Document excluded commands (destructive, plugin/theme, sync, dev tools)
- Add design rationale for category-based approach
- Update rollout plan to match new tool structure

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: remove search commands from v1 roadmap

search and search:context are redundant with Copilot's existing
keyword + semantic search. Update obsidianFiles to only include
read, append, prepend, random:read. Add search to excluded list.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: restrict file writes to daily notes, add write operations policy

- Remove append/prepend from obsidianFiles (now read-only: read, random:read)
- Daily note append/prepend: direct execution, no Composer diff needed
- Property set/remove and task toggle: require light confirmation
- Arbitrary file writes excluded — use existing Composer tool instead
- Add Write Operations Policy section to design doc

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: remove redundant CLI tools from roadmap, add tool disambiguation

Remove `read` from obsidianFiles (redundant with readNote) and drop
obsidianTags category (redundant with getTagList). Add Tool
Disambiguation section documenting overlap analysis and prompt
instruction guidelines for CLI tools.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: defer confirmation-required CLI mutations from v1 to v2

Move property:set, property:remove, and task toggle/status to v2 tier.
v1 now contains only read-only tools + direct-execution daily note
writes. This ensures v1 ships without needing confirmation UX.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: add Appendix A with v1 CLI command reference

Document all 12 v1 CLI commands with parameters, output format, and
real examples. Covers obsidianDailyNote, obsidianProperties,
obsidianTasks, obsidianRandomRead, and obsidianLinks tools. Includes
error response reference.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: implement v1 CLI tools (properties, tasks, links, daily note category)

Add 4 new category-based CLI tools for the Obsidian CLI integration:
- obsidianDailyNote: read, append, prepend, path (replaces v0 obsidianDailyRead)
- obsidianProperties: list vault properties, read per-note properties
- obsidianTasks: list and filter tasks across vault
- obsidianLinks: backlinks, outgoing links, orphans, unresolved links

All tools are desktop-only (gated by Platform.isDesktopApp), invisible on
mobile, and labeled as "Obsidian CLI: ... (Experimental)" in settings.

Includes shared error handling module (cliErrors.ts), reasoning summaries,
tool display names, disambiguation prompt instructions, and 21 new tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: prioritize env CLI binaries and align desktop runtime check

- resolveBinaryCandidates: move env var overrides (OBSIDIAN_CLI_BINARY/
  OBSIDIAN_CLI_PATH) before the default "obsidian" binary so configured
  overrides are tried first, preventing an incompatible PATH binary from
  blocking a valid configured override.
- builtinTools: replace Platform.isDesktopApp with isDesktopRuntime()
  (now exported) so CLI tools are registered on both modern isDesktopApp
  and legacy isDesktop runtimes, matching the check used in the executor.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor: single CLI master toggle, remove v0 overlap, extract buildCliParams

- Group all CLI tools under one "Obsidian CLI (Experimental)" section
  with a single master toggle in settings UI
- Remove v0 obsidianDailyRead registration (superseded by v1 obsidianDailyNote)
- Extract buildCliParams helper to reduce repetitive param building
- Use "cli" category for all CLI tool registrations

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: align CLI section title with other tool items

Remove horizontal padding from CLI container box so SettingItem
title aligns flush with Vault Search, Web Search, etc. above it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: preserve raw stdout for daily:read to avoid stripping Markdown whitespace

Only trim stdout for non-read commands (append, prepend, path) where
whitespace is not semantically meaningful. Read commands return note content
where leading/trailing whitespace may be intentional.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: add daily create command and obsidianTemplates tool to v1 CLI

- Add 'daily' command to obsidianDailyNote: creates today's daily note
  from configured template if it doesn't exist
- Add obsidianTemplates category tool with 'templates' (list) and
  'template:read' (read content) commands
- Register obsidianTemplates in builtinTools.ts with cli category
- Update AgentReasoningState and toolExecution with new tool/command cases
- Update obsidianDailyNote prompt instructions to mention 'daily' command
- Move obsidianTemplates from v2 to v1 in design doc; add daily command docs
- Add tests for daily command and full obsidianTemplates tool coverage

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: remove daily command, keep only obsidianTemplates addition

The 'daily' CLI command is a UI action (opens the note in Obsidian) and
returns no content, making it unsuitable as an agent tool. Remove it.

Instead, the agent workflow for template-based daily note creation is:
  obsidianTemplates template:read → daily:read (check existence) →
  daily:prepend (write template content if needed)

Update obsidianDailyNote prompt instructions to describe this pattern.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: update CLI tool prompt instructions for template-based daily note workflow

- obsidianDailyNote: add step-by-step guide for creating daily notes from
  templates (templates list → template:read → daily:prepend); note that
  daily:append/prepend auto-create the note if it doesn't exist
- obsidianTemplates: clarify usage of 'templates' and 'template:read',
  cross-reference the daily note creation workflow

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: preserve raw stdout for read commands in ObsidianCliDailyTools

Same fix as d2c81b5 for ObsidianCliTools: daily:read and random:read
commands return full markdown note content, so trimming stdout can silently
alter formatting-sensitive notes. Both tools now return raw stdout.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 17:40:07 -08:00
Emt-lin
11f3878244
feat: refactor GitHubCopilotChatModel to support tool calling (#2242)
* feat: refactor GitHubCopilotChatModel to support tool calling

Refactored GitHubCopilotChatModel from BaseChatModel to ChatOpenAICompletions
to enable bindTools() for Agent Mode tool calling support.

Key changes:
- Extend ChatOpenAICompletions instead of BaseChatModel for native tool support
- Override _streamResponseChunks to fix Copilot-specific streaming issues:
  1. Missing delta.role when proxying Claude models (defaults to "assistant")
  2. Non-string delta.content arrays from Claude (normalized to string)
- Use public convertCompletionsDeltaToBaseMessageChunk API (not deprecated method)
- Inject Copilot auth via configuration.fetch wrapper with 401/403 retry
- Export COPILOT_API_BASE and add public token management wrappers

* fix: improve type safety and retry logic in GitHubCopilotChatModel

- Replace loose `[key: string]: any` params interface with type derived
  from ChatOpenAICompletions constructor fields for proper type checking
- Limit auth retry to 401 only (not 403) to match GitHubCopilotProvider
  behavior and avoid useless token refresh on permanent permission errors
- Replace 80-line _streamResponseChunks fork with minimal
  _convertCompletionsDeltaToBaseMessageChunk override
- Remove unused imports (BaseChatModelParams, AIMessageChunk, etc.)

* refactor: remove dead code from GitHubCopilotProvider after ChatOpenAICompletions migration

Chat completions are now handled by GitHubCopilotChatModel (extends
ChatOpenAICompletions) with auth injected via configuration.fetch.
The old Provider-level request methods and their supporting types
are no longer referenced anywhere.

Removed:
- sendChatMessage, sendChatMessageStream, executeWithTokenRetry methods
- CopilotChatResponse, CopilotStreamChunk, CopilotRequestOptions interfaces
- CHAT_COMPLETIONS_URL, HTTP_STATUS_MESSAGES constants
- Unused imports: createParser, ParsedEvent, ReconnectInterval, FetchImplementation

* feat: add model policy terms UI, update Copilot headers, and fix review issues

- Update Copilot request headers to latest VSCode versions
- Cache model policy terms and surface activation guidance on 400 errors
- Extend GitHubCopilotModel interface with billing/policy/endpoint fields
- Show verification error with parsed Markdown links in ModelImporter
- Fix unused logInfo import, guard Request instanceof check,
  case-insensitive "not supported" match, clear policy cache on resetAuth
2026-03-05 09:46:53 -08:00
Logan Yang
ee8fd5779e
fix(settings): allow resetting default system prompt to built-in (#2246)
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>
2026-03-02 21:57:50 -08:00
Wenzheng Jiang
f67e835afb
feat: add custom Miyo server URL setting for remote deployments (#2229)
* 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>
2026-03-03 11:29:43 +09:00
Logan Yang
f19ebdf4f9
fix(ui): prevent stream usage row from crowding add model actions (#2234) 2026-03-02 17:01:26 -08:00
Wenzheng Jiang
edbe59901f
fix: skip redundant eligibility check when enabling Miyo (#2228)
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>
2026-03-02 11:10:42 +09:00
Logan Yang
c464b33cae
3.2.3 (#2214)
* 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>
2026-02-23 21:11:04 -08:00
Logan Yang
6fb63401ec
feat: configurable output folder for converted docs + Miyo PDF routing (#2210)
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>
2026-02-23 20:15:54 -08:00
Logan Yang
2fd6412942
fix: update "upcoming desktop app" language to "our desktop app Miyo" (#2207)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 23:31:11 -08:00
Wenzheng Jiang
4439ed9916
fix: address Miyo PR review findings (privacy, dedup, architecture) (#2191)
* 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>
2026-02-22 11:21:53 -08:00
Logan Yang
85192a3005
Rename enableMiyoSearch to enableMiyo and scope Miyo to Plus/agent chains (#2201)
- 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>
2026-02-21 21:27:54 -08:00
Wenzheng Jiang
ea552077d9
Add Miyo document parsing toggle and PDF parse-doc integration (#2199)
* feat: add miyo document parsing toggle for PDF parser

* refactor: consolidate miyo toggles into single enable flag
2026-02-21 20:11:11 -08:00
Wenzheng Jiang
0812ca3e40
Miyo indexing: reset batch size on enable and apply updates after resume (#2198)
* Reset embedding batch size to default when enabling Miyo search

* Apply updated embedding batch size after indexing resume

* Fix indexing progress total after pause/resume
2026-02-20 08:31:18 -08:00
Logan Yang
c7fa37f69c
feat: add Firecrawl and Supadata self-host support for web search and YouTube (#2196)
* 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>
2026-02-19 20:44:39 -08:00
Logan Yang
2da3fa8574
feat: self-host web search (Firecrawl) + YouTube (Supadata) (#2190)
* 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>
2026-02-18 23:23:39 -08:00
Wenzheng Jiang
dc729fdba4
Miyo integration: backend + retriever (#2156)
* feat: integrate miyo search backend

* fix: avoid node builtins in miyo discovery

* chore: omit model name for miyo embeddings

* Revert "chore: omit model name for miyo embeddings"

This reverts commit 9d1622b7d8bd8243b5b2e301e356a42fd0c934de.

* Add Miyo debug logging for search and upsert

* Include vault path hash in Miyo source_id

* Lock Miyo embedding model in settings UI

* Update Miyo collection naming and search payloads

* Implement Miyo garbage collection

* Add Miyo time-range filters

* Remove Miyo embeddings and move Miyo search toggle

* Revert "Remove Miyo embeddings and move Miyo search toggle"

This reverts commit b0b787103561459dbd9128f6a96dad3d2914e6c9.

* refactor miyo indexing integration

* fix: resolve build type import and remove Miyo API requirement doc

* Add Miyo related-notes endpoint support for Relevant Notes

* Refine Miyo search toggle availability and indexing flow

* Update Miyo API fields and streamline relevant-notes Miyo flow
2026-02-18 18:01:18 -08:00
wotan-allfather
b218dbf5b5
feat: add streamUsage config for 3rd party OpenAI-format providers (#2148)
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
2026-02-10 21:30:29 -08:00
Logan Yang
9535bf5a74
Replace indexing Notice with in-chat progress card & fix phantom re-indexing (#2173)
* 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>
2026-02-10 20:08:43 -08:00
Logan Yang
3de16740ec
Show Self-Host Mode section to all users with disabled toggle (#2169)
* Show Self-Host Mode section to all users with disabled toggle for non-lifetime

The Self-Host Mode settings section is now visible to everyone instead of
being hidden behind an eligibility check. The toggle is disabled (greyed out)
for users who are not on a lifetime plan (believer/supporter), making the
feature discoverable while preserving the access restriction.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix self-host toggle not disabled after removing license key

Three issues fixed:
1. useIsSelfHostEligible() checked grace period before license key,
   so removing the key didn't revoke eligibility while in grace period.
   Moved license key check to the top and added auto-revocation of
   enableSelfHostMode when key is absent.
2. isSelfHostModeValid() didn't check license key at all, so backend
   code (RetrieverFactory, isPlusEnabled) continued to grant self-host
   access after key removal. Added license key guard.
3. useIsPlusUser() self-host bypass didn't check license key. Added
   the same guard for consistency.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix self-host toggle not disabled when switching to non-lifetime key

useIsSelfHostEligible() trusted cached grace period / permanent
validation before consulting the API, so swapping from a believer
key to a plus key kept the toggle enabled.

Now always verifies via API when a license key is present. Cached
validation (grace period, permanent count) is only used as an
offline fallback when the API call fails. Also auto-revokes
enableSelfHostMode when the API confirms the key is not eligible.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 11:34:45 -08:00
Logan Yang
cbe75e76a3
Fix search recall with note-diverse top-K and chunk-aware scoring (#2165)
* fix: improve search recall with note-diverse top-K, chunk-aware scoring, and adaptive cutoff module

- Add note-diverse top-K selection so every unique note gets representation
  before any note gets a second chunk slot (fixes "find my ai digests" missing notes)
- Fix GraphBoostCalculator: resolve chunk IDs to note paths for metadata lookups,
  deduplicate to note level in filterCandidates
- Fix FolderBoostCalculator: count unique notes per folder instead of raw chunks
- Bump title field weight 3→5 for stronger filename match priority
- Add AdaptiveCutoff module (standalone, not yet plugged in) for score-based
  adaptive result limiting — designed for future Jina Reranker v3 integration
- Extract shared chunkIdUtils for consistent chunk ID → note path conversion

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Hardcode maxSourceChunks to 30 and remove user setting

Replace settings.maxSourceChunks with DEFAULT_MAX_SOURCE_CHUNKS (30)
across all retriever call sites. Remove the "Max Sources" slider from
QA settings — recall and precision are now handled automatically by
diverse top-K selection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: expose returnAll to LLM for exhaustive "find all" vault queries

Queries like "find all my AI digests" previously capped at 30 results.
Time-range and tag queries already bypassed this via shouldReturnAll,
but the trigger was mechanical. This exposes the returnAll concept to
both Agent chain (via tool schema + instructions) and CopilotPlus chain
(via planning prompt extraction) so the LLM can set it based on user
intent, returning up to 100 results when appropriate.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: replace raw tool names with human-friendly summaries in reasoning block

Add explicit cases for all tools (getFileTree, getTagList, time tools,
updateMemory, indexVault, youtubeTranscription) so the agent reasoning
block never exposes internal tool names. Default fallbacks now show
"Processing" / "Done" instead of "Calling toolName" / "Completed toolName".
Failure messages also use the friendly summary.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 13:31:59 -08:00
Emt-lin
57e95c34e6
fix: GitHub Copilot mobile CORS bypass and auth UX improvements. (#2140)
# Conflicts:
#	src/LLMProviders/githubCopilot/GitHubCopilotProvider.ts
2026-02-04 17:32:56 -08:00
Emt-lin
918969fc18
feat: add PatternListEditor component for include/exclude settings (#2141)
* feat: add PatternListEditor component for include/exclude settings

Add a new PatternListEditor component to manage include/exclude patterns
in QA settings with support for folders, tags, notes, and file extensions.

- Collapsible list UI with smooth expand/collapse animation
- Dropdown menu for adding different pattern types via modals
- Robust URI decoding with try/catch to prevent settings page crash
- Pattern deduplication via getUniquePatterns helper
- Test coverage for malformed URI sequences

# Conflicts:
#	src/styles/tailwind.css

# Conflicts:
#	src/styles/tailwind.css

* chore: translate Chinese comments to English in PatternListEditor
2026-02-04 17:31:55 -08:00
Logan Yang
b128b25558
Agent UIUX Improvements (#2149)
* Use openrouter openai embedding small as default embedding model

* Increase agent max iterations to 16 and add 5-minute loop timeout

- Raise max tool call iterations limit from 8 to 16
- Add AGENT_LOOP_TIMEOUT_MS constant (5 minutes) to prevent runaway loops
- Update slider max in settings UI to match new limit
- Show appropriate message when loop exits due to timeout vs max iterations

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Update agent reasoning spinner to sigma shape with smooth animation

- Change from 9-dot diamond to 7-dot sigma (Σ) pattern
- Animation traces sigma shape: top-right → top-left → center → bottom
- Use ease-in-out timing and gradual opacity transitions for smooth motion
- Adjust size to 13.5px for better alignment with text

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Improve agent reasoning step summaries and fix composer tool UX

- Add meaningful summaries for writeToFile/replaceInFile tools
  (e.g., "Writing to filename" instead of "Calling writeToFile")
- Show "Edit rejected" when user rejects changes in diff view
- Fix "Changes applied successfully" notice showing on reject
- Extract model's intermediate reasoning as finding summaries
- Add TODO doc for composer tool redesign to address 30s feedback gap

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add self-host mode eligibility gating and permanent validation

- Increase tool timeout from 60s to 120s
- Show self-host mode section only to Believer/Supporter plan users
- Add permanent validation after 3 consecutive successful checks
- Add useIsSelfHostEligible hook for offline-friendly eligibility check
- Preserve permanent validation status when re-enabling self-host mode

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix self-host validation to require 14-day intervals between count increments

- Only increment validation count when 14+ days have passed since last check
- Document need for structured ComposerTools results in TODO doc
- Add TODO for no-op case handling in AgentReasoningState

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix self-host validation timer and offline re-enable issues

- Don't reset timestamp when < 14 days (preserves interval countdown)
- Allow permanently validated users to see section even if toggle is off
- Supports offline re-enable for users with 3+ successful validations

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Wire refreshSelfHostModeValidation into plugin startup

Enables the 14-day periodic validation to actually increment the count
toward permanent self-host mode (3 successful checks).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Improve self-host mode validation with offline support and documentation

- Add grace period check in validateSelfHostMode for offline re-enable
- Change grace period from 14 to 15 days
- Add comprehensive documentation explaining validation flow
- Fix offline re-enable for users within grace period

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 17:10:28 -08:00
Logan Yang
308c88332d
Address quick ask (#2146)
* chore: translate Chinese comments to English and apply formatting

- Translate all Chinese comments in replaceGuard.ts to English for consistency
- Apply Prettier formatting to 33 files

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test: add unit tests for Quick Ask core modules

Add comprehensive tests for:
- replaceGuard.ts: validation logic, error messages, MapPos and Highlight strategies
- persistentHighlight.ts: CM6 state field, range mapping, show/hide behavior
- quickCommandPrompts.ts: placeholder appending logic

64 new test cases covering edge cases like:
- Leaf/editor/file change detection
- Document bounds validation
- Content change detection
- Range mapping through document changes
- Case-insensitive placeholder detection

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 20:04:10 -08:00
Logan Yang
1e3e0786ef
Migrate to native tool call in plus and agent (#2123)
* Implement self-host mode
* Migrate from XML tool calls to native tool calls and reimplement agent mode
* Use ChatOpenRouter for copilot-plus-flash for native tool call sse support
* Fix QA exclusion for search v3
* Update plan and remove debug messages in agent
* Migrate chains off of xml and clean up related logic
* Implement agent reasoning block
* Update css for responsiveness
* Implement agent query pre-expansion and proper reasoning block display for search
* Update agent docs
* Refine agent reason block
* Refine agent reasoning UX
* Fallback to plus non-agent if native tool call is not supported by the model
* Fix time filter query
* Update self-host description
* Skip agent reasoning block and old tool call banners during chat save and load
* Fix projects mode switch to plus and chat auto saves as non-project bug
2026-01-26 18:35:27 -08:00