{"version":3,"file":"agent/runner/loop-executor.mjs","sources":["webpack://@multimodal/agent/./src/agent/runner/loop-executor.ts"],"sourcesContent":["/*\n * Copyright (c) 2025 Bytedance, Inc. and its affiliates.\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { AgentEventStream, ToolCallEngine } from '@multimodal/agent-interface';\nimport { getLogger } from '../../utils/logger';\nimport { ResolvedModel } from '@multimodal/model-provider';\nimport { LLMProcessor } from './llm-processor';\nimport type { Agent } from '../agent';\n\n/**\n * LoopExecutor - Responsible for executing the agent's reasoning loop\n *\n * This class manages the core loop of the agent's reasoning process,\n * driving the interaction between the LLM, tools, and events.\n */\nexport class LoopExecutor {\n  private logger = getLogger('LoopExecutor');\n  private currentIteration = 1;\n\n  constructor(\n    private agent: Agent,\n    private llmProcessor: LLMProcessor,\n    private eventStream: AgentEventStream.Processor,\n    private instructions: string,\n    private maxIterations: number,\n  ) {}\n\n  /**\n   * Get the current iteration/loop number\n   * @returns The current loop iteration (1-based)\n   */\n  getCurrentIteration(): number {\n    return this.currentIteration;\n  }\n\n  /**\n   * Executes the full reasoning loop until completion or max iterations\n   *\n   * @param resolvedModel The resolved model configuration\n   * @param sessionId Session identifier\n   * @param toolCallEngine The tool call engine to use\n   * @param streamingMode Whether to operate in streaming mode\n   * @param abortSignal Optional signal to abort the execution\n   * @returns The final assistant message event\n   */\n  async executeLoop(\n    resolvedModel: ResolvedModel,\n    sessionId: string,\n    toolCallEngine: ToolCallEngine,\n    streamingMode = false,\n    abortSignal?: AbortSignal,\n  ): Promise<AgentEventStream.AssistantMessageEvent> {\n    let finalEvent: AgentEventStream.AssistantMessageEvent | null = null;\n\n    // Reset the current iteration to 1 at the start\n    this.currentIteration = 1;\n\n    for (let iteration = 1; iteration < this.maxIterations; iteration++) {\n      // Check if operation was aborted\n      if (abortSignal?.aborted) {\n        this.logger.info(`[Iteration] Aborted at iteration ${iteration}/${this.maxIterations}`);\n\n        // Add system event for aborted execution\n        const systemEvent = this.eventStream.createEvent('system', {\n          level: 'warning',\n          message: 'Execution aborted',\n        });\n        this.eventStream.sendEvent(systemEvent);\n\n        // Create final event for aborted execution with a unique messageId\n        const abortMessageId = `msg_abort_${Date.now()}_${Math.random().toString(36).substring(2, 10)}`;\n        finalEvent = this.eventStream.createEvent('assistant_message', {\n          content: 'Request was aborted',\n          finishReason: 'abort',\n          messageId: abortMessageId,\n        });\n\n        this.eventStream.sendEvent(finalEvent);\n        break;\n      }\n\n      // Check if higher-level agent requested termination\n      if (this.agent.isLoopTerminationRequested()) {\n        this.logger.info(\n          `[Iteration] Terminated at iteration ${iteration}/${this.maxIterations} due to higher-level agent request`,\n        );\n\n        // FIXME: add it back\n\n        // Create system event for terminated execution\n        // const systemEvent = this.eventStream.createEvent('system', {\n        //   level: 'info',\n        //   message: 'Execution terminated by higher-level agent',\n        // });\n        // this.eventStream.sendEvent(systemEvent);\n\n        // If we already have a final event, use it\n        if (finalEvent !== null) {\n          // No need to modify iteration count as it's already done below\n          break;\n        }\n\n        // Create final event for terminated execution with a unique messageId\n        const terminationMessageId = `msg_termination_${Date.now()}_${Math.random().toString(36).substring(2, 10)}`;\n        finalEvent = this.eventStream.createEvent('assistant_message', {\n          content: 'Aggent TARS is finished',\n          finishReason: 'stop',\n          messageId: terminationMessageId,\n        });\n\n        this.eventStream.sendEvent(finalEvent);\n        break;\n      }\n\n      if (finalEvent !== null) {\n        // Call hook to check if loop should actually terminate\n        try {\n          const terminationResult = await Promise.resolve(\n            this.agent.onBeforeLoopTermination(sessionId, finalEvent),\n          );\n\n          if (terminationResult.finished) {\n            // Higher-level agent allowed termination, exit the loop\n            this.logger.info(`[Agent] Loop termination approved by higher-level agent`);\n            break;\n          } else {\n            // Higher-level agent prevented termination, continue the loop\n            this.logger.info(\n              `[Agent] Loop termination prevented by higher-level agent: ${terminationResult.message || 'No reason provided'}`,\n            );\n\n            // Add system event to indicate continuation\n            const continueEvent = this.eventStream.createEvent('system', {\n              level: 'info',\n              message: `Loop continuation requested: ${terminationResult.message || 'No reason provided'}`,\n            });\n            this.eventStream.sendEvent(continueEvent);\n\n            // Reset finalEvent to continue the loop\n            finalEvent = null;\n          }\n        } catch (error) {\n          // If hook throws an error, log it and allow termination by default\n          this.logger.error(`[Agent] Error in onBeforeLoopTermination hook: ${error}`);\n          break;\n        }\n      }\n\n      this.logger.info(`[Iteration] ${iteration}/${this.maxIterations} started`);\n      // Update current iteration\n      this.currentIteration = iteration;\n\n      // Process the current iteration\n      await this.llmProcessor.processRequest(\n        resolvedModel,\n        this.instructions,\n        toolCallEngine,\n        sessionId,\n        streamingMode,\n        iteration,\n        abortSignal,\n      );\n\n      // Check if we've reached a final answer\n      const assistantEvents = this.eventStream.getEventsByType(['assistant_message']);\n      if (assistantEvents.length > 0) {\n        const latestAssistantEvent = assistantEvents[\n          assistantEvents.length - 1\n        ] as AgentEventStream.AssistantMessageEvent;\n\n        if (!latestAssistantEvent.toolCalls || latestAssistantEvent.toolCalls.length === 0) {\n          finalEvent = latestAssistantEvent;\n          const contentLength = latestAssistantEvent.content?.length || 0;\n          this.logger.info(`[LLM] Text response received | Length: ${contentLength} characters`);\n          this.logger.info(`[Agent] Final answer received`);\n        }\n      }\n\n      this.logger.info(`[Iteration] ${iteration}/${this.maxIterations} completed`);\n    }\n\n    // Handle case where max iterations is reached without resolution\n    if (finalEvent === null) {\n      this.logger.warn(\n        `[Agent] Maximum iterations reached (${this.maxIterations}), forcing termination`,\n      );\n      const errorMsg = 'Sorry, I could not complete this task. Maximum iterations reached.';\n\n      // Add system event for max iterations\n      const systemEvent = this.eventStream.createEvent('system', {\n        level: 'warning',\n        message: `Maximum iterations reached (${this.maxIterations}), forcing termination`,\n      });\n      this.eventStream.sendEvent(systemEvent);\n\n      // Add final assistant message event with a unique messageId\n      const maxIterMessageId = `msg_maxiter_${Date.now()}_${Math.random().toString(36).substring(2, 10)}`;\n      finalEvent = this.eventStream.createEvent('assistant_message', {\n        content: errorMsg,\n        finishReason: 'max_iterations',\n        messageId: maxIterMessageId,\n      });\n\n      this.eventStream.sendEvent(finalEvent);\n    }\n\n    this.logger.info(\n      `[Loop] Execution completed | SessionId: \"${sessionId}\" | ` +\n        `Iterations: ${this.currentIteration}/${this.maxIterations}`,\n    );\n\n    return finalEvent;\n  }\n}\n"],"names":["LoopExecutor","resolvedModel","sessionId","toolCallEngine","streamingMode","abortSignal","finalEvent","iteration","systemEvent","abortMessageId","Date","Math","terminationMessageId","terminationResult","Promise","continueEvent","error","assistantEvents","latestAssistantEvent","_latestAssistantEvent_content","contentLength","errorMsg","maxIterMessageId","agent","llmProcessor","eventStream","instructions","maxIterations","getLogger"],"mappings":"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"}