createTaskUpdateTool function

Tool<JsonMap, String> createTaskUpdateTool(
  1. Genkit ai, {
  2. required ToolRuntime runtime,
})

Builds the tool that updates a session task.

Implementation

Tool<JsonMap, String> createTaskUpdateTool(
  Genkit ai, {
  required ToolRuntime runtime,
}) {
  final idList = <String, Object?>{
    'type': 'array',
    'items': <String, Object?>{'type': 'string'},
  };
  return defineTextTool(
    ai,
    runtime: runtime,
    name: 'task_update',
    description: _description,
    properties: <String, Object?>{
      'taskId': stringProperty('The id of the task to update.'),
      'status': stringProperty('New status.', enumValues: <String>['pending', 'in_progress', 'completed']),
      'subject': stringProperty('New title.'),
      'description': stringProperty('New description.'),
      'activeForm': stringProperty('New present-continuous form.'),
      'owner': stringProperty('Who owns the task.'),
      'addBlocks': <String, Object?>{...idList, 'description': 'Ids of tasks this task blocks.'},
      'addBlockedBy': <String, Object?>{...idList, 'description': 'Ids of tasks that block this task.'},
      'metadata': <String, Object?>{'type': 'object', 'description': 'Metadata keys to merge; null removes a key.'},
    },
    required: <String>['taskId'],
    run: (input) async {
      final taskId = requireString(input, 'taskId').trim();
      final task = runtime.taskById(taskId);
      if (task == null) {
        throw ToolFailure('Task #$taskId was not found. Use task_list to see existing tasks.');
      }

      final statusInput = readString(input, 'status');
      final newStatus = parseSessionTaskStatus(statusInput);
      if (statusInput != null && newStatus == null) {
        throw const ToolFailure('`status` must be pending, in_progress, or completed.');
      }
      final transitionError = runtime.validateTaskTransition(taskId: taskId, newStatus: newStatus);
      if (transitionError != null) {
        throw ToolFailure(transitionError);
      }

      String? nonBlank(String key) {
        final value = readString(input, key)?.trim();
        return value == null || value.isEmpty ? null : value;
      }

      task
        ..subject = nonBlank('subject') ?? task.subject
        ..description = nonBlank('description') ?? task.description
        ..activeForm = nonBlank('activeForm') ?? task.activeForm
        ..owner = nonBlank('owner') ?? task.owner
        ..status = newStatus ?? task.status;
      _addIds(task.blocks, readList(input, 'addBlocks'));
      _addIds(task.blockedBy, readList(input, 'addBlockedBy'));
      for (final entry in (readJsonMap(input, 'metadata') ?? const <String, dynamic>{}).entries) {
        if (entry.value == null) {
          task.metadata.remove(entry.key);
        } else {
          task.metadata[entry.key] = entry.value;
        }
      }

      runtime.startTool('task_update', '#${task.id} ${sessionTaskStatusLabel(task.status)}: ${task.subject}');
      return jsonEncode(<String, Object?>{'task': task.toJson()});
    },
  );
}