All files / src/core-entities ai-action.entity.ts

94.44% Statements 34/36
100% Branches 0/0
71.42% Functions 5/7
93.54% Lines 29/31

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 17748x 48x 48x 48x 48x 48x                                             48x           48x           6x         48x                 48x             48x             48x           48x             48x             48x             48x           48x             48x                 48x             48x             6x 48x               48x               48x           48x           48x             48x             327x    
import { Entity, Column, ManyToOne, OneToMany, JoinColumn } from 'typeorm';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { BaseEntity } from './base.entity';
import { SessionInput } from './session-input.entity';
import { ExecutionLog } from './execution-log.entity';
import { AIActionStatus, AIActionType } from './enums';
 
/**
 * An AIAction represents a single file operation or tool call proposed by the AI.
 * Actions are generated from LLM responses and go through a lifecycle of states.
 *
 * Lifecycle (review_first strategy):
 * 1. proposed → User approves → approved_for_apply
 * 2. approved_for_apply → Batch apply → applied_pending_review
 * 3. applied_pending_review → User confirms → confirmed_kept (permanent)
 *    OR applied_pending_review → User reverts → confirmed_reverted (rolled back)
 *
 * Lifecycle (apply_revert strategy):
 * 1. proposed → Auto-apply → applied_pending_review
 * 2. applied_pending_review → User confirms → confirmed_kept
 *    OR applied_pending_review → User reverts → confirmed_reverted
 *
 * Use GET /inputs/{inputId}/actions to retrieve all actions for an input.
 * Use POST /actions/{actionId}/approve, POST /actions/{actionId}/discard for individual decisions.
 * Use POST /inputs/{inputId}/apply-approved-actions to execute all approved actions.
 * Use POST /inputs/{inputId}/confirm-all-actions or POST /inputs/{inputId}/revert-all-actions for batch decisions.
 */
@Entity('ai_actions')
export class AIAction extends BaseEntity {
  @ApiProperty({
    description: 'UUID of the SessionInput this action belongs to',
    format: 'uuid',
  })
  @Column({ type: 'uuid' })
  input_id: string;
 
  @ApiProperty({
    description: 'The SessionInput entity this action belongs to',
    type: () => SessionInput,
  })
  @ManyToOne(() => SessionInput, (si) => si.aiActions, {
    nullable: false,
    onDelete: 'CASCADE',
  })
  @JoinColumn({ name: 'input_id' })
  sessionInput: SessionInput;
 
  @ApiProperty({
    description: 'Type of action (determines execution behavior)',
    enum: AIActionType,
    enumName: 'AIActionType',
    example: AIActionType.CREATE_FILE,
  })
  @Column({ type: 'text' })
  action_type: string;
 
  @ApiPropertyOptional({
    description: 'Target file path (relative to project root)',
    example: 'src/utils/auth.ts',
  })
  @Column({ type: 'text', nullable: true })
  file_path: string;
 
  @ApiPropertyOptional({
    description:
      'Content to write/create (for create_file, overwrite_file, patch, etc.)',
  })
  @Column({ type: 'text', nullable: true })
  content: string;
 
  @ApiPropertyOptional({
    description: 'Original file content before modification (used for revert)',
  })
  @Column({ type: 'text', nullable: true })
  original_content_for_revert: string;
 
  @ApiPropertyOptional({
    description: 'Shell command to execute (for run_command action type)',
    example: 'npm install lodash',
  })
  @Column({ type: 'text', nullable: true })
  command_string: string | null;
 
  @ApiPropertyOptional({
    description:
      'JSON array of files to include in context (for request_context)',
  })
  @Column({ type: 'text', nullable: true })
  files: string | null;
 
  @ApiPropertyOptional({
    description:
      'JSON array of folders to include in context (for request_context)',
  })
  @Column({ type: 'text', nullable: true })
  folders: string | null;
 
  @ApiPropertyOptional({
    description: 'Reason/explanation for this action (from LLM)',
  })
  @Column({ type: 'text', nullable: true })
  reason: string | null;
 
  @ApiPropertyOptional({
    description:
      'Handover prompt for spawning new session (for new_session action type)',
  })
  @Column({ type: 'text', nullable: true })
  handover_string: string | null;
 
  @ApiProperty({
    description: 'Current lifecycle state of this action',
    enum: AIActionStatus,
    enumName: 'AIActionStatus',
    example: AIActionStatus.PROPOSED,
  })
  @Column({ type: 'text' })
  status: string;
 
  @ApiProperty({
    description: 'Execution order within batch (actions sorted by this number)',
    example: 1,
  })
  @Column({ type: 'integer' })
  order_of_execution: number;
 
  @ApiPropertyOptional({
    description:
      'Execution logs showing output and errors from execution attempts',
    type: () => [ExecutionLog],
  })
  @OneToMany(() => ExecutionLog, (executionLog) => executionLog.aiAction)
  executionLogs: ExecutionLog[];
 
  @ApiPropertyOptional({
    description:
      'MCP server name (for MCP tool actions with serverName__toolName pattern)',
    example: 'playwright',
  })
  @Column({ type: 'text', nullable: true })
  server_name: string | null;
 
  @ApiPropertyOptional({
    description:
      'MCP tool name (for MCP tool actions with serverName__toolName pattern)',
    example: 'browser_navigate',
  })
  @Column({ type: 'text', nullable: true })
  tool_name: string | null;
 
  @ApiPropertyOptional({
    description: 'Arguments for MCP tool call (JSON string)',
  })
  @Column({ type: 'text', nullable: true })
  arguments: string | null;
 
  @ApiPropertyOptional({
    description: 'Plain text explanation (for final action type)',
  })
  @Column({ type: 'text', nullable: true })
  plain: string | null;
 
  @ApiPropertyOptional({
    description:
      'Native tool call ID from LLM response (for function calling mode)',
  })
  @Column({ type: 'text', nullable: true })
  tool_call_id: string | null;
 
  @ApiPropertyOptional({
    description:
      'JSON array of selection groups for final action type. Each group has question and selections array.',
  })
  @Column({ type: 'text', nullable: true })
  selections: string | null = null;
}