askTool function

AgentTool askTool({
  1. AskCallback? callback,
})

Creates the ask tool bound to callback.

When callback is null (headless/non-interactive host), executing the tool throws — the agent loop converts it into an error tool result telling the model this host cannot answer questions (the safe fallback).

The tool forces its tool-call batch to ToolExecutionMode.sequential (omp's concurrency = "exclusive"): concurrent ask calls would clobber the host's single question surface.

Implementation

AgentTool askTool({AskCallback? callback}) {
  return AgentTool(
    name: 'ask',
    label: 'ask',
    tier: ApprovalTier.read,
    executionMode: ToolExecutionMode.sequential,
    description: askToolDescriptionPrompt,
    parameters: const {
      'type': 'object',
      'properties': {
        'questions': {
          'type': 'array',
          'description': 'Questions to ask the user (at least one)',
          'items': {
            'type': 'object',
            'properties': {
              'question': {
                'type': 'string',
                'description': 'Question text shown to the user',
              },
              'options': {
                'type': 'array',
                'description':
                    'Picker options (2-5 concise, distinct options); omit '
                    'for a free-form answer',
                'items': {
                  'type': 'object',
                  'properties': {
                    'label': {
                      'type': 'string',
                      'description': 'Short display label',
                    },
                    'description': {
                      'type': 'string',
                      'description':
                          'Optional explanatory text shown with the label',
                    },
                  },
                  'required': ['label'],
                },
              },
              'multiSelect': {
                'type': 'boolean',
                'description': 'Allow multiple selections (default: false)',
              },
              'recommended': {
                'type': ['integer', 'string'],
                'description':
                    'Recommended option: 0-based index or exact label; '
                    'rendered as a "Recommended" badge',
              },
            },
            'required': ['question'],
          },
        },
      },
      'required': ['questions'],
    },
    execute: (arguments, cancelToken, onUpdate) async {
      cancelToken?.throwIfCancelled();
      final questions = _parseQuestions(arguments['questions']);
      final ask = callback;
      if (ask == null) {
        throw StateError(
          'This host cannot answer questions interactively (no ask UI is '
          'installed). Ask the user in plain text instead.',
        );
      }
      final answers = await _awaitAnswers(ask, questions, cancelToken);
      if (answers == null) {
        return ToolExecutionResult.text(
          'The user cancelled the question dialog without answering. '
          'Proceed with a reasonable default, or ask in plain text if the '
          'input is critical.',
        );
      }
      return ToolExecutionResult.text(_formatAnswers(questions, answers));
    },
  );
}