tardigrde553 downloadsPrivacy-first agentic AI chat in the sidebar. The agent reads, searches, and edits your vault through visible tool calls, routed to zero-data-retention providers by default, or runs fully local with Ollama.
Experimental — under heavy development. APIs, settings, and behavior change frequently. Pin a version if you need stability.
A privacy-first, agent-led AI chat in Obsidian's right sidebar. Instead of a plain chatbot, you get an agent that acts on your vault: it reads, searches, writes, edits, renames, traverses links, and reads/writes frontmatter through typed tools — and every tool call is rendered inline in the chat, so you always see exactly what the agent is doing.
It runs entirely inside Obsidian on desktop and mobile, built on the pi agent packages. Use it with hosted models through OpenRouter — locked to zero-data-retention providers by default — or with a fully local Ollama server where nothing ever leaves your machine.
The full user and maintainer docs are published with VitePress at tardigrde.github.io/obsidian-agentic-chat.
Local docs commands:
npm run docs:dev
npm run docs:build
npm run docs:preview
Your notes are yours. This plugin is built so that using AI on them does not mean handing them to a model trainer.
zdr: true) and don't log or train on prompts (data_collection: "deny"). Provider fallbacks are allowed but must satisfy the same constraints — a fallback is never a privacy downgrade.data.json keeps only secret IDs and non-secret settings.With the strict zero-data-retention default, some models may have no compliant provider on OpenRouter. If a request can't be routed, pick a different model in settings, relax the privacy toggles deliberately, or use Ollama.
ItemView styled with theme variables (light and dark just work). Tool calls appear as live step cards with elapsed-time timing; reasoning tokens stream into a collapsible section; answers render with Obsidian-style Markdown, including callouts and Mermaid diagrams; an animated indicator shows while the agent is working.ask_user; the turn pauses on an inline question with optional answer buttons, then continues from your answer./undo reverts the agent's most recent vault change (write/edit/delete/rename/frontmatter)./add-dir, or settings). In Safe mode the agent then auto-runs reads/writes inside them and asks before touching anything outside — even reads. Granted folders show as removable "scope" chips; /dirs lists/revokes them. Empty = approval applies vault-wide. (The ignore list still wins inside a granted folder.)/add-dir /absolute/path for read-only external_inspect list/read/search calls. It is not prompt context, is not a vault working directory, and uses passive external://relative/path citations instead of Obsidian links. External workspace root tools are desktop-only and they are not registered on mobile./plan enters a sticky, read-only planning state (all mutations blocked) until /endplan restores your prior Safe/YOLO posture. A "Plan" badge shows in the composer while active.Private/, *.secret.md, **/diary/**) name notes the agent can never touch. Enforced at the tool layer: matching files are invisible to every tool — they report as "not found", so the agent can't read, list, search, or edit them.fs)./usage, /status), a pre-send "next ~$x" estimate, a one-time cost alert, a hard spend cap that blocks/aborts at a limit, and a context-window progress bar. Long sessions auto-compact older turns into a summary before they overflow the model's window, and /compact [instructions] can summarize older turns on demand./export saves the active conversation as Markdown, /sessions clear --confirm clears conversation history for the current scope, and /memory add, /memory review, /memory manage, /memory export, and /memory clear --confirm give stored long-term memories an explicit local lifecycle.AGENT.md profiles in a vault folder or use the built-in roster; invoke with /agent <name> <task>. See Subagents.SKILL.md files into a vault folder; they're offered to the agent (name + description only, body loaded on demand) and invokable with /skill <name> or directly as /<name>. Skills with $ARGUMENTS / $1 absorb the old "prompt template" concept. See Skills./style.AGENTS.md from your vault root (or CLAUDE.md / GEMINI.md if absent) as standing context on every turn, so the same facts and conventions persist across every conversation. Edit the file yourself, or run /init [instructions] to have the agent curate it surgically (each edit shown as a diff). Standing instruction files are implicit context and do not appear as active-note attachments. See Standing instructions.web_search (Tavily / Brave / SearXNG backend) and fetch_url, plus a built-in /deep-research skill. Egress-gated: while it's off the tools aren't registered, so nothing leaves your device for the web. See Web access & research.mcp__server__tool, flow through the approval gate, and return capped text into the model context. No stdio/subprocess transport. See MCP tools./ commands & skills, @ note mentions, including @note#heading / @note^block slices), the active note auto-attached as a removable chip (ignored notes are skipped), drag-and-drop or right-click a note/folder/selection to attach it, copy/retry buttons on every answer, prompt editing (click a sent message to rewind), shell-style up/down command history, a model pill with a per-request model override, and a settings page split into virtual tabs.All paths are vault-relative; absolute paths and .. escapes are rejected, and any path matched by your ignore list reports as "not found".
Read-only (always run, no approval):
| Tool | What it does |
|---|---|
read |
Read a note's contents, optionally bounded with startLine/endLine or byte offset/limit. |
ls |
List a folder. |
search |
Search note paths and note contents (with result caps and optional folder scope). |
get_active_note |
Read the note currently open in the editor. |
local_graph |
A note's immediate neighborhood — inbound (backlinks) and outbound notes. |
get_properties |
Read a note's YAML frontmatter as structured data. |
Mutating (gated by the approval policy):
| Tool | What it does |
|---|---|
write |
Create or overwrite a note. |
edit |
Exact-string replacements within a note. |
set_properties |
Write YAML frontmatter via Obsidian's API (won't corrupt the body). |
rename |
Rename or move a note — inbound wikilinks and backlinks are updated automatically. |
delete |
Move a note or empty folder to trash. |
The search meta-tool keeps path and content search behind one model-facing decision surface; the older find and grep tools remain compatibility implementations for tests/internal surfaces. The graph (local_graph), frontmatter (get_properties / set_properties), and link-aware rename tools are Obsidian-native: they let the agent traverse the [[wikilink]] graph and edit structured metadata reliably instead of brute-grepping or hand-editing raw YAML. get_backlinks and get_links also remain compatibility implementations, but the default model-facing surface uses local_graph to avoid three overlapping graph tools.
The agent loads a single standing-instructions file from the vault root on every turn and injects it into the system prompt — a place for the vault's purpose, key folders, conventions, and your preferences. It's the standard AGENTS.md convention: portable, transparent, and synced with the vault.
AGENTS.md is read first; if absent, CLAUDE.md, then GEMINI.md. Symlink one to another so several agents (Claude Code, Gemini CLI, this plugin) share one source of truth.AGENTS.md at the vault root and write what the agent should always know. Keep it concise — it's part of every request./init [instructions] asks the agent to read the vault structure and the current file, then make surgical edits to refine it (each shown as a diff for you to accept/reject; write is used only when creating the file). Anything after /init is passed to the agent as additional instructions for that curation run. Edits go through the normal approval gate and are undoable with /undo.AGENTS.md, CLAUDE.md, or GEMINI.md at the vault root does not show it as a removable active-note chip.edit/write tools — the next turn picks up the change.For multi-part work, the agent can delegate to focused child agents instead of doing everything in one context:
{ name, description, systemPrompt, model?, toolAllowlist } — authored as an AGENT.md file (YAML frontmatter + body) in a settings-configured vault folder. There's a built-in roster (researcher / reviewer / editor), and a vault profile overrides a built-in of the same name.subagent tool, either single ({ agent, task }) or parallel ({ tasks: [...], concurrency? }). Each child runs with its own context window, the profile's model (falling back to the parent's), and only the tools in its allowlist — so a research fan-out that can't write never prompts you.subagent tool, so delegation depth is capped at one (no grandchildren).Invoke directly with /agent <name> <task>, or /agent with no argument to pick from a list. The model can also fan out on its own when a task benefits from parallel, isolated subtasks.
Note: child steps are live-only. While a dispatch runs you can expand the per-child tree; what persists when you reopen the session is the summary text, not replayable child steps — the honest cost of context isolation.
Skills are reusable instruction/capability units in the agentskills.io SKILL.md format:
SKILL.md files into the vault folder set in settings. They're listed to the model with only their name + description (progressive disclosure); the full body is loaded only when a skill is invoked./skill <name> [args], or directly as /<name> (built-in commands win a name collision; the skill stays reachable via /skill <name>). Auto-loaded skills appear in the / autocomplete popup.$ARGUMENTS (all args) or $1, $2, … (positional). This absorbs the old "prompt template" concept — a template is just a skill that takes arguments. /template still works as a deprecated alias for /skill.Off by default. Enable Web access in settings (it carries an egress warning) to register two read-only tools and a research skill:
web_search — queries your configured backend (Tavily / Brave / SearXNG; keyed in settings) and returns ranked title/URL/snippet results.fetch_url — fetches an http(s) page and returns readable text (scripts/markup stripped, entities decoded). Long pages can be paged with offset / nextOffset. A best-effort SSRF guard blocks non-http(s) schemes and localhost/private/link-local hosts./deep-research — a built-in skill (advertised only while web access is on) that supervises parallel researcher subagents, runs an adversarial reviewer pass, then synthesizes and saves a sourced note with source artifacts/URLs and a ## Sources list.Egress is gated by a single off-by-default setting: while it's off, the web tools aren't registered at all, so nothing can leave your device for the web. When on, search queries and fetched URLs go to your chosen search provider and the fetched sites — outside the model-provider privacy boundary.
Off by default. Enable MCP in settings to discover tools from remote MCP servers over HTTPS Streamable HTTP.
stdio, subprocesses, and insecure http:// endpoints are intentionally unsupported, so the feature stays mobile-safe and cannot spawn local processes.mcp__<server-id>__<tool-name> so model tool names are stable and collision-free.obsidian://agentic-chat-mcp-oauth callback on mobile when the provider supports app redirects. Providers that require localhost redirects still require Obsidian desktop sign-in or bearer/static-header auth. After sign-in, settings immediately probes tool discovery so a successful login also confirms the server is usable.data.json stores only secret IDs plus non-secret MCP metadata.insufficient_scope, downgrade the MCP protocol version when initialization rejects the newest advertised version, reopen a Streamable HTTP session once if the server reports that the session expired, and make bounded Last-Event-ID resume attempts when SSE delivery ends before the matching JSON-RPC result. If a request is accepted asynchronously (202), the client opens the server's SSE stream and waits for the matching result. Each configured server also has a Test connection action in settings that lists tools through the same MCP client path used at runtime; runtime diagnostics include per-server URL/auth/token state and categorized discovery errors.read_artifact and search_artifact tools let the model inspect large results in chunks without re-running the remote MCP call. Artifacts are automatically pruned by age, count, and total byte budget, while artifacts still cited by saved sessions are retained. Image/resource payloads are summarized or omitted instead of being dumped raw.Current limits: tool calls only; MCP resources/prompts/roots/sampling, long-lived background MCP event consumers, and rich binary/resource rendering are future work tracked in the roadmap.
Off by default. Enable Observability in settings only when you want agent traces exported to an endpoint you control.
/api/public/otel/v1/traces; generic OTLP takes a full /v1/traces endpoint.data.json keeps only secret IDs and non-secret configuration.metadata mode exports turn/generation/tool/approval spans, model/provider ids, token usage, cost, stop reason, and error class. It does not export prompts, answer text, note contents, tool args, tool results, or vault paths. Redacted text previews adds short prompt/answer previews with obvious secret tokens masked. Full prompt/output content sends prompt and answer text to the configured backend and should only be used deliberately.In the interest of transparency (and the Obsidian Developer Policies):
The plugin is listed in the official community directory. Open Settings → Community plugins (disable Restricted mode if prompted) → Browse, search for Agentic Chat, and install + enable it. This is the simplest path and keeps you on official releases with automatic updates.
For pre-release builds not yet in the community directory, install BRAT, then add tardigrde/obsidian-agentic-chat as a beta plugin. BRAT installs and updates from GitHub releases.
main.js, manifest.json, and styles.css from the latest release (or build them — see Development).<your vault>/.obsidian/plugins/agentic-chat/.OpenRouter (default, zero data retention):
moonshotai/kimi-k2.6.Ollama (fully local):
http://localhost:11434) and model tag.For any OpenAI-compatible gateway, set the provider to OpenAI-compatible. The Gateway preset shortcut fills common base URLs for OpenWebUI, LM Studio, vLLM, llama.cpp, Chutes, and Venice.ai; the transport stays generic, so custom gateways still work by editing the Base URL and Model fields directly.
The settings page is organized into virtual tabs (Models / Agent / Approval / Web / MCP / Observability / Notifications / Resources) so it isn't one long scroll.
Behind a corporate proxy on desktop, set Models → Network proxy → HTTP proxy to an HTTP proxy URL such as http://host:port. Plugin-owned OpenRouter/OpenAI-compatible chat requests, model browsing, web tools, MCP, and observability inherit it. The MCP and Observability tabs have their own optional overrides; leave them empty unless a server needs different routing. On mobile, keep the plugin proxy fields empty and use the device/VPN/network-level proxy path instead.
Then click the chat ribbon icon, or run Agentic Chat: Open chat.
/ for commands and skills or @ to attach a note — both show an inline autocomplete dropdown./new resets it). Ignore-listed notes and root standing-instruction files (AGENTS.md, CLAUDE.md, GEMINI.md) are not auto-attached. Add more with @<note>, @<note>#<heading>, @<note>^<block-id>, + Folder (attaches a folder listing), by dragging a note/folder from the file explorer onto the composer, or from Obsidian context menus: right-click a note/folder or selected editor text and send it to Agentic Chat./plan to lock to read-only./undo reverts the agent's last vault change./model. Shift-click / Shift-Enter in the picker applies a model for the next prompt only (shown with a "next only" badge), then auto-reverts./help, /status, /usage) render as a collapsible in-pane block.| Command | What it does |
|---|---|
/new |
Start a new conversation. |
/sessions (/history) |
Browse, search, reopen, rename, or delete past conversations. |
/model |
Switch model (Shift = next-prompt-only override). |
/config (/mode) |
Switch permission mode (Safe / YOLO). |
/add-dir [folder] |
Grant a working directory (auto-run inside, ask outside); no arg = folder picker. |
/dirs |
List / revoke granted working directories. |
/plan … /endplan |
Enter / leave sticky read-only plan mode. |
/compact [instructions] |
Summarize older turns now; optional instructions guide the summary. |
/init [instructions] |
Curate standing instructions; optional text guides the update. |
/style [name] |
Switch output style (default / brainstorm / learning). |
/skill [name] [args] |
Run a vault skill (also /<skill-name> directly). |
/agent [name] [task] |
Delegate a task to a subagent (no arg = picker). |
/undo |
Undo the last vault change the agent made. |
/status |
Show provider, model, mode, output style, session, MCP servers/tools. |
/usage |
Show token & cost totals. |
/help |
List commands. |
Privacy settings are applied to every OpenRouter request via the provider routing options:
| Setting | Effect | Default |
|---|---|---|
| Require zero data retention | zdr: true — only endpoints that retain nothing |
on |
| Deny prompt logging and training | data_collection: "deny" |
on |
| Allow provider fallbacks | allow_fallbacks: true — fallbacks still obey the two rules above |
on |
Tightening these can leave a model with no compliant provider; that's the intended trade-off of privacy over convenience. Ollama bypasses all of this by never leaving your machine.
npm install
npm run dev # esbuild watch mode
npm run docs:dev # VitePress docs dev server
npm test # vitest (path safety, exact edits, search, routing, approval, working dirs, sessions, skills, agent service, …)
npm run typecheck # tsc — the lint gate
npm run lint # eslint
npm run build # typecheck + production bundle
npm run docs:build # VitePress production docs build
The unit suite runs without Obsidian: the obsidian package is replaced by a minimal mock via a vitest alias, the model stream by an injected streamFn, and the session store by an in-memory adapter. See AGENTS.md for architecture notes and ROADMAP.md for planned work.
A base end-to-end suite drives the plugin inside a real Obsidian instance via
wdio-obsidian-service — it
auto-downloads Obsidian, copies test/e2e/vault into a throwaway sandbox, loads this plugin, and
runs the specs in test/e2e/specs/. The no-token specs cover Obsidian boot, chat UI wiring,
session persistence, deterministic approval/write/edit/undo flows, settings UI persistence, and
existing data.json migration.
npm run test:e2e # builds the plugin, then boots Obsidian and runs the local e2e suite
npm run test:e2e -- --spec test/e2e/specs/smoke.e2e.ts
npm run test:e2e:dogfood
npm run test:e2e:mobile
npm run test:e2e:matrix -- --spec test/e2e/specs/smoke.e2e.ts
npm run verify:mobile
It is not part of CI yet (it launches Electron, so it needs a display + cached Obsidian
downloads). npm run test:e2e intentionally runs one Obsidian version at a time; set
OBSIDIAN_VERSIONS=earliest/earliest or another single pair for a targeted run. Use
npm run test:e2e:matrix for supported-version coverage; it runs sequentially across
OBSIDIAN_VERSION_MATRIX or, by default, earliest/earliest,latest/latest. Unit tests (vitest)
stay the fast inner loop; the e2e suite catches integration seams — view registration, settings
tabs, persistence/migration, composer wiring, slash-command routing, and approval gates — that the
mocked unit tests can't.
npm run test:e2e:dogfood is the expensive high-coverage dogfood target. It
generates a fresh adversarial vault plus external workspace under
logs/dogfood-runs/<run-id>/, installs the current plugin into that vault,
drives real Obsidian through WDIO, and fails if the dogfood invariant oracle or
the generated run report finds a product regression. It uses a deterministic
scripted model path by default, so it is suitable for open-source contributors
without live model credentials.
npm run verify:mobile is part of npm run build and blocks direct Node/Electron
API usage outside documented desktop-only fallbacks. npm run test:e2e:mobile
builds the e2e bundle, enables wdio-obsidian-service mobile emulation, applies
Chrome phone-sized device metrics, and runs a WDIO smoke to catch mobile layout
regressions. Real Obsidian Mobile still needs the Android/iOS checklist in
MOBILE_TESTING.md.
Live model-backed e2e specs are opt-in and skip unless their keys are present.
Set AGENTIC_CHAT_API_KEY, AGENTIC_CHAT_BASE_URL, and AGENTIC_CHAT_MODEL
for any OpenAI-compatible gateway (OpenRouter, OpenWebUI, local proxy, etc.). When running behind a
corporate proxy, keep NO_PROXY=localhost,127.0.0.1,::1; the WDIO config keeps
chromedriver local and passes a normalized proxy setting to Obsidian/Electron.
When provider transport, model settings, proxy handling, or request formatting
changes, run npm run verify:provider-live; unlike the default e2e command, it
requires live credentials and fails instead of accepting skipped provider specs.
That gate also runs npm run eval:provider-cache-live, which sends repeated
large stable prompts through the OpenAI-compatible gateway and fails unless a
warm request reports provider cache-read tokens. The cache probe accepts generic
OPENAI_COMPATIBLE_* env vars, or the same OPENWEBUI_* vars used by the e2e
spec. To load a local dotenv file without shell-sourcing secrets, pass
npm run eval:provider-cache-live -- --env-file .env.
Live MCP e2e is also opt-in and refuses insecure endpoints:
AGENTIC_CHAT_E2E_MCP_URL=https://mcp.example.com/mcp \
AGENTIC_CHAT_E2E_MCP_TOOL=tool-name \
AGENTIC_CHAT_E2E_MCP_ARGS_JSON='{"input":"value"}' \
npm run test:e2e -- --spec test/e2e/specs/mcp-live.e2e.ts
For static-header servers, also set AGENTIC_CHAT_E2E_MCP_HEADER_NAME and
AGENTIC_CHAT_E2E_MCP_HEADER_VALUE.
@earendil-works/pi-agent-core, @earendil-works/pi-ai), MIT.lhr0909/pi-obsidian by Simon Liang (MIT / 0BSD).MIT. See also the third-party notices for bundled dependencies and adapted code.