diff --git a/main.js b/main.js index ba5bacd..35bef4d 100644 --- a/main.js +++ b/main.js @@ -3180,7 +3180,7 @@ Do not make up facts.`, description: "Create, modify, and organize notes with full editing tools.", systemPrompt: `You are an assistant that helps edit and manage notes in the user's Obsidian vault. You have full access to reading, searching, creating, appending, renaming, moving, and deleting notes. -IMPORTANT: When you need vault information, do NOT say you will search or read files. You MUST immediately emit a tool_call to the appropriate tool. +CRITICAL RULE: When you need to take action, you MUST immediately emit the tool_call(s). Do NOT output text like "Let me search..." or "I will..." or "Now I...". Either emit the tool calls immediately, or provide the final answer. Only respond to the user after you have received and analyzed the tool results. When editing notes: - Prefer modifying existing content over creating duplicates. @@ -3196,9 +3196,8 @@ When editing notes: label: "Organize", description: "Tag, rename, move, and link notes to keep the vault tidy.", systemPrompt: `You are an assistant that helps organize the user's Obsidian vault. -You can search notes, read them, list all vault tags, get vault structure stats, update frontmatter tags, rename files, move files to folders, and insert wiki-links. -IMPORTANT: When the user asks about tags or folder structure, use list_vault_tags or get_vault_stats FIRST instead of searching blindly. -When you need vault information, do NOT say you will search or read files. You MUST immediately emit a tool_call to the appropriate tool. +You can search notes, read them, update frontmatter tags, rename files, move files to folders, and insert wiki-links. +CRITICAL RULE: When you need to take action, you MUST immediately emit the tool_call(s). Do NOT output text like "Let me search..." or "I will..." or "Now I...". Either emit the tool calls immediately, or provide the final answer. Only respond to the user after you have received and analyzed the tool results. When organizing: - Suggest consistent tag vocabularies. @@ -3214,8 +3213,7 @@ When organizing: description: "Deep vault search and synthesis across multiple notes.", systemPrompt: `You are a research assistant that dives deep into the user's Obsidian vault. Your job is to synthesize information across multiple notes, find connections, and produce comprehensive summaries. -IMPORTANT: When the user asks about tags or vault structure, use list_vault_tags or get_vault_stats FIRST before searching. -When you need vault information, do NOT say you will search or read files. You MUST immediately emit a tool_call to search_vault_files or read_vault_file. +CRITICAL RULE: When you need vault information, you MUST immediately emit the tool_call(s). Do NOT output text like "Let me search..." or "I will..." or "Now I...". Either emit the tool calls immediately, or provide the final answer. Only respond to the user after you have received and analyzed the tool results. Search broadly, read key sources, and cross-reference information. Cite specific notes and quotes where possible. @@ -11285,13 +11283,13 @@ var ChatView = class extends import_obsidian5.ItemView { type: "function", function: { name: "search_vault_files", - description: "Searches for files in the vault that match a given query", + description: "Searches for files in the vault by content, tags, titles, and headings using a given query. Returns matching file metadata including paths, titles, and tags.", parameters: { type: "object", properties: { query: { type: "string", - description: "The search query to use" + description: "The search query to use (can be a keyword, tag, or phrase)" }, limit: { type: "number", @@ -11893,6 +11891,60 @@ ${actualMessage}` : actualMessage; assistantMessageId ); } + const toolCapableModes = ["edit", "organize", "research"]; + if (toolCalls.length === 0 && toolCapableModes.includes(this.currentAgentMode) && fullResponse.trim().length > 0) { + const intentPhrases = [ + "let me", + "i will", + "i need to", + "search for", + "find", + "read", + "explore", + "look for", + "check", + "move", + "rename", + "create" + ]; + const lowerResponse = fullResponse.toLowerCase(); + const seemsToWantTools = intentPhrases.some((p) => lowerResponse.includes(p)); + if (seemsToWantTools) { + const nudgeMessages = [ + ...messagesWithMemory, + { role: "assistant", content: fullResponse }, + { + role: "user", + content: "You indicated you would take action but did not emit any tool_calls. Please emit the required tool_calls now. Do not output explanatory text." + } + ]; + const nudgeStream = activeClient.streamChat(nudgeMessages, tools); + fullResponse = ""; + toolCalls = []; + for await (const chunk of nudgeStream) { + if (chunk.content) { + fullResponse += chunk.content; + this.updateMessageById(assistantMessageId, { + content: fullResponse, + isStreaming: true, + isThinking: false + }); + } + if (chunk.tool_calls) { + toolCalls = [...toolCalls, ...chunk.tool_calls]; + } + } + if (toolCalls.length > 0) { + await this.processToolCalls( + toolCalls, + nudgeMessages, + tools, + fullResponse, + assistantMessageId + ); + } + } + } if (toolCalls.length === 0) { this.updateMessageById(assistantMessageId, { content: fullResponse, diff --git a/src/agent-modes.ts b/src/agent-modes.ts index 711125b..461e872 100644 --- a/src/agent-modes.ts +++ b/src/agent-modes.ts @@ -86,7 +86,7 @@ Do not make up facts.`, description: 'Create, modify, and organize notes with full editing tools.', systemPrompt: `You are an assistant that helps edit and manage notes in the user's Obsidian vault. You have full access to reading, searching, creating, appending, renaming, moving, and deleting notes. -IMPORTANT: When you need vault information, do NOT say you will search or read files. You MUST immediately emit a tool_call to the appropriate tool. +CRITICAL RULE: When you need to take action, you MUST immediately emit the tool_call(s). Do NOT output text like "Let me search..." or "I will..." or "Now I...". Either emit the tool calls immediately, or provide the final answer. Only respond to the user after you have received and analyzed the tool results. When editing notes: - Prefer modifying existing content over creating duplicates. @@ -103,9 +103,8 @@ When editing notes: label: 'Organize', description: 'Tag, rename, move, and link notes to keep the vault tidy.', systemPrompt: `You are an assistant that helps organize the user's Obsidian vault. -You can search notes, read them, list all vault tags, get vault structure stats, update frontmatter tags, rename files, move files to folders, and insert wiki-links. -IMPORTANT: When the user asks about tags or folder structure, use list_vault_tags or get_vault_stats FIRST instead of searching blindly. -When you need vault information, do NOT say you will search or read files. You MUST immediately emit a tool_call to the appropriate tool. +You can search notes, read them, update frontmatter tags, rename files, move files to folders, and insert wiki-links. +CRITICAL RULE: When you need to take action, you MUST immediately emit the tool_call(s). Do NOT output text like "Let me search..." or "I will..." or "Now I...". Either emit the tool calls immediately, or provide the final answer. Only respond to the user after you have received and analyzed the tool results. When organizing: - Suggest consistent tag vocabularies. @@ -122,8 +121,7 @@ When organizing: description: 'Deep vault search and synthesis across multiple notes.', systemPrompt: `You are a research assistant that dives deep into the user's Obsidian vault. Your job is to synthesize information across multiple notes, find connections, and produce comprehensive summaries. -IMPORTANT: When the user asks about tags or vault structure, use list_vault_tags or get_vault_stats FIRST before searching. -When you need vault information, do NOT say you will search or read files. You MUST immediately emit a tool_call to search_vault_files or read_vault_file. +CRITICAL RULE: When you need vault information, you MUST immediately emit the tool_call(s). Do NOT output text like "Let me search..." or "I will..." or "Now I...". Either emit the tool calls immediately, or provide the final answer. Only respond to the user after you have received and analyzed the tool results. Search broadly, read key sources, and cross-reference information. Cite specific notes and quotes where possible. diff --git a/src/chat-view.ts b/src/chat-view.ts index d8ab47b..4e4d2be 100755 --- a/src/chat-view.ts +++ b/src/chat-view.ts @@ -522,13 +522,14 @@ export class ChatView extends ItemView { type: 'function', function: { name: 'search_vault_files', - description: 'Searches for files in the vault that match a given query', + description: + 'Searches for files in the vault by content, tags, titles, and headings using a given query. Returns matching file metadata including paths, titles, and tags.', parameters: { type: 'object', properties: { query: { type: 'string', - description: 'The search query to use', + description: 'The search query to use (can be a keyword, tag, or phrase)', }, limit: { type: 'number', @@ -1234,6 +1235,67 @@ export class ChatView extends ItemView { ); } + // Auto-nudge for tool-capable modes if assistant didn't emit tools but seems to intend to + const toolCapableModes: AgentMode[] = ['edit', 'organize', 'research']; + if ( + toolCalls.length === 0 && + toolCapableModes.includes(this.currentAgentMode) && + fullResponse.trim().length > 0 + ) { + const intentPhrases = [ + 'let me', + 'i will', + 'i need to', + 'search for', + 'find', + 'read', + 'explore', + 'look for', + 'check', + 'move', + 'rename', + 'create', + ]; + const lowerResponse = fullResponse.toLowerCase(); + const seemsToWantTools = intentPhrases.some((p) => lowerResponse.includes(p)); + if (seemsToWantTools) { + const nudgeMessages: OllamaMessage[] = [ + ...messagesWithMemory, + { role: 'assistant', content: fullResponse }, + { + role: 'user', + content: + 'You indicated you would take action but did not emit any tool_calls. Please emit the required tool_calls now. Do not output explanatory text.', + }, + ]; + const nudgeStream = activeClient.streamChat(nudgeMessages, tools); + fullResponse = ''; + toolCalls = []; + for await (const chunk of nudgeStream) { + if (chunk.content) { + fullResponse += chunk.content; + this.updateMessageById(assistantMessageId, { + content: fullResponse, + isStreaming: true, + isThinking: false, + }); + } + if (chunk.tool_calls) { + toolCalls = [...toolCalls, ...chunk.tool_calls]; + } + } + if (toolCalls.length > 0) { + await this.processToolCalls( + toolCalls, + nudgeMessages, + tools, + fullResponse, + assistantMessageId + ); + } + } + } + // Update assistant message immutably — only if no tool calls were processed if (toolCalls.length === 0) { this.updateMessageById(assistantMessageId, {