Add automatic folder creation for file operations and improve chat session handling
Implement `ensureFolderExists` in `ToolExecutor` to create parent directories when creating, moving, or renaming notes. Update `ChatView` to properly persist agent mode and derive session titles from the first user message. Fix error handling in tool execution to return structured failures instead of null, and include failure details in follow-up messages.
This commit is contained in:
+45
-9
@@ -140,6 +140,7 @@ export class ChatView extends ItemView {
|
||||
newSettings.agentModel ?? newSettings.model,
|
||||
{ cacheConfig: newSettings.cacheConfig }
|
||||
);
|
||||
this.conversationStateManager.setSystemPrompt(getSystemPromptForMode(this.currentAgentMode));
|
||||
void this.initializeClientCaches().catch(() => {
|
||||
new Notice(
|
||||
'Semantic cache unavailable: could not connect to ChromaDB. Check the ChromaDB URL in settings.'
|
||||
@@ -318,7 +319,7 @@ export class ChatView extends ItemView {
|
||||
}
|
||||
}
|
||||
this.modeSelectorEl.addEventListener('change', () => {
|
||||
this.currentAgentMode = this.modeSelectorEl!.value as AgentMode;
|
||||
this.setAgentMode(this.modeSelectorEl!.value as AgentMode);
|
||||
});
|
||||
} else {
|
||||
newChatContainer.appendChild(this.modeSelectorEl);
|
||||
@@ -561,6 +562,10 @@ export class ChatView extends ItemView {
|
||||
this.modeSelectorEl.value = mode;
|
||||
}
|
||||
this.conversationStateManager.setSystemPrompt(getSystemPromptForMode(mode));
|
||||
const activeId = this.chatHistoryManager?.getActiveSessionId();
|
||||
if (activeId) {
|
||||
this.chatHistoryManager?.updateSession(activeId, { agentMode: mode });
|
||||
}
|
||||
}
|
||||
|
||||
clearConversation(): void {
|
||||
@@ -610,7 +615,11 @@ export class ChatView extends ItemView {
|
||||
const activeId = this.chatHistoryManager.getActiveSessionId();
|
||||
if (!activeId) return;
|
||||
const nonStreamingMessages = this.messages.filter((msg) => !msg.isStreaming);
|
||||
this.chatHistoryManager.updateSessionMessages(activeId, nonStreamingMessages);
|
||||
this.chatHistoryManager.updateSession(activeId, {
|
||||
messages: nonStreamingMessages,
|
||||
agentMode: this.currentAgentMode,
|
||||
title: this.deriveSessionTitle(nonStreamingMessages),
|
||||
});
|
||||
}
|
||||
|
||||
private syncMessagesToSession(): void {
|
||||
@@ -618,7 +627,18 @@ export class ChatView extends ItemView {
|
||||
const activeId = this.chatHistoryManager.getActiveSessionId();
|
||||
if (!activeId) return;
|
||||
const nonStreamingMessages = this.messages.filter((msg) => !msg.isStreaming);
|
||||
this.chatHistoryManager.updateSessionMessages(activeId, nonStreamingMessages);
|
||||
this.chatHistoryManager.updateSession(activeId, {
|
||||
messages: nonStreamingMessages,
|
||||
agentMode: this.currentAgentMode,
|
||||
title: this.deriveSessionTitle(nonStreamingMessages),
|
||||
});
|
||||
}
|
||||
|
||||
private deriveSessionTitle(messages: ChatMessage[]): string {
|
||||
const firstUser = messages.find((m) => m.role === 'user');
|
||||
const text = firstUser?.content.trim() ?? '';
|
||||
if (!text) return 'New Chat';
|
||||
return text.length > 40 ? text.slice(0, 40) + '…' : text;
|
||||
}
|
||||
|
||||
private populateHistoryDropdown(): void {
|
||||
@@ -1053,11 +1073,15 @@ export class ChatView extends ItemView {
|
||||
return { ...toolResult, id: action.toolCall.id };
|
||||
} catch (error) {
|
||||
ErrorHandler.handleError(error, 'ChatView.processToolCalls');
|
||||
return null;
|
||||
return {
|
||||
success: false,
|
||||
message: error instanceof Error ? error.message : String(error),
|
||||
id: action.toolCall.id,
|
||||
};
|
||||
}
|
||||
})
|
||||
)
|
||||
).filter((result): result is NonNullable<typeof result> => result !== null);
|
||||
);
|
||||
}
|
||||
|
||||
// No write tools (or they were already executed) — proceed with follow-up
|
||||
@@ -1137,13 +1161,18 @@ export class ChatView extends ItemView {
|
||||
return { ...toolResult, id: action.toolCall.id };
|
||||
} catch (error) {
|
||||
ErrorHandler.handleError(error, 'ChatView.applyPendingActions');
|
||||
return null;
|
||||
return {
|
||||
success: false,
|
||||
message: error instanceof Error ? error.message : String(error),
|
||||
id: action.toolCall.id,
|
||||
};
|
||||
}
|
||||
})
|
||||
)
|
||||
).filter((result): result is NonNullable<typeof result> => result !== null);
|
||||
);
|
||||
|
||||
const allResults = [...this.pendingReadResults, ...writeResults];
|
||||
const failedWrites = writeResults.filter((result) => !result.success);
|
||||
|
||||
const followUpMessages: OllamaMessage[] = allResults.map((result) => ({
|
||||
role: 'tool',
|
||||
@@ -1191,14 +1220,21 @@ export class ChatView extends ItemView {
|
||||
);
|
||||
} else {
|
||||
this.updateMessageById(assistantMessageId, {
|
||||
content: followUpContent || 'Actions applied successfully.',
|
||||
content:
|
||||
followUpContent ||
|
||||
(failedWrites.length > 0
|
||||
? `Some actions failed:\n${failedWrites.map((r) => `- ${r.message}`).join('\n')}`
|
||||
: 'Actions applied successfully.'),
|
||||
isStreaming: false,
|
||||
isThinking: false,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.updateMessageById(assistantMessageId, {
|
||||
content: 'Actions applied successfully.',
|
||||
content:
|
||||
failedWrites.length > 0
|
||||
? `Actions failed:\n${failedWrites.map((r) => `- ${r.message}`).join('\n')}`
|
||||
: 'Actions applied successfully.',
|
||||
isStreaming: false,
|
||||
isThinking: false,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user