The `enabled` flag no longer exists on `agentMode`. The Codex/Opencode
backend test fixtures set it on directly-typed `setSettings`/`updateSetting`
literals, which failed the CI `tsc` excess-property check.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Agent Mode is always on now (still desktop-only, since it requires
subprocess support). Drop the persisted `agentMode.enabled` flag and
the master toggle from settings, make `isAgentModeEnabled()`
platform-only, and clean up the now-dead toggle-reactive wiring
(ribbon refresh, settings-driven command re-registration).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the per-backend install/settings panels with a shared Configure
dialog layout. Introduces ConfigDialogShell + ConfigSection, InstallCommandRow,
and an installStatus badge/status-line module (with tests), and reworks the
Claude, Codex, and opencode Configure dialogs onto it. The settings page now
shows each backend's icon, an install-status badge, and a Configure button,
dropping the old BinaryInstallContent and SimpleBackendSettingsPanel helpers.
Copy is simplified for non-technical users and dialog spacing/dividers are
made consistent.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replaces every pending top-level `- [ ]` in `AGENT_MODE_TODOS.md` with a
link to its corresponding issue in logancyang/obsidian-copilot-preview
(#59-#98). Follows the playbook in `AGENT_TODOS_TO_ISSUES_PLAYBOOK.md`:
sub-bullets folded into issue bodies, and "Integrate copilot plus tool
calls" split into 5 child issues (#80-#84) under a now-plain parent
bullet. 39 issues opened end-to-end via paired author/reviewer subagents.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the rescan/import-consent flow with unified discovery across
canonical and agent project folders, lazy migration with a global
suppress flag, and an overflow-menu lockdown for mirrored duplicates.
Replaces the older MODEL_MANAGEMENT_REDESIGN.md with a more focused tech spec,
a data-model spec with review notes, and a portal-container contract doc that
the upcoming reimplementation will follow.
* fix(skills): eliminate Skills Settings UI lag from watcher-driven refreshes
Replaces the time-based watcher suppression with path-scoped expectations:
each SkillManager FS write registers predicates (exists/missing/modified/
subtree-*) and the matching vault events are dropped without triggering a
debounced reconcile. A 10s safety timer backstops never-arriving events.
Toggle/delete/rename/saveProperties now publish incremental in-memory updates
instead of re-running full discovery, so the Properties modal save returns
synchronously and the grid reflects the change on the next render.
* fix(skills): heal drift when expectations expire; preserve external reconciles
Two fixes for the watcher-suppression machinery on PR #2508:
- `armSafetyTimer`: when the 10s backstop fires with expectations still
pending, schedule a debounced reconcile before clearing. Catches the case
where an external process undid our change (e.g. deleted a link we expected
to exist) — the matching event was suppressed, predicate never satisfied,
and previously the state drifted silently until an unrelated watched event
fired.
- `runInternalMutation`: stop cancelling pre-existing reconcile timers in
the finally block. Events fired during the mutation are dropped by the
depth gate, so any debounce timer left armed was scheduled by an external
event before the mutation started and still needs servicing.
Adds two regression tests and updates the pre-existing "safety timer clears
stale expectations" test which is superseded by the new healing behavior.
* perf(agent-mode): warm-adopt preload probe to skip first-chat spawn
The preloader's probe subprocess is now retained as a "warm" entry and
adopted by the first chat-open on that backend, so the first session
skips both the subprocess spawn and the `newSession` round-trip. Preload
status is also tracked per-backend: the chat gates on the active
backend's status only, and the picker shows per-backend loading rows for
the others.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(agent-mode): defer model+effort seeding to backend confirmation
- Seed only baseModelId (not effort) so descriptor-style backends
(Claude) don't see a matching seeded effort and skip the real
setConfigOption inside applyInitialSessionConfig.
- Gate warm-session `ready` on confirmSeededSelection so sendPrompt
can't race onto the probe's model before setModel resolves.
- Add regression tests covering both paths.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace non-existent `copilot:open-copilot-chat` with the registered
`copilot:agent-chat-open-window` ID in both the "Run a command" example
and the smoke-test scaffold so the documented commands actually work.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds OBSIDIAN_CLI_TEST.md with a field guide for driving the plugin
through the Obsidian desktop CLI: multi-vault targeting (the biggest
gotcha — default routes to whichever window was touched last), popout
window limitations, settings round-trips via eval/saveData, real-input
UI driving via dev:cdp, screenshots, console/error capture, performance
metrics, and a smoke-test scaffold. AGENTS.md now points to it and
keeps only the multi-vault warning inline so there is one source of
truth.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(agent-mode): make note links clickable in chat and tool cards
Switches markdown rendering to the modern `MarkdownRenderer.render(app, …)`
API via a shared `renderMarkdown` wrapper, and adds a delegated click
handler so `a.internal-link` inside chat/agent/plan-preview views opens
via `app.workspace.openLinkText` (left-click in current pane, cmd/middle
in a new tab). Tool ActionCards now also expose the collapsed line as
a clickable target once the call has completed.
Threads `app` explicitly through the components and utilities that
previously relied on the global `app` (lexicalTextUtils, notePreviewUtils,
vaultPath, AtMention/Note/Paste/TextInsertion plugins) using the
`useApp()` hook at the leaf, and updates AGENTS.md to discourage new
uses of the global.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(chat): fix ChatSingleMessage mocks for wireInternalLinks
The `renderMarkdown` wrapper now calls `component.register(...)` after
`MarkdownRenderer.render(...)`. The test's `obsidian.Component` mock was
missing `register`, causing the promise to reject and `logError` (which
calls the unmocked `getSettings()`) to crash. Add `register` to the
Component mock and mock `getSettings` as a safety net.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(agent-mode): resolve internal links against active note
Pass the active file path as sourcePath to renderMarkdown so wikilinks
with duplicate basenames or heading-only references resolve to the right
file instead of falling back to vault root.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(agent-mode): teach backends about @-mention pill syntax
The chat editor serializes @-mention pills to `[[note]]`, `{folder}`, and
`{activeNote}` tokens, but only `[[note]]` was understood — the others
were literal strings to the agent. Add a shared pill-syntax directive
appended to opencode, codex, and claude SDK spawn-time instructions
(mirroring the existing skill-creation directive plumbing), and resolve
`{activeNote}` to the real `[[Note Title]]` at send time since the active
file is a client-side concept the agent can't query.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(agent-mode): prune narration comments from pill-syntax change
Cut multi-line JSDoc and inline rationale that explained WHAT or referenced
callers, per the project's "one short line max, only WHY" rule.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(agent-mode): preserve extension when resolving {activeNote}
Non-markdown active files (.pdf, .canvas) lost their extension when
{activeNote} was rewritten via activeFile.basename, leaving the agent
with an ambiguous [[name]] that didn't match the path the pill envelope
carries. Mirror NotePillNode.getTextContent's extension-aware
serialization so the inline token matches.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`rowsToRecord` was persisting every non-empty trimmed key, so names
failing `ENV_VAR_NAME_RE` (e.g. `BAD KEY`, `FOO=BAR`) flowed through
`onChange` into settings and were applied verbatim by backend spawn
paths, which read `envOverrides` directly without re-sanitizing.
Filter invalid rows at the commit boundary so the persisted record
and subprocess env only contain valid POSIX identifiers.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codex (and other non-Claude agents) silently ignore the Claude-only
frontmatter flags (`disable-model-invocation`, `user-invocable`,
`model`), so hard-disabling their toggles on those skills was overly
restrictive. The inline chips on the row already communicate which
flags are Claude-specific.
Adds an "Environment variables" editor to each Agent Mode backend
(Claude, Codex, opencode) so users can inject vars like
CLAUDE_CONFIG_DIR, CODEX_HOME, XDG_CONFIG_HOME, or HTTPS_PROXY without
polluting the parent shell. Overrides are merged onto process.env at
spawn (Codex, opencode) or per prompt() (Claude SDK), and persisted
through a shared sanitizer that enforces POSIX-style names, drops
control chars, and caps the record at 64 entries. The editor debounces
commits to avoid rewriting settings on every keystroke.
Also relocates BinaryPathSetting into src/agentMode/backends/shared/
to honor the agent-mode layer boundaries.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the modal-based permission prompter with inline ToolPermissionCard
rendered at the tail of the chat scroll, matching how plan proposals already
work. Refactor AgentSession status to be derived from underlying primitives
(resolver maps, abortController, etc.) so it cannot drift from reality, and
add tests covering the awaiting_permission lifecycle and turn-error reset.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the brief "Read Read" / "Edited Edit" flash that appeared while
a tool call's input JSON was still streaming. `targetFromTitle` now
returns an ellipsis when the title is just the vendor tool name (the
SDK's documented placeholder state), and a new `verb()` helper picks the
present-participle form for pending/in_progress calls and the past-tense
form for completed/failed ones — so cards read "Reading notes/foo.md" →
"Read notes/foo.md", "Fetching url" → "Fetched url", etc.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- MODEL_MANAGEMENT_REDESIGN.md: BYOK is now the central, user-bring-only
registry; drop per-model and per-provider Availability/Capability toggles;
one global table with provider section rows; Quick Chat added as a 4th
agent sub-tab; OpenCode/Plus models stay in the Agent panel only.
- QUICK_CHAT_AGENT_INTEGRATION.md (new): follow-up plan for elevating
LangChain chat into a first-class agent backend (routing service, session
adapter, v2->v3 settings migration, milestones Q1-Q6).
- AGENT_MODE_TODOS.md: mark shipped items; add notes for opencode/local
models and subscription-based opencode flows.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Stop persisting agent mode selection
* Force canonical default mode on every new agent session
Restore `applyPersistedMode` as a small coerce helper and wire it back
into every backend's `applyInitialSessionConfig` with a literal
`"default"` — never reading from settings. Codex-acp's natural starting
native mode is `read-only` (canonical `plan`), so without this the
picker landed on Plan on every fresh chat.
For codex specifically, also pin the spawn-time approval/sandbox config
via `-c approval_policy="on-request"` and `-c sandbox_mode="workspace-write"`
so codex-acp's first `currentModeId` already matches the canonical `auto`
preset. Without these, the picker briefly flashed "Plan" before the
post-spawn coerce switched it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Drop redundant post-spawn mode coerce
The codex picker briefly flashing "Plan" was fixed at the spawn level
(`-c approval_policy/sandbox_mode` in CodexBackend). Claude's SDK and
opencode's spawn-time `default_agent` already start in canonical
`default`, so the runtime `applyPersistedMode(session, "default")`
calls on all three backends were dead defense-in-depth. Remove them
along with the now-unused helper.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Drop stale type-only import comment in settings/model.ts
`import type` is erased at runtime, so there's no actual cycle to
guard against — the keyword itself already signals "type-only" to
any reader.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Hoist the agent-mode picker out of the not-generating branch so users
can flip between Auto/Plan/Safe mid-turn instead of having to wait for
the agent to stop. Non-agent chats are unaffected since they don't
pass modePickerOverride.
Consolidates the three duplicated model configuration surfaces (basic
settings keys, Models settings table, agent mode curation) into a
single design for product designer handoff. Covers unified Models
panel, agent-first onboarding with three paths, per-backend defaults,
and OpenCode/Copilot Plus as system-managed sections.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(agent-mode): queue mode/model switch while session is still starting
* fix(agent-mode): disable picker while session is starting instead of queueing
Replace the await-session.ready queueing approach with a UI-level gate:
canSwitchModel/Effort/Mode now return false while status === "starting",
so the picker renders as disabled until the backend session is ready.
The await-during-onChange path made the picker feel buggy because clicks
appeared to do nothing for a beat before applying.
* fix(agent-mode): re-render mode/model picker on session status change
The picker hooks compute a memoization signal that React diffs to decide
when to rebuild the override. Status was not part of the signal, so the
starting → idle transition fired listeners but the snapshot string was
unchanged and the picker stayed disabled forever. Include `getStatus()`
in both signals so the picker re-enables once the session is ready.
* feat(agent-mode): tint chat input border and mode picker by mode
Plan mode shows a blue border and soft glow; auto mode shows red.
The mode picker label picks up a matching faded color that brightens
on hover/focus, making the active agent mode obvious at a glance.
* fix(agent-mode): preserve mode tint on keyboard focus
The ghost2 button variant applies focus-visible:tw-text-normal, which
overrode the focus: tint on keyboard focus. Switch to focus-visible:
so tailwind-merge resolves the conflict in favor of the mode color.
* fix(agent-mode): preserve persisted enabled flag on mobile
Sanitization no longer force-overrides agentMode.enabled on mobile, so
a desktop-synced preference is not silently overwritten the next time
mobile persists settings. Mobile gating now lives at usage sites via a
shared isAgentModeEnabled / useIsAgentModeEnabled helper.
* fix(lint): drop unused Platform import in commands/index.ts
The remaining `Platform.isDesktopApp` site uses a dynamic import that
shadows the top-level binding, so the static import is unused after the
agent-mode gating refactor.
New installs now start with Agent Mode on. Mobile is locked off at the
sanitizer level so a desktop-synced settings file can't activate the
unsupported subprocess flow.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
macOS GUI apps launched from Finder/Dock inherit a minimal launchd PATH
(`/usr/bin:/bin:/usr/sbin:/sbin`) that omits Homebrew and user install
prefixes, so `which codex-acp` returned nothing and the Auto-detect button
falsely reported "not found." Augment PATH with the well-known prefixes
(`/opt/homebrew/bin`, `/usr/local/bin`) before invoking `which`, sharing
one source of truth with the existing spawn-time augmentation in
`nodeShebangPath`. For Claude, the Auto-detect button now routes through
the existing `resolveClaudeBinary` resolver (Volta/asdf/NVM/Homebrew/
npm-global/`~/.local/bin`) via a new optional `detect` prop on
`BinaryPathSetting`, matching the candidate list already used by the top
status row.