basant307/AI_Governance_Project
045
1/**2 * @license3 * Copyright 2025 Qwen4 * SPDX-License-Identifier: Apache-2.05 */6 7/**8 * task_create tool — create a new task in the team task list.9 */10 11import type {12 ToolCallConfirmationDetails,13 ToolInfoConfirmationDetails,14 ToolInvocation,15 ToolResult,16} from './tools.js';17import { BaseDeclarativeTool, BaseToolInvocation, Kind } from './tools.js';18import { ToolNames, ToolDisplayNames } from './tool-names.js';19import type { Config } from '../config/config.js';20import type { PermissionDecision } from '../permissions/types.js';21import { resolveActiveTeamName } from '../agents/team/identity.js';22import {23 getPlanRequiredTeammatePreApprovalMessage,24 isPlanRequiredTeammateAwaitingApproval,25} from '../agents/runtime/subagent-plan-tool-policy.js';26import { createTask } from '../agents/team/tasks.js';27 28export interface TaskCreateParams {29 subject: string;30 description: string;31 activeForm?: string;32 metadata?: Record<string, unknown>;33}34 35/** Cap on how much of a task description the confirmation dialog shows. */36const CONFIRMATION_DESCRIPTION_LIMIT = 2000;37 38/**39 * Truncate a task description for the interactive confirmation dialog.40 * Descriptions can be up to 10KB; the dialog needs enough to judge the41 * instruction, not the whole payload.42 */43export function truncateForConfirmation(text: string): string {44 if (text.length <= CONFIRMATION_DESCRIPTION_LIMIT) return text;45 return (46 `${text.slice(0, CONFIRMATION_DESCRIPTION_LIMIT)}\n` +47 `… (${text.length - CONFIRMATION_DESCRIPTION_LIMIT} more characters)`48 );49}50 51class TaskCreateInvocation extends BaseToolInvocation<52 TaskCreateParams,53 ToolResult54> {55 constructor(56 private config: Config,57 params: TaskCreateParams,58 ) {59 super(params);60 }61 62 getDescription(): string {63 return `Create task: ${this.params.subject}`;64 }65 66 /**67 * A task's `description` becomes the prompt an idle teammate auto-claims68 * and executes with full tool access — the same privileged-sink shape as69 * `send_message`, where free-form text turns into a new instruction for70 * another agent. The base default `'allow'` short-circuits the classifier71 * in AUTO mode, so override to `'ask'` to keep that injection path under72 * the classifier / human-in-the-loop.73 */74 override async getDefaultPermission(): Promise<PermissionDecision> {75 return 'ask';76 }77 78 /**79 * Unlike the one-line getDescription() used for transcript rendering,80 * the confirmation prompt must show the instruction text itself: the81 * `description` is what an idle teammate will auto-claim and execute82 * with full tool access, so it is exactly what the human is approving.83 */84 override getConfirmationDetails(85 _abortSignal: AbortSignal,86 ): Promise<ToolCallConfirmationDetails> {87 const details: ToolInfoConfirmationDetails = {88 type: 'info',89 title: 'Confirm TaskCreate',90 prompt:91 `Create task: ${this.params.subject}\n\n` +92 truncateForConfirmation(this.params.description),93 onConfirm: async () => {94 // No-op: persistence is handled by coreToolScheduler via PM rules95 },96 };97 return Promise.resolve(details);98 }99 100 async execute(): Promise<ToolResult> {101 if (isPlanRequiredTeammateAwaitingApproval(this.config)) {102 const msg = getPlanRequiredTeammatePreApprovalMessage(103 ToolNames.TASK_CREATE,104 );105 return {106 llmContent: msg,107 returnDisplay: msg,108 error: { message: msg },109 };110 }111 112 const teamName = resolveActiveTeamName(113 this.config.getTeamContext()?.teamName,114 );115 if (!teamName) {116 const msg = 'No active team. Create a team first.';117 return {118 llmContent: msg,119 returnDisplay: msg,120 error: { message: msg },121 };122 }123 124 const task = await createTask(teamName, {125 subject: this.params.subject,126 description: this.params.description,127 activeForm: this.params.activeForm,128 metadata: this.params.metadata,129 });130 131 const llmContent = `Task #${task.id} created: "${task.subject}"`;132 return { llmContent, returnDisplay: llmContent };133 }134}135 136export class TaskCreateTool extends BaseDeclarativeTool<137 TaskCreateParams,138 ToolResult139> {140 static readonly Name = ToolNames.TASK_CREATE;141 142 constructor(private config: Config) {143 super(144 TaskCreateTool.Name,145 ToolDisplayNames.TASK_CREATE,146 'Create a new task in the team task list. ' +147 'Tasks are automatically assigned to idle teammates.',148 Kind.Other,149 {150 type: 'object',151 properties: {152 subject: {153 type: 'string',154 description: 'Short title for the task.',155 maxLength: 200,156 },157 description: {158 type: 'string',159 description: 'Detailed description of the task.',160 maxLength: 10000,161 },162 activeForm: {163 type: 'string',164 maxLength: 200,165 description:166 'Present tense label for UI ' + '(e.g., "Running tests").',167 },168 metadata: {169 type: 'object',170 description: 'Optional arbitrary metadata.',171 },172 },173 required: ['subject', 'description'],174 additionalProperties: false,175 },176 );177 }178 179 protected createInvocation(180 params: TaskCreateParams,181 ): ToolInvocation<TaskCreateParams, ToolResult> {182 return new TaskCreateInvocation(this.config, params);183 }184 185 /**186 * Forward the task content to the classifier. The base sentinel `''`187 * projects to an empty args object, so without this override the AUTO188 * classifier rules on `task_create({})` — the injected payload that189 * `getDefaultPermission() === 'ask'` exists to inspect would be190 * invisible to it. Mirrors `send_message`'s projection.191 */192 override toAutoClassifierInput(193 params: TaskCreateParams,194 ): Record<string, unknown> {195 return {196 subject: params.subject,197 description: params.description,198 };199 }200}201 