diff --git a/AIClasses/FunctionDefinitions/AIFunctionDefinitions.ts b/AIClasses/FunctionDefinitions/AIFunctionDefinitions.ts index 5f04c8f..1953abb 100644 --- a/AIClasses/FunctionDefinitions/AIFunctionDefinitions.ts +++ b/AIClasses/FunctionDefinitions/AIFunctionDefinitions.ts @@ -12,10 +12,8 @@ import { SubmitPlan } from "./Functions/SubmitPlan"; import { CancelPlan } from "./Functions/CancelPlan"; import { AskUserQuestionPlanning } from "./Functions/AskUserQuestionPlanning"; import { CompletePlan } from "./Functions/CompletePlan"; -import { AskUserQuestionExecution } from "./Functions/AskUserQuestionExecution"; -import { ContinuePlanExecution } from "./Functions/ContinuePlanExecution"; import { CompleteTask } from "./Functions/CompleteTask"; -import { ExecuteWorkflow } from "./Functions/CreatePlan"; +import { ExecuteWorkflow } from "./Functions/ExecuteWorkflow"; export abstract class AIFunctionDefinitions { @@ -61,29 +59,6 @@ export abstract class AIFunctionDefinitions { return [...this.agentDefinitions(true, false), CompleteTask]; } - public static agentExecutionDefinitions() { - this.isGated = false; - return [ - SearchVaultFiles, - ReadVaultFiles, - ListVaultFiles, - WriteVaultFile, - PatchVaultFile, - DeleteVaultFiles, - MoveVaultFiles, - AskUserQuestionExecution, - CompletePlan - ]; - } - - public static gatedDefinitions() { - this.isGated = true; - return [ - ContinuePlanExecution, - CompleteStep - ]; - } - public static compactSummaryForPlanningAgent(): string { return this.definitionsList.map(definition => { // Extract first line of description as brief purpose diff --git a/AIClasses/FunctionDefinitions/Functions/CreatePlan.ts b/AIClasses/FunctionDefinitions/Functions/ExecuteWorkflow.ts similarity index 100% rename from AIClasses/FunctionDefinitions/Functions/CreatePlan.ts rename to AIClasses/FunctionDefinitions/Functions/ExecuteWorkflow.ts diff --git a/AIPrompts/IPrompt.ts b/AIPrompts/IPrompt.ts index 5f3fe8d..a449913 100644 --- a/AIPrompts/IPrompt.ts +++ b/AIPrompts/IPrompt.ts @@ -3,7 +3,7 @@ import { Services } from "Services/Services"; import { SystemInstruction } from "./SystemPrompt"; import type { FileSystemService } from "Services/FileSystemService"; import type { SettingsService } from "Services/SettingsService"; -import { PlanningAgentSystemPrompt } from "AIPrompts/PlanningAgentSystemPrompt"; +import { PlanningPrompt } from "AIPrompts/PlanningPrompt"; import { ExecutionPrompt } from "./ExecutionPrompt"; import { OrchestrationPrompt } from "./OrchestrationPrompt"; @@ -34,7 +34,7 @@ export class AIPrompt implements IPrompt { } public planningInstruction(): string { - return PlanningAgentSystemPrompt; + return PlanningPrompt; } public executionInstruction(): string { diff --git a/AIPrompts/PlanningAgentSystemPrompt.ts b/AIPrompts/PlanningPrompt.ts similarity index 99% rename from AIPrompts/PlanningAgentSystemPrompt.ts rename to AIPrompts/PlanningPrompt.ts index d214ac7..2cb1c43 100644 --- a/AIPrompts/PlanningAgentSystemPrompt.ts +++ b/AIPrompts/PlanningPrompt.ts @@ -1,6 +1,6 @@ import { AIFunctionDefinitions } from "AIClasses/FunctionDefinitions/AIFunctionDefinitions"; -export const PlanningAgentSystemPrompt: string = ` +export const PlanningPrompt: string = ` # Obsidian Vault Planning Agent You are a specialized planning agent within a multi-agent Obsidian vault assistant system. Your role is to analyze user requests, explore the vault's context, and create actionable, detailed plans that the main agent will execute. diff --git a/Services/AIServices/AIController.ts b/Services/AIServices/AIController.ts index 3e2de1f..fcfae90 100644 --- a/Services/AIServices/AIController.ts +++ b/Services/AIServices/AIController.ts @@ -45,7 +45,7 @@ export class AIController { this.debugService?.log("AgentLoop", `Starting ${agentType} agent loop`); let response = await this.streamRequestResponse(agentType, this.ensureCorrectConversationStructure(conversation), callbacks); - this.saveConversation(agentType, conversation); + await this.saveConversation(agentType, conversation); while (response.functionCall || response.shouldContinue) { if (response.functionCall) { @@ -53,7 +53,7 @@ export class AIController { const result = await handleFunctionCall(response.functionCall); if (result.shouldExit) { this.debugService?.log("AgentLoop", `${agentType} exiting loop (shouldExit: true)`); - this.saveConversation(agentType, conversation); + await this.saveConversation(agentType, conversation); return; } } else { @@ -62,7 +62,7 @@ export class AIController { response = await this.streamRequestResponse(agentType, this.ensureCorrectConversationStructure(conversation), callbacks); - this.saveConversation(agentType, conversation); + await this.saveConversation(agentType, conversation); } this.debugService?.log("AgentLoop", `${agentType} agent loop completed`); } diff --git a/Services/AIServices/AIFunctionService.ts b/Services/AIServices/AIFunctionService.ts index e9d45f8..085d490 100644 --- a/Services/AIServices/AIFunctionService.ts +++ b/Services/AIServices/AIFunctionService.ts @@ -121,11 +121,13 @@ export class AIFunctionService { return new AIFunctionResponse(functionCall.name, {}, functionCall.toolId) // multi-agent functions are handled elsewhere - this shouldn't really ever get hit - case AIFunction.CreatePlan: + case AIFunction.ExecuteWorkflow: + case AIFunction.ContinuePlanExecution: case AIFunction.Replan: case AIFunction.SubmitPlan: case AIFunction.AskUserQuestionPlanning: case AIFunction.AskUserQuestionExecution: + case AIFunction.CompleteTask: case AIFunction.CompleteStep: case AIFunction.CompletePlan: case AIFunction.CancelPlan: { diff --git a/Services/AIServices/ExecutionAgent.ts b/Services/AIServices/ExecutionAgent.ts index 18a29de..58c8fc4 100644 --- a/Services/AIServices/ExecutionAgent.ts +++ b/Services/AIServices/ExecutionAgent.ts @@ -62,7 +62,7 @@ export class ExecutionAgent extends AIController { return { shouldExit: true }; } - this.debugService?.log("ExecutionAgent", `Executing function: ${functionCallName}`); + this.debugService?.log("ExecutionAgent", `Executing function: ${functionCall.name}`); this.updateThought(functionCall, callbacks); const functionResponse = await this.aiFunctionService.performAIFunction(functionCall); this.conversation.addFunctionResponse(functionResponse); diff --git a/Services/AIServices/MainAgent.ts b/Services/AIServices/MainAgent.ts index f555a01..35164df 100644 --- a/Services/AIServices/MainAgent.ts +++ b/Services/AIServices/MainAgent.ts @@ -74,7 +74,7 @@ export class MainAgent extends AIController { return { shouldExit: true }; } - this.debugService?.log("MainAgent", `Executing function: ${functionCallName}`); + this.debugService?.log("MainAgent", `Executing function: ${functionCall.name}`); this.updateThought(functionCall, callbacks); const functionResponse = await this.aiFunctionService.performAIFunction(functionCall); conversation.addFunctionResponse(functionResponse); diff --git a/Services/AIServices/OrchestrationAgent.ts b/Services/AIServices/OrchestrationAgent.ts index a72529c..94eff5a 100644 --- a/Services/AIServices/OrchestrationAgent.ts +++ b/Services/AIServices/OrchestrationAgent.ts @@ -31,13 +31,16 @@ export class OrchestrationAgent extends AIController { const planningAgent = new PlanningAgent(); planningAgent.resolveAIProvider(); + let planCompleted = false; + let isReplan = false; while (!planCompleted) { callbacks.onPlanReset(); callbacks.onPlanningStarted(); this.debugService?.log("OrchestrationAgent", "Spawning PlanningAgent to generate execution plan"); - const executionPlan = await planningAgent.runPlanningAgent(planningConversation, callbacks, false); + const executionPlan = await planningAgent.runPlanningAgent(planningConversation, callbacks, isReplan); + callbacks.onPlanningFinished(); if (!executionPlan) { @@ -45,6 +48,7 @@ export class OrchestrationAgent extends AIController { return { message: Copy.PlanningFailedNoSteps }; } this.debugService?.log("OrchestrationAgent", `Execution plan received with ${executionPlan.executionSteps.length} steps`); + callbacks.onPlanUpdate(executionPlan); let currentStepIndex = 0; @@ -109,6 +113,7 @@ export class OrchestrationAgent extends AIController { planCompleted = true; } } + isReplan = true; } this.debugService?.log("OrchestrationAgent", "Planned workflow completed - requesting summary"); @@ -143,7 +148,7 @@ export class OrchestrationAgent extends AIController { { message: Copy.OrchestrationToolDenial }, functionCall.toolId )); - return { shouldExit: false }; + return Promise.resolve({ shouldExit: false }); } if (isAIFunction(functionCallName, AIFunction.CompleteStep)) { @@ -154,7 +159,7 @@ export class OrchestrationAgent extends AIController { { error: `Invalid arguments for ${AIFunction.CompleteStep}: ${parseResult.error.message}` }, functionCall.toolId )); - return { shouldExit: false }; + return Promise.resolve({ shouldExit: false }); } if (!parseResult.data.confirm_completion) { planningConversation.addFunctionResponse(new AIFunctionResponse( @@ -162,7 +167,7 @@ export class OrchestrationAgent extends AIController { { error: "Confirmation was false, no action taken" }, functionCall.toolId )); - return { shouldExit: false }; + return Promise.resolve({ shouldExit: false }); } this.debugService?.log("Orchestration", `CompleteStep called (confirmed: ${parseResult.data.confirm_completion})`); planningConversation.addFunctionResponse(new AIFunctionResponse( @@ -171,7 +176,7 @@ export class OrchestrationAgent extends AIController { functionCall.toolId )); orchestrationResult = new OrchestrationResult({ continue: true, continueContext: parseResult.data.context_for_next_step }); - return { shouldExit: true } + return Promise.resolve({ shouldExit: true }); } if (isAIFunction(functionCallName, AIFunction.CompletePlan)) { @@ -182,7 +187,7 @@ export class OrchestrationAgent extends AIController { { error: `Invalid arguments for ${AIFunction.CompletePlan}: ${parseResult.error.message}` }, functionCall.toolId )); - return { shouldExit: false }; + return Promise.resolve({ shouldExit: false }); } if (!parseResult.data.confirm_completion) { planningConversation.addFunctionResponse(new AIFunctionResponse( @@ -190,7 +195,7 @@ export class OrchestrationAgent extends AIController { { error: "Confirmation was false, no action taken" }, functionCall.toolId )); - return { shouldExit: false }; + return Promise.resolve({ shouldExit: false }); } this.debugService?.log("Orchestration", `CompletePlan called (confirmed: ${parseResult.data.confirm_completion})`); planningConversation.addFunctionResponse(new AIFunctionResponse( @@ -199,7 +204,7 @@ export class OrchestrationAgent extends AIController { functionCall.toolId )); orchestrationResult = new OrchestrationResult({ complete: true }); - return { shouldExit: true } + return Promise.resolve({ shouldExit: true }); } if (isAIFunction(functionCallName, AIFunction.Replan)) { @@ -210,7 +215,7 @@ export class OrchestrationAgent extends AIController { { error: `Invalid arguments for ${AIFunction.Replan}: ${parseResult.error.message}` }, functionCall.toolId )); - return { shouldExit: false }; + return Promise.resolve({ shouldExit: false }); } this.debugService?.log("Orchestration", `Replan requested: ${parseResult.data.context}`); planningConversation.addFunctionResponse(new AIFunctionResponse( @@ -219,7 +224,7 @@ export class OrchestrationAgent extends AIController { functionCall.toolId )); orchestrationResult = new OrchestrationResult({ replan: true, replanContext: parseResult.data.context }); - return { shouldExit: true } + return Promise.resolve({ shouldExit: false }); } if (isAIFunction(functionCallName, AIFunction.CancelPlan)) { @@ -230,7 +235,7 @@ export class OrchestrationAgent extends AIController { { error: `Invalid arguments for ${AIFunction.CancelPlan}: ${parseResult.error.message}` }, functionCall.toolId )); - return { shouldExit: false }; + return Promise.resolve({ shouldExit: false }); } this.debugService?.log("Orchestration", `Plan cancellation requested: ${parseResult.data.context}`); planningConversation.addFunctionResponse(new AIFunctionResponse( @@ -239,10 +244,10 @@ export class OrchestrationAgent extends AIController { functionCall.toolId )); orchestrationResult = new OrchestrationResult({ abort: true, abortContext: parseResult.data.context }); - return { shouldExit: true } + return Promise.resolve({ shouldExit: true }); } - return { shouldExit: false }; + return Promise.resolve({ shouldExit: false }); }); if (!orchestrationResult) { @@ -261,7 +266,7 @@ export class OrchestrationAgent extends AIController { return input.goal + context; } - private async setAgentPromptAndTools(): Promise { + private setAgentPromptAndTools(): void { if (!this.ai) { // this shouldn't ever happen Exception.throw("Error: No AI provider has been set!"); } diff --git a/Services/DebugService.ts b/Services/DebugService.ts index 49d9a52..b6fc765 100644 --- a/Services/DebugService.ts +++ b/Services/DebugService.ts @@ -9,7 +9,7 @@ export class DebugService { } public log(level: string, message: string): void { - console.log(`%c${level}: ${message}`, `color:${this.debugColor};`); + console.debug(`%c${level}: ${message}`, `color:${this.debugColor};`); } } \ No newline at end of file diff --git a/Services/VaultCacheService.ts b/Services/VaultCacheService.ts index 13fa232..712e82e 100644 --- a/Services/VaultCacheService.ts +++ b/Services/VaultCacheService.ts @@ -106,12 +106,9 @@ export class VaultCacheService { break; case FileEvent.Delete: - if (shouldCacheOldPath) { - this.wikiLinks.removeWikiLink(file); - this.files.delete(args.oldPath); - const orphanedTags = this.mapping.deleteFromMapping(args.oldPath); - orphanedTags.forEach(tag => this.tags.delete(tag)); - } + this.wikiLinks.removeWikiLink(file); + this.files.delete(file.path); + this.mapping.deleteFromMapping(file.path).forEach(tag => this.tags.delete(tag)); break; } this.fuzzySortPrepareTags(); @@ -134,9 +131,7 @@ export class VaultCacheService { break; case FileEvent.Delete: - if (shouldCacheOldPath) { - this.folders.delete(args.oldPath); - } + this.folders.delete(file.path); break; case FileEvent.Modify: