normalizeToolInput function

Map<String, dynamic> normalizeToolInput({
  1. required String toolName,
  2. required Map<String, dynamic> input,
  3. String? cwd,
  4. String? agentId,
  5. Map<String, dynamic> getPlan(
    1. String? agentId
    )?,
})

Normalize tool input for specific tools before execution.

Handles ExitPlanModeV2 plan injection, Bash command cleanup, FileEdit normalization, FileWrite trailing whitespace, and TaskOutput legacy parameter renaming.

Implementation

Map<String, dynamic> normalizeToolInput({
  required String toolName,
  required Map<String, dynamic> input,
  String? cwd,
  String? agentId,
  Map<String, dynamic> Function(String? agentId)? getPlan,
}) {
  switch (toolName) {
    case 'ExitPlanModeV2':
      if (getPlan != null) {
        final plan = getPlan(agentId);
        return {...input, ...plan};
      }
      return input;

    case 'Bash':
      final command = input['command'] as String? ?? '';
      var normalized = command;
      if (cwd != null) {
        normalized = normalized.replaceAll('cd $cwd && ', '');
      }
      // Replace \\; with \; for find -exec commands.
      normalized = normalized.replaceAll(RegExp(r'\\\\;'), r'\;');
      return {
        'command': normalized,
        if (input['description'] != null) 'description': input['description'],
        if (input['timeout'] != null) 'timeout': input['timeout'],
        if (input['run_in_background'] != null)
          'run_in_background': input['run_in_background'],
        if (input['dangerouslyDisableSandbox'] != null)
          'dangerouslyDisableSandbox': input['dangerouslyDisableSandbox'],
      };

    case 'FileEdit':
      return {
        'file_path': input['file_path'],
        'old_string': input['old_string'],
        'new_string': input['new_string'],
        if (input['replace_all'] != null) 'replace_all': input['replace_all'],
      };

    case 'FileWrite':
      final filePath = input['file_path'] as String? ?? '';
      final content = input['content'] as String? ?? '';
      final isMarkdown = RegExp(
        r'\.(md|mdx)$',
        caseSensitive: false,
      ).hasMatch(filePath);
      return {
        'file_path': filePath,
        'content': isMarkdown ? content : _stripTrailingWhitespace(content),
      };

    case 'TaskOutput':
      final taskId =
          input['task_id'] ?? input['agentId'] ?? input['bash_id'] ?? '';
      final timeout =
          input['timeout'] ??
          ((input['wait_up_to'] is num)
              ? (input['wait_up_to'] as num).toInt() * 1000
              : 30000);
      return {
        'task_id': taskId,
        'block': input['block'] ?? true,
        'timeout': timeout,
      };

    default:
      return input;
  }
}