Last Updated: 2026-04-04
- Name: Claudesidian MCP
- Version: 5.11.1
- Type: Obsidian Community Plugin
- Purpose: MCP integration for Obsidian with AI-powered vault operations
- Architecture: Agent-Tool pattern with domain-driven design
- Stack: TypeScript, Node.js, Obsidian Plugin API, MCP SDK
This is an Obsidian community plugin that must follow official Plugin Guidelines. All code changes must adhere to these best practices.
// Plugins extend the Plugin base class
export default class MyPlugin extends Plugin {
async onload() {
// Initialize UI, register commands, set up events
// Use registration methods for auto-cleanup
}
async onunload() {
// Clean up resources (most handled automatically)
}
}Key lifecycle rules:
- All registration methods (
registerEvent,addCommand,registerView,registerInterval) auto-cleanup on unload - Never store view references in the plugin instance (causes memory leaks)
- Use
this.app.workspace.onLayoutReady()to defer startup operations
CRITICAL: All styles must be defined in styles.css, never inline in TypeScript/JavaScript.
// ❌ NEVER do this
element.style.color = 'white';
element.style.backgroundColor = 'red';
element.style.display = 'flex';
// ✅ ALWAYS do this
element.addClass('my-plugin-element');/* In styles.css - use CSS variables for theme compatibility */
.my-plugin-element {
color: var(--text-normal);
background-color: var(--background-primary);
display: flex;
}Required CSS Variables (never hardcode colors):
| Variable | Purpose |
|---|---|
--text-normal, --text-muted, --text-faint |
Text colors |
--text-accent |
Interactive/link text |
--background-primary, --background-secondary |
Background colors |
--background-modifier-border |
Borders |
--background-modifier-error |
Error states |
--interactive-accent, --interactive-accent-hover |
Buttons/interactive |
--radius-s, --radius-m, --radius-l |
Border radius |
innerHTML is FORBIDDEN with dynamic content:
// ❌ NEVER - XSS vulnerability
element.innerHTML = userProvidedContent;
element.innerHTML = `<div>${dynamicData}</div>`;
// ✅ Safe patterns
element.textContent = userProvidedContent; // For text
element.createEl('div', { text: dynamicData }); // Obsidian API
// ✅ Safe innerHTML patterns (only these are acceptable)
element.innerHTML = ''; // Clearing
const escaped = div.innerHTML; // Reading already-escaped contentSafe DOM creation with Obsidian API:
// Use createEl, createDiv, createSpan
const container = contentEl.createDiv({ cls: 'my-container' });
const heading = container.createEl('h2', { text: 'Title' });
const button = container.createEl('button', {
text: 'Click me',
cls: 'my-button'
});
// For icons, use setIcon
import { setIcon } from 'obsidian';
setIcon(button, 'chevron-right');Always use registerDomEvent for DOM events:
// ❌ NEVER - causes memory leaks on unload
element.addEventListener('click', handler);
document.addEventListener('keydown', handler);
window.addEventListener('resize', handler);
// ✅ ALWAYS - auto-cleanup on unload
this.registerDomEvent(element, 'click', handler);
this.registerDomEvent(document, 'keydown', handler);
this.registerDomEvent(window, 'resize', handler);
// ✅ For Obsidian workspace events
this.registerEvent(this.app.vault.on('modify', handler));
this.registerEvent(this.app.workspace.on('active-leaf-change', handler));// ❌ NEVER use vault.adapter directly (mobile incompatible)
await this.app.vault.adapter.read(path);
await this.app.vault.adapter.write(path, content);
// ✅ Use Vault API
await this.app.vault.read(file);
await this.app.vault.cachedRead(file); // Faster, uses cache
// ✅ For modifying files, use Vault.process() (atomic, prevents conflicts)
await this.app.vault.process(file, (content) => {
return content.replace('old', 'new');
});
// ✅ Use Editor API for active file (preserves cursor)
const editor = this.app.workspace.activeEditor?.editor;
if (editor) {
editor.replaceRange('new text', from, to);
}Exception: vault.adapter is acceptable for storage paths that Obsidian does not index or that must be addressed directly, including hidden legacy paths and configured Nexus data roots. Normalize paths first and keep storage roots settings-derived; do not hardcode .nexus.
| Task | Do This | Not This |
|---|---|---|
| HTTP requests | requestUrl() |
fetch() |
| Path handling | normalizePath(userPath) |
Direct string concat |
| OS detection | Platform.isMobile, Platform.isDesktop |
User agent sniffing |
| File lookup | vault.getFileByPath(path) |
Iterating vault.getFiles() |
| View access | workspace.getActiveViewOfType(MarkdownView) |
workspace.activeLeaf.view |
this.addCommand({
id: 'my-action', // Don't duplicate plugin ID
name: 'My action', // Sentence case, no "command" word
// NO default hotkey - users set their own
checkCallback: (checking) => {
const view = this.app.workspace.getActiveViewOfType(MarkdownView);
if (view) {
if (!checking) {
// Execute command
}
return true;
}
return false;
}
});import { Platform } from 'obsidian';
if (Platform.isMobile) {
// Mobile-specific code
}
// ❌ These APIs are NOT available on mobile
import { fs, path, crypto } from 'node:*'; // Node.js modules
require('electron'); // Electron APIs
// ✅ Mobile alternatives
// Use SubtleCrypto instead of crypto
// Use navigator.clipboard instead of electron clipboard
// Set isDesktopOnly: true in manifest if Node.js required// ✅ Interactive elements need aria-labels
const iconButton = container.createEl('button', { cls: 'icon-button' });
iconButton.setAttribute('aria-label', 'Open settings');
setIcon(iconButton, 'settings');
// ✅ Keyboard navigation (Tab, Enter, Space)
// ✅ Focus indicators with :focus-visible
// ✅ Touch targets minimum 44×44px on mobile| Rule | Requirement |
|---|---|
| Type safety | No as any casts, use instanceof checks |
| Variables | Use const/let, never var |
| Console logging | No console.log in production, only console.error for actual errors |
| UI text | Sentence case everywhere |
| Cleanup | Remove all template/sample code before submission |
{
"id": "my-plugin-id", // Lowercase, no "obsidian", doesn't end with "plugin"
"name": "My Plugin Name", // No "Obsidian" or "Plugin" suffix
"version": "1.0.0",
"minAppVersion": "1.0.0",
"description": "Does something useful.", // <250 chars, ends with punctuation
"author": "Author Name",
"isDesktopOnly": false // true only if Node.js APIs required
}// ❌ Vault 'create' fires for ALL files on startup
this.registerEvent(this.app.vault.on('create', handler));
// ✅ Wait for layout ready
this.app.workspace.onLayoutReady(() => {
this.registerEvent(this.app.vault.on('create', handler));
});
// OR check inside handler
onCreate(file: TFile) {
if (!this.app.workspace.layoutReady) return;
// Process event
}Substantial UI work gets a standalone mockup in docs/mockups/ before any
production code: new views, panels, modals, settings tabs, chat surfaces, and
layout refactors. A tweak inside an existing layout does not — say so and
implement.
Do not improvise the process from this note. The nexus-ui-mockups skill is the
source of truth for how a mockup is built, revised, validated and handed off as a
plan's visual contract — load it before starting, and follow its protocols and
its check_mockup.py validator.
Implementation is a separate job under src/ and is governed by CLAUDE.md's hard
rules (styles.css, registerDomEvent, no dynamic innerHTML). Mockup-only
liberties never travel into plugin code.
Mar 4: New Models + Bug Fixes ✅ (v4.4.5 → v4.4.6)
- Added Codex Sonnet 4.6, Gemini 3.1 Pro/Flash Lite, GPT-5.3 Chat/Codex; removed legacy Codex 4 Opus/Sonnet
- Fixed ConversationTitleModal focus trap: replaced fragile setTimeout/rAF hack, added focus restoration in onClose()
- Fixed default temperature not loading from user settings (hardcoded 0.5 → reads
defaultTemperature) - Fixed prompt selector using name instead of id as dropdown key (wouldn't persist selection)
- Fixed
syncWorkspacePrompt()saving name instead of id
Feb 28: Dynamic Image Model Defaults ✅ (v4.4.4)
- Image generation defaults now resolve from user settings instead of hardcoded values
- Priority chain: explicit param > user settings > first available provider/model > fallback
generateImage: newresolveDefaults(),getAvailableProviderNames(), dynamic schema/errorsexecutePrompts/promptParser/RequestExecutor: removed hardcoded model lists and google-only restrictionsImageGenerationService: addedgetInitializedProviders()executeTypes: provider optional + accepts openrouter, model widened to string
Feb 26: Dynamic Image Model Loading ✅ (v4.4.3)
- Image model dropdowns now load dynamically from adapters (removed hardcoded
IMAGE_MODELSin ChatSettingsRenderer) generateImagetool schema builds model enum at runtime from configured providers- Added
getModelsForProvider()andgetSupportedModelIds()toImageGenerationService - Adding a new image model to an adapter now auto-populates UI and tool schema
Feb 26: New Image Models + FLUX Validation Fix ✅ (v4.4.2)
- Added
gemini-3.1-flash-image-preview(Nano Banana 2): Google direct + OpenRouter, 512px-4K, 14 ref images, new aspect ratios (1:4, 4:1, 1:8, 8:1) - Added
gpt-5-image(OpenRouter only): GPT-5 with image generation, 400K context - Fixed FLUX models (
flux-2-pro,flux-2-flex) failing validation — missing fromsupportedModelsandImageModelunion type
Feb 23: Workspace Settings Display Fix ✅ (v4.4.1)
- Root cause:
renderWorkspacesTab()passed staleprefetchedWorkspaces(populated before SQLite ready) → WorkspacesTab skipped async load, showed JSONL fallback data only - Fix: pass
prefetchedWorkspaces: null→ WorkspacesTab always shows loading skeleton → awaits bothworkspaceService+hybridStorageAdapter→ queries SQLite with full data - Also removed temporary
[DEBUG-WS]console.error logs from 6 files:SettingsView.ts,WorkspacesTab.ts,ServiceManager.ts,WorkspaceService.ts,BackgroundProcessor.ts,PluginLifecycleManager.ts
Feb 22: Tool Call History Fix ✅ (v4.4.0)
MessageStreamHandler.ts— post-loop safety net: forcesstate=complete+ accumulated toolCalls onto in-memory message before second save runs; prevents staledraft/nullfrom overwriting good dataConversationService.ts— try-catch aroundJSON.parse(tc.function.arguments)inconvertToLegacyConversation; malformed JSON no longer crashes entire conversation loadMessageRepository.ts— defensive try-catch inrowToMessage()for toolCallsJson/metadataJson/alternativesJson- Note for existing stale conversations: Use the
Nexus: Rebuild cachecommand to force SQLite cache rebuild from the synced event store; tool calls will reappear if present in JSONL.
Feb 22: TypeScript Build Fix ✅ (PR #31)
IPCTransportManager.ts— changed socket param fromNodeJS.ReadWriteStreamtonet.Socket; removed 4 redundant castsnpm run build(tsc + esbuild) now passes clean
Feb 22: OpenAI CORS Bypass + Validation Fixes ✅ (PR #29)
nodeFetch.ts— Node.jshttps.request()passed as customfetchto OpenAI SDK; bypasses CORS on/v1/responses- Fixed 3 stale validation probes:
gpt-5-nanoneedsmax_completion_tokens;Codex-3-5-haiku-latestdeprecated →Codex-haiku-4-5-20251001 - 619 tests (adds 55 nodeFetch unit tests)
Feb 22: Provider OAuth Connect ✅ (PR #26 — v4.3.4)
- OpenAI Codex OAuth via ChatGPT: connect button, token refresh, model listing with
(ChatGPT)suffix - Codex Responses API gotchas (CRITICAL):
- No
previous_response_idsupport — must use stateless full input array continuation deltais plain string, not object —typeof event.delta === 'string'check required- CORS from
app://obsidian.md— must use Node.jsrequire('https').request()notfetch() for awaitunreliable in Electron — use explicitchunkQueue/chunkWaiterevent-listener queueinstructionsfield always required — cannot be conditional on conversationHistory- Model won't use tools without
tool_choice: "auto"+ explicit tool-use preamble in instructions
- No
- 547 tests passing, all console.log removed, JSON.parse in try-catch,
expires_invalidated
Feb 21: IPC Transport Fix ✅ (v4.3.2 — cherry-pick from PR #24, DylanLacey)
- Fixed
handleSocketConnectionnever wiring socketclose/endtotransport.close() - Crashed connectors no longer permanently wedge
Protocol._transport; reconnects cleanly - FD leak on failed
connect()also fixed (socket destroyed on rejection) - PR #24 mux part still open — pending contributor fix for hardcoded socket path
Feb 20: New Model Definitions ✅ (PR #22 — v4.3.1)
- Added Codex Sonnet 4.6 (
Codex-sonnet-4-6): 200K ctx, 64K out, $3/$15/M + 1M beta variant - Added Gemini 3.1 Pro Preview (
gemini-3.1-pro-preview): 1M ctx, 65K out, $2/$12/M - Updated: AnthropicModels.ts, GoogleModels.ts, OpenRouterModels.ts
Feb 9: Conversation Memory Search ✅ (PR #19 — merged)
- Semantic search across conversation turns and tool call traces via
searchMemorytool - Two modes: Discovery (workspace-scoped) and Scoped (session-filtered, N-turn window)
- QA pair model + ContentChunker (500-char/100-overlap) + sqlite-vec KNN + multi-signal reranking
- Real-time indexing via ConversationEmbeddingWatcher + background backfill
- Actionable error feedback, enhanced descriptions, optional workspaceId
- EmbeddingService refactored: facade pattern (1034→199 lines) + 3 domain services
- MemorySearchProcessor (824→553) and IndexingQueue (822→497) split into extracted modules
- 351 tests pass (205 new), all coverage thresholds met, 19 commits
- Plan:
docs/plans/conversation-memory-search-plan.md - Review:
docs/review/pr19-conversation-memory-search.md
Feb 5: Startup Performance Fix ✅ (PR #15)
- Non-blocking startup ~200ms (75x improvement from ~15s)
- Root cause: deadlock between ChatView.onOpen() and onLayoutReady
- Solution: setTimeout(0) services, registerViewEarly(), non-blocking onOpen()
Feb 5: Chat Stop/Retry/Branch Bug Fixes ✅ (PR #16)
- 12 bugs fixed across stop, retry, and branch navigation
- Key patterns: clear-and-restream, incremental reconciliation, dual abort controllers
- 142 unit tests across 7 new test files
Feb 5: Inline AI Editing Feature ✅ (PR #14)
- Right-click or hotkey to edit selected text via LLM
- State machine pattern, streaming preview, Jest test infrastructure (41 tests)
Jan 24: ExecutePrompts improvements (optional provider/model, reference images, CommandManager cleanup) Jan 12: MCP integration settings fix (invalid config handling) Jan 4: CanvasManager agent (4 tools), SQLite transaction fix, memory leak fixes (7), embeddings toggle
Dec 22: Subagent UI + architecture Dec 20: Auto-compaction + dual models + WebLLM Dec 17: Two-Tool Architecture (95% token reduction) Dec 16: Local embeddings + dead code cleanup (~4,000 lines removed) Dec 9: Mobile + branching persistence Dec 3: SQLite + JSONL hybrid storage
/src/agents/- Agent implementations (PromptManager, ContentManager, etc.)/src/services/- Shared services (LLM providers, memory, conversations)/src/components/- UI components (chat view, settings, modals)/src/types/- TypeScript type definitions/src/utils/- Utility functions and helpers
main.ts- Plugin entry point and lifecycle managementconnector.ts- MCP server connector for Codex Desktopsrc/agents/index.ts- Agent registry and initializationsrc/services/conversationService.ts- Chat conversation managementsrc/services/llmService.ts- LLM provider abstraction layer
ToolManager (src/agents/toolManager/) - MCP Entry Point (Two-Tool Architecture)
getTools: Discovery - returns tool schemas for requested agents/toolsuseTools: Execution - unified context-first tool execution- Only these 2 tools are exposed to Codex Desktop. All other agents work internally.
Tool names below are the CLI form (
agent tool-name, kebab-case) — what a caller actually types and whatgetTools/useToolsresolve. Verify againstsrc/agents/**(slug:) before trusting; the/nexus-releaseskill gates on refreshing this list, andtests/unit/shippedGuidanceCommands.test.tsfails when shipped docs name a tool that does not exist.
-
PromptManager (
src/agents/promptManager/) - Custom prompts and LLM integrationprompt: execute, create, get, list, update, archive, sub, list-models, generate-image, generate-audio, generate-video, check-generated-artifact- No delete — the AI gets
archive(reversible). Media generation is async:generate-*returns a job, pollcheck-generated-artifact <jobId>.
-
ContentManager (
src/agents/contentManager/) - Note reading/editing operationscontent: read, write, replace, insert, set-propertyreadrequires--start-line.replaceis pattern-anchored{path, start, end, content}— anchor TEXT, not line numbers.
-
StorageManager (
src/agents/storageManager/) - File/folder managementstorage: list, create-folder, move, copy, archive, open
-
SearchManager (
src/agents/searchManager/) - Advanced search operationssearch: content, directory, memory, query-notes
-
MemoryManager (
src/agents/memoryManager/) - Workspace/state/workflow managementmemory: create-workspace, list-workspaces, search-workspaces, load-workspace, update-workspace, archive-workspace, create-state, list-states, load-state, update-state, archive-state, run- No session tools — sessions are context fields, not tools.
memory runtriggers a workflow (--workflow-id/--workflow-name).
-
CanvasManager (
src/agents/canvasManager/) - Obsidian canvas operationscanvas: read, write, update, list
-
TaskManager (
src/agents/taskManager/) - Workspace-scoped project/task management with DAG dependenciestask: create-project, list-projects, update-project, archive-project, create, list, update, move, query, open, link-note- Note the asymmetry: project tools are suffixed (
create-project), task tools are bare (create,list,update,move,query). - Services: TaskService (business facade), DAGService (pure computation)
- Auto-loads task summary when workspace loads
agents/
[agentName]/
[agentName].ts # Main agent class extending BaseAgent
tools/ # Operation tools
[toolName].ts # File: read.ts, Class: ReadTool
services/ # Tool-specific services
services/ # Agent-level shared services
types.ts # Agent-specific types
utils/ # Agent-specific utilities
- BaseAgent (
src/agents/baseAgent.ts) - Common agent functionality - BaseTool (
src/agents/baseTool.ts) - Common tool functionality with generic types - IAgent (
src/agents/interfaces/IAgent.ts) - Agent interface contract - ITool (
src/agents/interfaces/ITool.ts) - Tool interface contract
main (current) — active worktrees:
.worktrees/fix-subagent-bugs(branch:fix/subagent-bugs) — pending manual test
| # | Title | Status |
|---|---|---|
| #29 | OpenAI CORS bypass + validation probe fixes | Merged ✅ |
| #23 | Plugin store compliance audit | Awaiting manual test in Obsidian before merge |
| #24 | Socket lifecycle fix (DylanLacey) | Transport fix in main (v4.3.2); mux awaiting contributor socket path fix |
PR #23 — plugin store compliance (fix/plugin-store-audit-fixes): ready to merge, needs manual test
PR (untracked) — subagent fixes (fix/subagent-bugs): 29 fixes, 372 tests passing, awaiting manual test
A branch IS a conversation with parent metadata:
metadata.parentConversationId: parent conversationmetadata.parentMessageId: message the branch is attached tometadata.branchType: 'alternative' | 'subagent'
Key Files:
src/services/chat/BranchService.ts- Facade over ConversationServicesrc/ui/chat/controllers/SubagentController.ts- Subagent infrastructuresrc/ui/chat/controllers/NexusLoadingController.ts- Loading overlayssrc/ui/chat/services/ContextTracker.ts- Token/cost tracking
Workspace Delete Persistence (Feb 2):
- Deleted workspaces may reappear on page reload
- Backend delete logic looks correct, may be UI cache issue
Subagent Flow (Dec 22, fixed Feb 20 in fix/subagent-bugs — awaiting manual test):
- 29 bugs fixed: icon race, retry-stuck, abort race, O(N) scan, Continue feature, EventBus instance-scoping, maxIterations enforcement, and more
- Full fix list:
docs/review/pr23-subagent-functionality-review.md
WebLLM/Nexus (Dec 20):
- Multi-turn tool continuations may crash on Apple Silicon (WebGPU issue)
- If startup hangs on "loading cache", clear site data
- Obsidian Secrets API Adoption (target: March 2026): Migrate API key storage to
SecretStorageAPI (v1.11.4+). Research:docs/preparation/obsidian-secrets-api-research.md - Port 3000 conflict:
FixedOpenRouter OAuth port changed to 3456 (commit 15576fb2). MCPServer.ts HttpTransportManager still on 3000; long-term: make configurable. - SOLID Audit:
SystemPromptBuilder.tsandModelAgentManager.tsare large files - SQLiteCacheManager.ts (849 lines): Above 600-line threshold
- v5.0.0 Deprecation Cleanup: Remove backward compatibility for old dedicated agent structures (TODO(v5.0.0) in WorkspacePromptResolver)
- Obsidian CLI Integration (blocked: Catalyst-only): Research:
docs/preparation/obsidian-cli-research.md - Missing
version-bump.mjs:package.jsonversionlifecycle script referencesnode version-bump.mjsbut file doesn't exist —npm versionwill error. Either create it or remove the script reference.
Status: All major issues addressed (Dec 2025). isDesktopOnly: false is correct (chat works on mobile, MCP requires desktop).
| Issue | Status |
|---|---|
| innerHTML security | XSS fix applied (MessageEditController); 11 safe patterns remain |
| registerDomEvent | Complete (15 more migrated Feb 20) |
| console.log cleanup | 398 → 1 (ImageGenerationService.ts:55 pending) |
| Inline styles | 85 → 13 |
Type safety (as any) |
0 remaining (all 12 removed Feb 20) |
@ts-ignore |
1 remaining (documented) |
npm run dev- Development build with watch modenpm run build- Production build (TypeScript + esbuild)npm run test- Run Jest test suitenpm run lint- Run ESLintnpm run deploy- Build and deploy via PowerShell script
- Unit Tests: Jest for core logic and services (619 tests total — 351 baseline + 268 OAuth/Codex/nodeFetch)
- Integration Tests: Manual testing in Obsidian environment
- MCP Testing: Via Codex Desktop connection
- Agents: Extend
BaseAgent, register tools in constructor - Tools: Extend
BaseTool<Params, Result>, implementexecute(),getParameterSchema(),getResultSchema() - Results: Return
{ success: boolean, ...data }or{ success: false, error: string } - Services: Singletons with dependency injection via constructor
See package.json. Key: MCP SDK, LLM provider SDKs (Anthropic, OpenAI, Google, Groq), express, winston, uuid.
Key reductions: HybridStorageAdapter (-72%), LLMService (-75%), ChatService (-60%), LLMProviderModal (-82%), MessageBubble (-45%), EmbeddingService (-81% facade), MemorySearchProcessor (-33%), IndexingQueue (-40%)
ChatSettingsModal (702), ChatView (659), OpenRouterAdapter (640), ValidationService (625), BatchExecutePromptTool (618), GoogleAdapter (612)
- Server runs locally via
connector.js - Configured in Codex Desktop's
claude_desktop_config.json - Server identifier:
claudesidian-mcp-[vault-name] - Supports multiple vault instances simultaneously
Instead of 50+ tools, MCP exposes just 2: getTools (discovery) and useTools (execution).
Context Schema: { workspaceId, sessionId, memory, goal, constraints? } - all required except constraints.
Flow: getTools → get schemas → useTools with the context fields at the top level plus a single tool string.
calls: [{agent, tool, params}] array and the nested context: {...} object were removed in v5.9.0 and are rejected outright (Deprecated payload shape).
Benefits: 95% token reduction (~15,000 → ~500), works with small context models.
Key Files: src/agents/toolManager/ (agent + tools), src/services/trace/ToolCallTraceService.ts
Tool Count: 60 tools across 9 agents (not counting ToolManager meta-tools)
Primary synced event store: settings-derived vault root, settings.storage.rootPath (default Nexus) with managed data under <rootPath>/data/:
conversations/<conversationId>/shard-*.jsonl- sharded append-only conversation eventsworkspaces/<workspaceId>/shard-*.jsonl- sharded append-only workspace/session/state/trace eventstasks/<workspaceId>/shard-*.jsonl- sharded append-only task/project events_meta/- storage and migration manifests
Configured root rules: resolve with resolveVaultRoot(settings, { configDir }); never hardcode Nexus except as DEFAULT_STORAGE_SETTINGS.rootPath, and never hardcode .nexus for new writes.
Legacy read paths: .obsidian/plugins/<plugin-folder>/data/, compatibility plugin folders (nexus, claudesidian-mcp), legacy .nexus/, and storage.previousRootPaths remain read/migration fallback sources. They are not the primary write target.
Local-only cache (auto-rebuilt from JSONL, never source of truth):
- Desktop: IndexedDB-backed via
IndexedDBCacheBlobStore - Mobile:
vault.adapterfile backend at.obsidian/plugins/<plugin-folder>/data/cache.db
- Hybrid JSONL + SQLite: sharded JSONL event store = source of truth, SQLite = rebuildable fast query/vector cache
- True database pagination with OFFSET/LIMIT
- Workspace-scoped sessions and traces
- Searchable via MemoryManager and SearchManager agents
- Chat View:
src/components/ChatView.ts- conversations, branching, streaming, tool accordion - Settings:
src/components/ConfigModal.ts- tabbed LLM/agent configuration
| Trigger | Purpose |
|---|---|
/ |
Tool hints |
@ |
Custom agents |
[[ |
Note links |
# |
Workspace data |
Key files: src/ui/chat/components/suggesters/, MessageEnhancer.ts, SystemPromptBuilder.ts
- Subagents: Branch → stream via LLMService → save result.
chunk.toolCallsare display-only. - WebLLM/Nexus: Nexus Quark (4B, 4K context),
<tool_call>format. May crash on Apple Silicon. - Storage: Branches as JSONL events, SQLite v4 schema, tool names use
agent_toolformat. - Apps & Vault Access: App agents now receive the full Obsidian
AppviaBaseAppAgent.setApp(), which also wires inVault. UsegetVault()for file writes andgetApp()when a tool needs workspace, command, or Web Viewer access. Usevault.createBinary()for binary outputs (audio, images, PDFs) andvault.create()for text files. Always ensure parent directories exist before writing. Follow the patterns established by ElevenLabs audio tools and thewebToolsapp. For generated artifacts where the caller must choose the destination, prefer a requiredoutputPathparameter.
- Resume:
Codex --resume f2266b8f-b5ca-4b90-a87e-db0a0304ffd7 - Team:
pact-f2266b8f - Started: 2026-03-08 11:52:33 UTC