runToolUse function

Stream<MessageUpdate> runToolUse(
  1. ToolUseBlock toolUse,
  2. AssistantMessage assistantMessage,
  3. CanUseToolFn canUseTool,
  4. ToolUseContext toolUseContext,
)

Run a single tool use block — checks permissions and executes the tool.

Implementation

Stream<MessageUpdate> runToolUse(
  ToolUseBlock toolUse,
  AssistantMessage assistantMessage,
  CanUseToolFn canUseTool,
  ToolUseContext toolUseContext,
) async* {
  final toolName = toolUse.name;

  // First try available tools
  var tool = findToolByName(toolUseContext.tools, toolName);

  // Fallback for deprecated aliases
  if (tool == null) {
    // Would search all base tools — simplified for port
  }

  if (tool == null) {
    yield MessageUpdate(
      message: ToolMessage(
        type: 'user',
        toolResults: [
          ToolResultBlock(
            toolUseId: toolUse.id,
            content:
                '<tool_use_error>Error: No such tool available: $toolName</tool_use_error>',
            isError: true,
          ),
        ],
        toolUseResult: 'Error: No such tool available: $toolName',
        sourceToolAssistantUUID: assistantMessage.uuid,
      ),
    );
    return;
  }

  if (toolUseContext.abortController.isAborted) {
    yield MessageUpdate(
      message: ToolMessage(
        type: 'user',
        toolResults: [
          ToolResultBlock(toolUseId: toolUse.id, content: cancelMessage),
        ],
        toolUseResult: cancelMessage,
        sourceToolAssistantUUID: assistantMessage.uuid,
      ),
    );
    return;
  }

  try {
    // Check permissions
    final permissionResult = await canUseTool(
      tool,
      toolUse.input,
      toolUseContext,
      assistantMessage,
      toolUse.id,
    );

    if (permissionResult.behavior == PermissionBehavior.deny) {
      yield MessageUpdate(
        message: ToolMessage(
          type: 'user',
          toolResults: [
            ToolResultBlock(
              toolUseId: toolUse.id,
              content: permissionResult.message ?? rejectMessage,
              isError: true,
            ),
          ],
          toolUseResult: permissionResult.message ?? rejectMessage,
          sourceToolAssistantUUID: assistantMessage.uuid,
        ),
      );
      return;
    }

    // Execute the tool
    final effectiveInput = permissionResult.updatedInput ?? toolUse.input;
    final result = await tool.execute(effectiveInput, toolUseContext);

    yield MessageUpdate(message: result);
  } catch (error) {
    final errorMsg = error is Exception ? error.toString() : '$error';
    final detailedError = 'Error calling tool (${tool.name}): $errorMsg';

    yield MessageUpdate(
      message: ToolMessage(
        type: 'user',
        toolResults: [
          ToolResultBlock(
            toolUseId: toolUse.id,
            content: '<tool_use_error>$detailedError</tool_use_error>',
            isError: true,
          ),
        ],
        toolUseResult: detailedError,
        sourceToolAssistantUUID: assistantMessage.uuid,
      ),
    );
  }
}