createBuiltInTools function

List<AiTool> createBuiltInTools(
  1. BuiltInToolHandlers handlers
)

Creates the standard set of built-in tools that work on any Flutter app.

These tools are auto-registered and allow the LLM to interact with the app's UI via the Semantics tree.

Implementation

List<AiTool> createBuiltInTools(BuiltInToolHandlers handlers) {
  return [
    // --- tap_element ---
    AiTool(
      name: 'tap_element',
      description:
          'Tap a button, link, or interactive element on screen by its label text. '
          'If multiple elements share the same label, use parentContext to specify which one '
          '(e.g., the section or nearby heading). '
          'Do NOT use for adjusting quantities — use increase_value/decrease_value for +/- steppers.',
      parameters: {
        'label': const ToolParameter(
          type: 'string',
          description: 'The visible text label of the element to tap.',
        ),
        'parentContext': const ToolParameter(
          type: 'string',
          description:
              'Optional: the ITEM NAME or PRIMARY TITLE near the element to disambiguate '
              'when multiple elements share the same label. Use the product name, heading, '
              'or main title — NOT prices, discounts, or badges. '
              'Example: for "ADD" next to "Organic Milk", use parentContext="Organic Milk".',
        ),
      },
      required: const ['label'],
      handler: (args) => handlers.onTap(
        args['label'] as String,
        parentContext: args['parentContext'] as String?,
      ),
    ),

    // --- set_text ---
    AiTool(
      name: 'set_text',
      description:
          'Enter text into a text field (search bar, input box, form field). '
          'This clears any existing text and replaces it with the new value. '
          'IMPORTANT: You do NOT need to see a text field in the screen content to use this tool. '
          'The tool automatically finds text fields even if they are hidden, unfocused, inside '
          'app bars, or not yet loaded. It will tap to activate search bars, wait for async fields '
          'to appear, and find text inputs by type even if the label does not match exactly. '
          'When you need to type into a search bar, ALWAYS call this tool — do not skip it because '
          'you cannot see a text field in the screen content.',
      parameters: {
        'label': const ToolParameter(
          type: 'string',
          description:
              'The label, hint, or placeholder text of the text field.',
        ),
        'text': const ToolParameter(
          type: 'string',
          description: 'The text to enter into the field.',
        ),
        'parentContext': const ToolParameter(
          type: 'string',
          description:
              'Optional: a nearby heading or section label to disambiguate.',
        ),
      },
      required: const ['label', 'text'],
      handler: (args) => handlers.onSetText(
        args['label'] as String,
        args['text'] as String,
        parentContext: args['parentContext'] as String?,
      ),
    ),

    // --- scroll ---
    AiTool(
      name: 'scroll',
      description:
          'Scroll the current scrollable area. Use this when you need to find '
          'elements that are off-screen or to see more content.',
      parameters: {
        'direction': const ToolParameter(
          type: 'string',
          description: 'The direction to scroll.',
          enumValues: ['up', 'down', 'left', 'right'],
        ),
      },
      required: const ['direction'],
      handler: (args) => handlers.onScroll(args['direction'] as String),
    ),

    // --- navigate_to_route ---
    AiTool(
      name: 'navigate_to_route',
      description:
          'Navigate to a different screen/route by its exact route name. '
          'Use the route names EXACTLY as listed in the ALL APP SCREENS / ALL ROUTES section of your context. '
          'Do NOT add a leading "/" unless the route name already has one.',
      parameters: {
        'routeName': const ToolParameter(
          type: 'string',
          description:
              'The exact route name to navigate to, as listed in the available routes '
              '(e.g., "Dashboard", "Dipendenti", "Impostazioni").',
        ),
      },
      required: const ['routeName'],
      handler: (args) => handlers.onNavigate(args['routeName'] as String),
    ),

    // --- go_back ---
    AiTool(
      name: 'go_back',
      description:
          'Go back to the previous screen (pop the current route). '
          'Equivalent to pressing the back button.',
      parameters: const {},
      required: const [],
      handler: (_) => handlers.onGoBack(),
    ),

    // --- get_screen_content ---
    AiTool(
      name: 'get_screen_content',
      description:
          'Re-read the current screen to get a fresh view of what is visible. '
          'Use this after performing actions to see the updated UI, or when '
          'you need to check what is currently on screen.',
      parameters: const {},
      required: const [],
      handler: (_) => handlers.onGetScreenContent(),
    ),

    // --- long_press_element ---
    AiTool(
      name: 'long_press_element',
      description:
          'Long press an element on screen. Some elements show context menus '
          'or additional options when long pressed.',
      parameters: {
        'label': const ToolParameter(
          type: 'string',
          description: 'The visible text label of the element to long press.',
        ),
        'parentContext': const ToolParameter(
          type: 'string',
          description:
              'Optional: a nearby heading or section label to disambiguate.',
        ),
      },
      required: const ['label'],
      handler: (args) => handlers.onLongPress(
        args['label'] as String,
        parentContext: args['parentContext'] as String?,
      ),
    ),

    // --- increase_value ---
    AiTool(
      name: 'increase_value',
      description:
          'Increase the numeric value of a QUANTITY STEPPER or SLIDER. '
          'The label should identify the stepper/slider — typically "quantity", '
          '"Increase quantity", or the product name next to +/- buttons. '
          'Do NOT pass button labels like "Add to cart" or "Buy" — use tap_element for those.',
      parameters: {
        'label': const ToolParameter(
          type: 'string',
          description:
              'The label of the quantity stepper or slider to increase '
              '(e.g., "quantity", "Increase quantity", or the product name).',
        ),
      },
      required: const ['label'],
      handler: (args) => handlers.onIncrease(args['label'] as String),
    ),

    // --- decrease_value ---
    AiTool(
      name: 'decrease_value',
      description:
          'Decrease the numeric value of a QUANTITY STEPPER or SLIDER. '
          'The label should identify the stepper/slider — typically "quantity", '
          '"Decrease quantity", or the product name next to +/- buttons. '
          'Do NOT pass button labels like "Remove" or "Delete" — use tap_element for those.',
      parameters: {
        'label': const ToolParameter(
          type: 'string',
          description:
              'The label of the quantity stepper or slider to decrease '
              '(e.g., "quantity", "Decrease quantity", or the product name).',
        ),
      },
      required: const ['label'],
      handler: (args) => handlers.onDecrease(args['label'] as String),
    ),

    // --- ask_user ---
    AiTool(
      name: 'ask_user',
      description:
          'LAST RESORT: Ask the user a question and wait for their response. '
          'Use ONLY when: (1) there are multiple options with genuinely different consequences '
          '(different prices, different destinations) that the user has not specified, or '
          '(2) critical information is truly missing and cannot be inferred from context. '
          'Do NOT use to confirm actions the user already requested. '
          'Do NOT use when the app structure makes the answer obvious. '
          'When presenting options, list ALL with full details (name, price).',
      parameters: {
        'question': const ToolParameter(
          type: 'string',
          description:
              'A clear, specific question. When presenting options, list each with details '
              '(e.g. "1) Option A — 100  2) Option B — 150. Which one?").',
        ),
      },
      required: const ['question'],
      handler: (args) async {
        final question = args['question'] as String;
        final response = await handlers.onAskUser(question);
        return {
          'yourQuestion': question,
          'userResponse': response,
          'instruction':
              'The user responded to your question. '
              'If their response ANSWERS your question → continue with the task. '
              'If their response is a COMPLETELY DIFFERENT REQUEST '
              '(unrelated to your question) → ABANDON your current task '
              'and handle their new request instead. '
              'NEVER repeat the same question — if they didn\'t answer it, they don\'t want to.',
        };
      },
    ),

    // --- hand_off_to_user ---
    if (handlers.onHandoff != null)
      AiTool(
        name: 'hand_off_to_user',
        description:
            'Hand control to the user for the FINAL irreversible action. '
            'Use this ONLY when you have completed ALL preparatory steps and the '
            'final action button (Book Ride, Place Order, Confirm Payment, Submit, etc.) '
            'is visible on screen. The overlay will clear so the user can see the full app '
            'screen with all details and tap the button themselves. '
            'Do NOT use for intermediate steps or information queries.',
        parameters: {
          'button_label': const ToolParameter(
            type: 'string',
            description:
                'The EXACT label of the final action button the user should tap '
                '(e.g., "Book Ride", "Place Order", "Confirm Payment").',
          ),
          'summary': const ToolParameter(
            type: 'string',
            description:
                'Brief description of what will happen when the user taps the button '
                '(e.g., "Your order will be placed and payment of ₹150 will be charged").',
          ),
        },
        required: const ['button_label', 'summary'],
        handler: (args) async {
          final buttonLabel = args['button_label'] as String;
          final summary = args['summary'] as String;
          final result = await handlers.onHandoff!(buttonLabel, summary);
          return {
            'handoffResult': result,
            'instruction':
                'The user has acted. Call get_screen_content to see '
                'the current screen and report the outcome to the user.',
          };
        },
      ),
  ];
}