centaurus-cli 3.1.1 → 3.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli-adapter.js +4 -2
- package/dist/cli-adapter.js.map +1 -1
- package/dist/config/models.js +2 -0
- package/dist/config/models.js.map +1 -1
- package/dist/config/types.js.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/services/ai-service-client.js +3 -2
- package/dist/services/ai-service-client.js.map +1 -1
- package/dist/services/api-client.js.map +1 -1
- package/dist/ui/components/App.js +2 -2
- package/dist/ui/components/App.js.map +1 -1
- package/dist/ui/components/AuthWelcomeScreen.js +1 -2
- package/dist/ui/components/AuthWelcomeScreen.js.map +1 -1
- package/dist/ui/components/VersionUpdatePrompt.js +31 -2
- package/dist/ui/components/VersionUpdatePrompt.js.map +1 -1
- package/dist/utils/git-stats.js +7 -5
- package/dist/utils/git-stats.js.map +1 -1
- package/dist/utils/ink-static-render.js +25 -0
- package/dist/utils/ink-static-render.js.map +1 -0
- package/package.json +1 -1
package/dist/cli-adapter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli-adapter.ts"],"sourcesContent":["import * as fs from 'fs';\r\nimport * as path from 'path';\r\nimport { fileURLToPath } from 'url';\r\nimport { dirname } from 'path';\r\nimport * as shellUtils from './utils/shell.js';\r\nimport { sanitizeForContext } from './utils/context-sanitizer.js';\r\n\r\nconst __filename = fileURLToPath(import.meta.url);\r\nconst __dirname = dirname(__filename);\r\nimport { ConfigManager } from './config/manager.js';\r\nimport { ToolRegistry } from './tools/registry.js';\r\nimport { viewFileTool, writeToFileTool, editFileTool, listDirTool, multiEditFileTool } from './tools/file-ops.js';\r\nimport { runCommandTool } from './tools/command.js';\r\nimport { grepSearchTool } from './tools/grep-search.js';\r\nimport { findFilesTool } from './tools/find-files.js';\r\nimport { getDiffTool } from './tools/get-diff.js';\r\nimport { inspectSymbolTool } from './tools/inspect-symbol.js';\r\nimport { createPlanTool, markTaskCompleteTool, getCurrentPlan, clearPlan, approvePlan, getPlanContextForPrompt, getPhaseContextForPrompt, getCurrentPhase, advanceToNextPhase, areAllTasksComplete, Plan, PlanStep } from './tools/plan-mode.js';\r\nimport { webSearchTool, fetchUrlTool } from './tools/web-search.js';\r\nimport { taskCompleteTool } from './tools/task-complete.js';\r\nimport { readBinaryFileTool } from './tools/read-binary-file.js';\r\nimport { createImageTool } from './tools/create-image.js';\r\nimport { backgroundCommandTool } from './tools/background-command.js';\r\nimport { subAgentTool } from './tools/sub-agent.js';\r\nimport { enterRemoteSessionTool } from './tools/enter-remote-session.js';\r\nimport { workflowTool } from './tools/workflow-tool.js';\r\nimport { fastContextTool } from './tools/fast-context.js';\r\nimport { addMcpTool } from './tools/add-mcp.js';\r\nimport { SubAgentManager } from './services/sub-agent-manager.js';\r\nimport { ShellInputAgent } from './services/shell-input-agent.js';\r\nimport { apiClient } from './services/api-client.js';\r\nimport { conversationManager } from './services/conversation-manager.js';\r\nimport { aiServiceClient, Message as AIMessage, StreamChunk } from './services/ai-service-client.js';\r\nimport { getSupportedModels, getModelDisplayName, isValidModel, getInvalidModelError, fetchModelsConfig, getModelConfigByIdAndName } from './config/models.js';\r\nimport { authenticateWithGoogle } from './services/auth-handler.js';\r\nimport { ContextManager } from './context/context-manager.js';\r\nimport { CommandDetector } from './context/command-detector.js';\r\nimport { SSHHandler } from './context/handlers/ssh-handler.js';\r\nimport { WSLHandler } from './context/handlers/wsl-handler.js';\r\nimport { DockerHandler } from './context/handlers/docker-handler.js';\r\nimport { SubshellContext } from './context/types.js';\r\nimport { AIContextInjector } from './services/ai-context-injector.js';\r\nimport { environmentContextInjector } from './services/environment-context-injector.js';\r\nimport { MCPConfigManager } from './config/mcp-config-manager.js';\r\nimport { MCPServerManager } from './mcp/mcp-server-manager.js';\r\nimport { MCPCommandHandler } from './mcp/mcp-command-handler.js';\r\nimport { isInteractiveEditorCommand, runWSLCommand, runDockerCommand, runSSHCommand } from './utils/editor-utils.js';\r\nimport { conversationLogger, quickLog } from './utils/conversation-logger.js';\r\nimport { localChatStorage, StoredMessage, LocalChatMeta, StoredUIMessage, StoredRemoteContext, StoredCheckpointMeta } from './services/local-chat-storage.js';\r\nimport { Message } from './types/index.js';\r\nimport { logError, logWarning } from './utils/logger.js';\r\nimport { BackgroundTaskManager, BackgroundTaskInfo } from './services/background-task-manager.js';\r\nimport { sessionQuotaManager } from './services/session-quota-manager.js';\r\nimport { ollamaService, OllamaService } from './services/ollama-service.js';\r\nimport { workflowStorage } from './services/workflow-storage.js';\r\nimport { Workflow, WorkflowStep, WorkflowExecutionState } from './types/workflow.js';\r\nimport { CheckpointManager, CheckpointMeta, RemoteFileHandler, RemoteSessionInfo } from './services/checkpoint-manager.js';\r\nimport { rulesStorage } from './services/rules-storage.js';\r\nimport { RulesEditorRequest, SaveRuleResult } from './types/rule.js';\r\nimport { resolveRuleReferences } from './utils/rule-reference-resolver.js';\r\n\r\ntype AutoCompactionCandidateKind = 'read_file_tool_result' | 'execute_command_tool_result' | 'user_shell_command_output';\r\n\r\ninterface AutoCompactionCandidate {\r\n index: number;\r\n kind: AutoCompactionCandidateKind;\r\n toolCallId?: string;\r\n}\r\n\r\ninterface AutoCompactionOutcome {\r\n triggered: boolean;\r\n historyChanged: boolean;\r\n compactedCount: number;\r\n beforeUsagePercent: number;\r\n afterUsagePercent: number;\r\n targetReached: boolean;\r\n}\r\n\r\nexport class CentaurusCLI {\r\n private configManager: ConfigManager;\r\n private toolRegistry: ToolRegistry;\r\n private conversationHistory: AIMessage[] = [];\r\n private cwd: string;\r\n private planMode: boolean = false;\r\n private pendingPlanRequest: string | null = null; // Stores original user request during planning phase\r\n private commandMode: boolean = false;\r\n private backgroundMode: boolean = false; // Background shell mode for running commands in background\r\n private shellIdCounter: number = 1;\r\n private previousMode: 'execution' | 'planning' = 'execution';\r\n private onResponseCallback?: (message: string) => void;\r\n private onDirectMessageCallback?: (message: string) => void; // For slash commands - adds directly to history\r\n private onFileChangeSummaryCallback?: (data: { filesChanged: number; insertions: number; deletions: number }) => void;\r\n private onResponseStreamCallback?: (chunk: string) => void;\r\n private onClearStreamedResponse?: () => void; // Clear streamed text when task_complete has summary\r\n private onThoughtStreamCallback?: (thought: string) => void;\r\n private onThoughtCompleteCallback?: (durationSeconds: number) => void;\r\n private onCommandModeChange?: (commandMode: boolean) => void;\r\n private onCwdChange?: (cwd: string) => void;\r\n private onModelChange?: (modelName: string, contextWindow: number) => void;\r\n private onShowPickerCallback?: (options: { message: string; choices: Array<{ label: string; value: string }>; type: 'model' }) => void;\r\n private onToolExecutionUpdate?: (update: { toolName: string; status: 'pending' | 'executing' | 'completed' | 'error'; result?: string; error?: string; arguments?: Record<string, any> }) => void;\r\n private onToolApprovalRequest?: (request: { message: string; risky: boolean; preview?: { type: 'code' | 'diff'; content: string; language?: string }; operationType?: 'write_file' | 'edit_file' | 'execute_command'; operationDetails?: Record<string, any> }) => Promise<boolean>;\r\n private onToolStreamingOutput?: (update: { toolName: string; chunk: string; type: 'stdout' | 'stderr' }) => void;\r\n private onPlanModeChange?: (planMode: boolean) => void;\r\n private onPlanApprovalRequest?: (plan: Plan) => Promise<boolean>;\r\n private onPlanCreated?: (plan: Plan) => void;\r\n private onTaskCompleted?: (task: PlanStep, taskNumber: number, totalTasks: number, completionNote?: string, taskDescription?: string) => void;\r\n private onPasswordRequest?: (message: string) => Promise<string>;\r\n private currentInteractiveProcess?: shellUtils.InteractiveProcess;\r\n private conversationStarted: boolean = false;\r\n private contextManager: ContextManager;\r\n private commandDetector: CommandDetector;\r\n private aiContextInjector: AIContextInjector;\r\n private onSubshellContextChange?: (context: SubshellContext, stack?: SubshellContext[]) => void;\r\n private currentAbortController?: AbortController;\r\n private requestIntentionallyAborted: boolean = false;\r\n private mcpCommandHandler?: MCPCommandHandler;\r\n private onInteractiveEditorMode?: (active: boolean, command?: string, cwd?: string, remoteContext?: SubshellContext, parentContext?: SubshellContext) => void;\r\n private onConnectionStatusUpdate?: (status: { type: 'ssh' | 'wsl' | 'docker'; status: 'connecting' | 'connected' | 'error' | 'disconnected'; connectionString?: string; error?: string }) => void;\r\n private currentChatId: string | null = null;\r\n private onShowChatPickerCallback?: (chats: LocalChatMeta[], currentChatId: string | null) => void;\r\n private onShowChatDeletePickerCallback?: (chats: LocalChatMeta[], currentChatId: string | null) => void;\r\n private onShowChatListCallback?: (chats: LocalChatMeta[], currentChatId: string | null) => void;\r\n private onShowChatRenamePickerCallback?: (chats: LocalChatMeta[], currentChatId: string | null) => void;\r\n private onRestoreMessagesCallback?: (messages: Message[]) => void;\r\n private uiMessageHistory: Message[] = []; // Mirror of App.tsx's messageHistory for saving\r\n private cwdStack: string[] = []; // Stack of CWDs for nested sessions (pushed when entering, popped when exiting)\r\n private connectionCommandStack: string[] = []; // Stack of commands used to connect (for nested sessions like SSH>SSH, SSH>Docker)\r\n private onBackgroundModeChange?: (backgroundMode: boolean) => void;\r\n private onBackgroundTaskCountChange?: (count: number) => void;\r\n private onSetAutoMode?: (enabled: boolean) => void;\r\n private onShowBackgroundTaskPickerCallback?: (tasks: { id: string; command: string; cwd: string; durationMs: number; isRunning: boolean; outputPreview: string }[]) => void;\r\n private onShowBackgroundTaskCancelPickerCallback?: (tasks: { id: string; command: string; cwd: string; durationMs: number; isRunning: boolean; outputPreview: string }[]) => void;\r\n private onBackgroundTaskViewCallback?: (task: { id: string; command: string; cwd: string; output: string; isRunning: boolean; onOutput: (handler: (chunk: string) => void) => void; onComplete: (handler: (exitCode: number) => void) => void }) => void;\r\n private onTokenCountUpdate?: (tokens: number) => void; // Report actual AI context token count to UI\r\n private currentTokenCount: number = 0; // Track current token count for context limit checking\r\n private tokenCountUpdateVersion: number = 0; // Guards against stale async token count updates\r\n private contextLimitReached: boolean = false; // Track if context limit has been reached\r\n private onContextLimitReached?: (reached: boolean) => void; // Notify UI about context limit state\r\n private onSessionQuotaUpdate?: (remaining: number, canSend: boolean, timeRemaining: string) => void;\r\n // MCP screen callbacks\r\n private onShowMCPAddScreen?: () => void;\r\n private onShowMCPRemoveScreen?: (servers: { name: string; command: string; args?: string[]; enabled?: boolean }[]) => void;\r\n private onShowMCPEnableScreen?: (servers: { name: string; command: string; args?: string[]; enabled?: boolean }[]) => void;\r\n private onShowMCPDisableScreen?: (servers: { name: string; command: string; args?: string[]; enabled?: boolean }[]) => void;\r\n private onShowMCPListScreen?: (servers: Array<{ name: string; enabled: boolean; status: 'connected' | 'disconnected' | 'error' | 'connecting'; tools: Array<{ name: string }> }>) => void;\r\n private onSubAgentCountChange?: (count: number) => void; // Callback for sub-agent count changes\r\n private onPromptAnswered?: (shellId: string) => void; // Callback when AI answers a shell prompt\r\n private onShowWorkflowCreatorCallback?: (initialSteps?: Array<{ type: 'command' | 'instruction'; content: string }>) => void; // Callback to show workflow creator screen with optional initial steps\r\n private onWorkflowSaveCallback?: (workflow: Workflow) => void; // Callback when workflow is saved\r\n private onShowRulesEditorCallback?: (request: RulesEditorRequest) => void; // Callback to show the rules editor\r\n private onAiAutoSuggestChange?: (enabled: boolean) => void; // Callback for AI auto-suggest setting changes\r\n private onRevertToCheckpointCallback?: (checkpointIndex: number, prompt: string) => void; // Callback for revert UI update\r\n\r\n private static readonly AUTO_COMPACTION_TRIGGER_PERCENT = 80;\r\n private static readonly AUTO_COMPACTION_TARGET_PERCENT = 40;\r\n private static readonly AUTO_COMPACTION_TOOL_NAME = 'auto_context_compaction';\r\n private static readonly READ_FILE_COMPACTION_MESSAGE = '[This tool result has been compacted to reduce context usage.]';\r\n private static readonly TERMINAL_COMPACTION_NOTICE = '[This terminal output was compacted to show the last 20 lines to reduce context usage.]';\r\n private static readonly TERMINAL_LINES_TO_KEEP = 20;\r\n private static readonly USER_SHELL_PREFIX = '[User ran shell command in ';\r\n private static readonly USER_SHELL_OUTPUT_MARKER = '\\nOutput:\\n';\r\n private static readonly COMPACTION_NOTICE_FOR_AGENT =\r\n '[SYSTEM NOTE: Older read-file and terminal outputs were compacted to reduce context usage. Continue working with the compacted history.]';\r\n\r\n // Checkpoint manager for revert functionality\r\n private checkpointManager?: CheckpointManager;\r\n private currentCheckpointId?: string; // Track current checkpoint being created\r\n\r\n // Workflow learning mode state\r\n private workflowLearningActive: boolean = false;\r\n private learnedWorkflowSteps: Array<{ type: 'command' | 'instruction'; content: string }> = [];\r\n\r\n // Callback to set input value (e.g., for revert)\r\n private onSetInputCallback: ((value: string) => void) | null = null;\r\n\r\n // Interrupt Queue state tracking\r\n private interruptQueue: string[] = [];\r\n private onInterruptQueueUpdateCallback?: (queue: string[]) => void;\r\n private onQueuedMessageDispatchedCallback?: (message: string) => void;\r\n // Session shell command queue (separate from AI interrupt queue)\r\n private sessionCommandQueue: string[] = [];\r\n private isProcessingSessionCommandQueue: boolean = false;\r\n private onCommandQueueUpdateCallback?: (queue: string[]) => void;\r\n private onQueuedCommandDispatchedCallback?: (command: string) => void;\r\n\r\n private static readonly INTERRUPT_SYSTEM_NOTE_PREFIX =\r\n '[SYSTEM NOTE: The user interrupted you with the following message. Please address it, adjust your actions, and proceed with the task.]';\r\n\r\n // Track if we are currently reconnecting to remote sessions (prevent race conditions)\r\n private isReconnecting: boolean = false;\r\n // Track session transitions (warpify/nesting) to prevent queue races across context changes.\r\n private isSessionTransitioning: boolean = false;\r\n\r\n constructor() {\r\n this.configManager = new ConfigManager();\r\n this.toolRegistry = new ToolRegistry();\r\n this.cwd = process.cwd();\r\n\r\n // Initialize Context Manager and Command Detector\r\n this.contextManager = new ContextManager(this.cwd, process.platform);\r\n this.commandDetector = new CommandDetector();\r\n this.aiContextInjector = new AIContextInjector();\r\n\r\n // Register context change callback to update cwd\r\n // Register context change callback to update cwd\r\n this.contextManager.onContextChange((context, stack) => {\r\n this.cwd = context.metadata.workingDirectory;\r\n if (this.onCwdChange) {\r\n this.onCwdChange(this.cwd);\r\n }\r\n if (this.onSubshellContextChange) {\r\n this.onSubshellContextChange(context, stack);\r\n }\r\n // A context transition can unblock queued command processing.\r\n void this.maybeProcessSessionCommandQueue();\r\n });\r\n\r\n // Initialize MCP\r\n this.initializeMCP();\r\n\r\n // Initialize ShellInputAgent with tool registry and wire shell input callback\r\n ShellInputAgent.initialize(this.toolRegistry);\r\n ShellInputAgent.setOnShellInput((shellId: string, input: string) => {\r\n this.writeToShellStdin(input);\r\n });\r\n }\r\n\r\n setOnResponseCallback(callback: (message: string) => void): void {\r\n this.onResponseCallback = callback;\r\n }\r\n\r\n setOnDirectMessageCallback(callback: (message: string) => void): void {\r\n this.onDirectMessageCallback = callback;\r\n }\r\n\r\n setOnFileChangeSummaryCallback(callback: (data: { filesChanged: number; insertions: number; deletions: number }) => void): void {\r\n this.onFileChangeSummaryCallback = callback;\r\n }\r\n\r\n setOnResponseStreamCallback(callback: (chunk: string) => void): void {\r\n this.onResponseStreamCallback = callback;\r\n }\r\n\r\n setOnClearStreamedResponse(callback: () => void): void {\r\n this.onClearStreamedResponse = callback;\r\n }\r\n\r\n setOnThoughtStreamCallback(callback: (thought: string) => void): void {\r\n this.onThoughtStreamCallback = callback;\r\n }\r\n\r\n setOnThoughtCompleteCallback(callback: (durationSeconds: number) => void): void {\r\n this.onThoughtCompleteCallback = callback;\r\n }\r\n\r\n setOnShowPickerCallback(callback: (options: { message: string; choices: Array<{ label: string; value: string }>; type: 'model' }) => void): void {\r\n this.onShowPickerCallback = callback;\r\n }\r\n\r\n setOnToolExecutionUpdate(callback: (update: { toolName: string; status: 'pending' | 'executing' | 'completed' | 'error'; result?: string; error?: string; arguments?: Record<string, any> }) => void): void {\r\n this.onToolExecutionUpdate = callback;\r\n }\r\n\r\n setOnToolApprovalRequest(callback: (request: { message: string; risky: boolean; preview?: { type: 'code' | 'diff'; content: string; language?: string; fullDiff?: string }; operationType?: 'write_file' | 'edit_file' | 'execute_command'; operationDetails?: Record<string, any> }) => Promise<boolean>): void {\r\n this.onToolApprovalRequest = callback;\r\n }\r\n\r\n setOnToolStreamingOutput(callback: (update: { toolName: string; chunk: string; type: 'stdout' | 'stderr' }) => void): void {\r\n this.onToolStreamingOutput = callback;\r\n }\r\n\r\n setOnPlanModeChange(callback: (planMode: boolean) => void): void {\r\n this.onPlanModeChange = callback;\r\n }\r\n\r\n setOnPlanApprovalRequest(callback: (plan: Plan) => Promise<boolean>): void {\r\n this.onPlanApprovalRequest = callback;\r\n }\r\n\r\n setOnPlanCreated(callback: (plan: Plan) => void): void {\r\n this.onPlanCreated = callback;\r\n }\r\n\r\n setOnTaskCompleted(callback: (task: PlanStep, taskNumber: number, totalTasks: number, completionNote?: string, taskDescription?: string) => void): void {\r\n this.onTaskCompleted = callback;\r\n }\r\n\r\n setOnCommandModeChange(callback: (commandMode: boolean) => void): void {\r\n this.onCommandModeChange = callback;\r\n }\r\n\r\n setOnCwdChange(callback: (cwd: string) => void): void {\r\n this.onCwdChange = callback;\r\n }\r\n\r\n setOnModelChange(callback: (modelName: string, contextWindow: number) => void): void {\r\n this.onModelChange = callback;\r\n }\r\n\r\n setOnSubshellContextChange(callback: (context: SubshellContext, stack?: SubshellContext[]) => void): void {\r\n this.onSubshellContextChange = callback;\r\n }\r\n\r\n setOnPasswordRequest(callback: (message: string) => Promise<string>): void {\r\n this.onPasswordRequest = callback;\r\n // Update SSH handler if already initialized\r\n if (this.sshHandler) {\r\n this.sshHandler.setPasswordRequestCallback(callback);\r\n }\r\n }\r\n\r\n setOnInteractiveEditorMode(callback: (active: boolean, command?: string, cwd?: string, remoteContext?: SubshellContext, parentContext?: SubshellContext) => void): void {\r\n this.onInteractiveEditorMode = callback;\r\n }\r\n\r\n setOnConnectionStatusUpdate(callback: (status: { type: 'ssh' | 'wsl' | 'docker'; status: 'connecting' | 'connected' | 'error'; connectionString?: string; error?: string }) => void): void {\r\n this.onConnectionStatusUpdate = callback;\r\n }\r\n\r\n setOnTokenCountUpdate(callback: (tokens: number) => void): void {\r\n this.onTokenCountUpdate = callback;\r\n }\r\n\r\n setOnContextLimitReached(callback: (reached: boolean) => void): void {\r\n this.onContextLimitReached = callback;\r\n }\r\n\r\n setOnSubAgentCountChange(callback: (count: number) => void): void {\r\n this.onSubAgentCountChange = callback;\r\n }\r\n\r\n setOnPromptAnswered(callback: (shellId: string) => void): void {\r\n this.onPromptAnswered = callback;\r\n // Wire this callback to ShellInputAgent\r\n ShellInputAgent.setOnPromptAnswered(callback);\r\n }\r\n\r\n setOnShowWorkflowCreator(callback: (initialSteps?: Array<{ type: 'command' | 'instruction'; content: string }>) => void): void {\r\n this.onShowWorkflowCreatorCallback = callback;\r\n }\r\n\r\n setOnWorkflowSave(callback: (workflow: Workflow) => void): void {\r\n this.onWorkflowSaveCallback = callback;\r\n }\r\n\r\n setOnShowRulesEditor(callback: (request: RulesEditorRequest) => void): void {\r\n this.onShowRulesEditorCallback = callback;\r\n }\r\n\r\n setOnInterruptQueueUpdate(callback: (queue: string[]) => void): void {\r\n this.onInterruptQueueUpdateCallback = callback;\r\n }\r\n\r\n setOnQueuedMessageDispatched(callback: (message: string) => void): void {\r\n this.onQueuedMessageDispatchedCallback = callback;\r\n }\r\n\r\n setOnCommandQueueUpdate(callback: (queue: string[]) => void): void {\r\n this.onCommandQueueUpdateCallback = callback;\r\n }\r\n\r\n setOnQueuedCommandDispatched(callback: (command: string) => void): void {\r\n this.onQueuedCommandDispatchedCallback = callback;\r\n }\r\n\r\n setOnAiAutoSuggestChange(callback: (enabled: boolean) => void): void {\r\n this.onAiAutoSuggestChange = callback;\r\n }\r\n\r\n setOnRevertToCheckpoint(callback: (checkpointIndex: number, prompt: string) => void): void {\r\n this.onRevertToCheckpointCallback = callback;\r\n }\r\n\r\n /**\r\n * Get checkpoints for autocomplete dropdown\r\n */\r\n getCheckpointsForAutocomplete(): Array<{ id: string; prompt: string; timestamp: Date }> {\r\n if (!this.currentChatId) return [];\r\n this.ensureCheckpointManagerForChat(this.currentChatId);\r\n if (!this.checkpointManager) return [];\r\n return this.checkpointManager.list().map(cp => ({\r\n id: cp.id,\r\n prompt: this.normalizePromptForInputBar(cp.prompt),\r\n timestamp: new Date(cp.createdAt)\r\n }));\r\n }\r\n\r\n private ensureCheckpointManagerForChat(chatId: string, fallbackCheckpoints?: StoredCheckpointMeta[]): void {\r\n if (!this.checkpointManager) {\r\n this.checkpointManager = new CheckpointManager();\r\n }\r\n\r\n this.checkpointManager.setCurrentChatId(chatId);\r\n\r\n if (fallbackCheckpoints && fallbackCheckpoints.length > 0) {\r\n this.checkpointManager.hydrateFromChatHistory(fallbackCheckpoints as unknown as CheckpointMeta[]);\r\n }\r\n }\r\n\r\n private toStoredCheckpointMeta(checkpoint: CheckpointMeta): StoredCheckpointMeta {\r\n return {\r\n id: checkpoint.id,\r\n prompt: checkpoint.prompt,\r\n createdAt: checkpoint.createdAt,\r\n createdAtMs: checkpoint.createdAtMs,\r\n cwd: checkpoint.cwd,\r\n contextType: checkpoint.contextType,\r\n remoteSessionInfo: checkpoint.remoteSessionInfo,\r\n conversationIndex: checkpoint.conversationIndex,\r\n uiMessageIndex: checkpoint.uiMessageIndex,\r\n uiMessageId: checkpoint.uiMessageId,\r\n snapshotDir: checkpoint.snapshotDir,\r\n manifestPath: checkpoint.manifestPath,\r\n changes: checkpoint.changes,\r\n commands: [...checkpoint.commands],\r\n toolCalls: checkpoint.toolCalls.map(toolCall => ({\r\n id: toolCall.id,\r\n name: toolCall.name,\r\n arguments: toolCall.arguments,\r\n timestamp: toolCall.timestamp,\r\n })),\r\n status: checkpoint.status,\r\n };\r\n }\r\n\r\n\r\n /**\r\n * Save a workflow from the workflow creator UI\r\n */\r\n saveWorkflow(name: string, steps: Array<{ type: 'command' | 'instruction'; content: string }>, description?: string): void {\r\n const workflow = workflowStorage.createWorkflow(name, steps, description);\r\n workflowStorage.save(workflow);\r\n if (this.onWorkflowSaveCallback) {\r\n this.onWorkflowSaveCallback(workflow);\r\n }\r\n }\r\n\r\n saveRule(name: string, content: string, previousName?: string): SaveRuleResult {\r\n return rulesStorage.save(name, content, previousName);\r\n }\r\n\r\n /**\r\n * Toggle workflow learning mode.\r\n * First call: Start learning and return a message to display.\r\n * Second call: Stop learning and show workflow creator with learned steps.\r\n * @returns Message to display to user, or null if showing workflow creator\r\n */\r\n toggleWorkflowLearning(): string | null {\r\n if (this.workflowLearningActive) {\r\n // Stop learning mode\r\n this.workflowLearningActive = false;\r\n const steps = [...this.learnedWorkflowSteps];\r\n this.learnedWorkflowSteps = []; // Clear for next learning session\r\n\r\n if (steps.length === 0) {\r\n return '⚠️ No steps were recorded. Learning mode cancelled.';\r\n }\r\n\r\n // Show workflow creator with the learned steps\r\n if (this.onShowWorkflowCreatorCallback) {\r\n this.onShowWorkflowCreatorCallback(steps);\r\n return null; // UI will handle the screen change\r\n } else {\r\n return '❌ Workflow creator not available. Please update the CLI.';\r\n }\r\n } else {\r\n // Start learning mode\r\n this.workflowLearningActive = true;\r\n this.learnedWorkflowSteps = [];\r\n return `📚 **Learning mode started!**\r\n\r\nCommands and prompts from here will be recorded to create your workflow.\r\n\r\n**Instructions:**\r\n• Run commands normally - they'll be saved as command steps\r\n• Type AI prompts - they'll be saved as instruction steps\r\n• Run \\`/workflow new learn-workflow\\` again when you're done\r\n\r\nRecording will begin with your next input.`;\r\n }\r\n }\r\n\r\n /**\r\n * Check if workflow learning is currently active\r\n */\r\n isWorkflowLearningActive(): boolean {\r\n return this.workflowLearningActive;\r\n }\r\n\r\n /**\r\n * Record a step during workflow learning mode\r\n * @param type Type of step (command or instruction)\r\n * @param content The command or instruction content\r\n */\r\n recordWorkflowStep(type: 'command' | 'instruction', content: string): void {\r\n if (this.workflowLearningActive && content.trim()) {\r\n this.learnedWorkflowSteps.push({ type, content: content.trim() });\r\n quickLog(`[${new Date().toISOString()}] [WorkflowLearning] Recorded ${type}: ${content.trim()}\\n`);\r\n }\r\n }\r\n\r\n /**\r\n * Cancel workflow learning mode with a message\r\n * Used when conflicting commands are run during learning (e.g., /exit, /clear, /workflow run)\r\n * @param reason The reason for cancellation\r\n * @returns Message to display to user, or empty string if not in learning mode\r\n */\r\n cancelWorkflowLearning(reason: string): string {\r\n if (!this.workflowLearningActive) {\r\n return '';\r\n }\r\n\r\n this.workflowLearningActive = false;\r\n const stepsCount = this.learnedWorkflowSteps.length;\r\n this.learnedWorkflowSteps = [];\r\n\r\n quickLog(`[${new Date().toISOString()}] [WorkflowLearning] Cancelled: ${reason} (${stepsCount} steps discarded)\\n`);\r\n\r\n return `⚠️ **Learning mode cancelled**: ${reason}\\n\\n` +\r\n `${stepsCount > 0 ? `${stepsCount} recorded step(s) were discarded.` : 'No steps were recorded.'}\\n` +\r\n `Run \\`/workflow new learn-workflow\\` to start again.`;\r\n }\r\n\r\n /**\r\n * Build a prompt for the AI to execute a workflow\r\n * This formats the workflow steps into a clear instruction set\r\n */\r\n private buildWorkflowPrompt(workflow: Workflow): string {\r\n const stepsText = workflow.steps.map((step, index) => {\r\n const stepNum = index + 1;\r\n if (step.type === 'command') {\r\n return `Step ${stepNum}: [RUN COMMAND] ${step.content}`;\r\n } else {\r\n return `Step ${stepNum}: [INSTRUCTION] ${step.content}`;\r\n }\r\n }).join('\\n');\r\n\r\n return `Execute the following workflow \"${workflow.name}\" step by step:\r\n\r\n${stepsText}\r\n\r\nIMPORTANT: \r\n- For [RUN COMMAND] steps, execute the exact command shown using the execute_command tool.\r\n- For [INSTRUCTION] steps, follow the instruction using appropriate tools.\r\n- Execute each step in order, waiting for completion before moving to the next.\r\n- If any step fails, stop and report the error.\r\n- After all steps complete successfully, summarize what was done.\r\n\r\nBegin executing now, starting with Step 1.`;\r\n }\r\n\r\n /**\r\n * Warpify a detected SSH/WSL/Docker session.\r\n * This establishes a proper ssh2/handler connection (will prompt for password)\r\n * so commands can be executed via the handler, not just PTY passthrough.\r\n * \r\n * @param command - The original command (e.g., \"ssh rohan@localhost\")\r\n * @param type - The detected session type\r\n * @param connectionString - The connection string (e.g., \"rohan@localhost\")\r\n * @returns Promise<boolean> - true if warpify succeeded\r\n */\r\n async warpifySession(command: string, type: 'ssh' | 'wsl' | 'docker', connectionString?: string): Promise<boolean> {\r\n this.isSessionTransitioning = true;\r\n try {\r\n // Show connecting status\r\n if (this.onConnectionStatusUpdate) {\r\n this.onConnectionStatusUpdate({\r\n type: type,\r\n status: 'connecting',\r\n connectionString: connectionString\r\n });\r\n }\r\n\r\n // Detect and connect using the command\r\n const detection = this.commandDetector.detect(command);\r\n if (!detection) {\r\n throw new Error(`Could not detect handler for command: ${command}`);\r\n }\r\n\r\n // Check if we are already in a remote session\r\n const currentContext = this.contextManager.getCurrentContext();\r\n // Setup variables for the new context\r\n let context: SubshellContext;\r\n\r\n if (currentContext.type !== 'local') {\r\n // Nested session: connect from remote\r\n if (detection.handler.connectFromRemote) {\r\n quickLog(`[${new Date().toISOString()}] [Warpify] nesting connection: ${currentContext.type} -> ${type}\\n`);\r\n context = await detection.handler.connectFromRemote(command, this.cwd, currentContext);\r\n } else {\r\n throw new Error(`Nested connections are not supported by the ${type} handler yet.`);\r\n }\r\n } else {\r\n // Local session: connect from local\r\n const initialCwd = type === 'ssh' ? undefined : this.cwd;\r\n const newHandler = detection.handler.createNew();\r\n context = await newHandler.connect(command, initialCwd);\r\n }\r\n\r\n // Connection succeeded! Now we can safely mutate the stacks.\r\n // Save current CWD to stack BEFORE entering the new session state (local or nested)\r\n this.cwdStack.push(this.cwd);\r\n this.connectionCommandStack.push(command);\r\n this.contextManager.pushContext(context);\r\n\r\n // Explicitly sync this.cwd with the new context's CWD to ensure consistency immediately\r\n if (context.metadata.workingDirectory) {\r\n this.cwd = context.metadata.workingDirectory;\r\n }\r\n\r\n // Set up disconnect callback for remote connections\r\n if (context.handler && context.handler.setDisconnectCallback) {\r\n context.handler.setDisconnectCallback((error?: string) => {\r\n this.handleRemoteDisconnect(connectionString || '', type, error);\r\n });\r\n }\r\n\r\n // Show success\r\n if (this.onConnectionStatusUpdate) {\r\n this.onConnectionStatusUpdate({\r\n type: type,\r\n status: 'connected',\r\n connectionString: connectionString\r\n });\r\n }\r\n\r\n // Update CWD to remote working directory\r\n if (this.onCwdChange && context.metadata.workingDirectory) {\r\n this.onCwdChange(context.metadata.workingDirectory);\r\n }\r\n\r\n quickLog(`[${new Date().toISOString()}] [Warpify] Successfully established ${type} connection via ssh2/handler\\n`);\r\n return true;\r\n\r\n } catch (error: any) {\r\n // Connection failed\r\n if (this.onConnectionStatusUpdate) {\r\n this.onConnectionStatusUpdate({\r\n type: type,\r\n status: 'error',\r\n connectionString: connectionString,\r\n error: error.message\r\n });\r\n }\r\n quickLog(`[${new Date().toISOString()}] [Warpify] Failed to establish connection: ${error.message}\\n`);\r\n return false;\r\n } finally {\r\n this.isSessionTransitioning = false;\r\n void this.maybeProcessSessionCommandQueue();\r\n }\r\n }\r\n\r\n /**\r\n * Exit the current remote session and return to parent environment.\r\n * Called by the AI agent's enter_remote_session tool with action=\"exit\".\r\n * @returns Promise<boolean> - true if there was a session to exit\r\n */\r\n async exitRemoteSession(): Promise<boolean> {\r\n const currentContext = this.contextManager.getCurrentContext();\r\n\r\n // Check if we're in a remote session\r\n if (currentContext.type === 'local') {\r\n return false; // No remote session to exit\r\n }\r\n\r\n // Get connection info for logging\r\n const sessionType = currentContext.type;\r\n const metadata = currentContext.metadata as any;\r\n const connectionString = metadata.connectionString ||\r\n metadata.host ||\r\n metadata.containerId ||\r\n metadata.distro ||\r\n metadata.workingDirectory ||\r\n 'unknown';\r\n\r\n quickLog(`[${new Date().toISOString()}] [ExitRemoteSession] Exiting ${sessionType} session: ${connectionString}\\n`);\r\n\r\n // Close the handler if it has a disconnect method\r\n if (currentContext.handler && typeof currentContext.handler.disconnect === 'function') {\r\n try {\r\n await currentContext.handler.disconnect();\r\n } catch (error: any) {\r\n quickLog(`[${new Date().toISOString()}] [ExitRemoteSession] Error during handler disconnect: ${error.message}\\n`);\r\n }\r\n }\r\n\r\n // Pop the context\r\n this.contextManager.popContext();\r\n\r\n // Restore CWD from stack\r\n const previousCwd = this.cwdStack.pop();\r\n const newContext = this.contextManager.getCurrentContext();\r\n\r\n if (previousCwd) {\r\n this.cwd = previousCwd;\r\n } else if (newContext.type !== 'local') {\r\n this.cwd = newContext.metadata.workingDirectory;\r\n }\r\n this.contextManager.updateWorkingDirectory(this.cwd);\r\n\r\n if (newContext.type === 'local') {\r\n this.connectionCommandStack.pop();\r\n }\r\n\r\n // Notify CWD change\r\n if (this.onCwdChange) {\r\n this.onCwdChange(this.cwd);\r\n }\r\n\r\n // Save chat state\r\n this.saveCurrentChat();\r\n\r\n // Update UI connection status\r\n if (this.onConnectionStatusUpdate) {\r\n if (newContext.type === 'local') {\r\n this.onConnectionStatusUpdate({\r\n type: sessionType as 'ssh' | 'wsl' | 'docker',\r\n status: 'disconnected',\r\n connectionString: connectionString\r\n });\r\n } else {\r\n // Still in a nested session\r\n const newMetadata = newContext.metadata as any;\r\n this.onConnectionStatusUpdate({\r\n type: newContext.type as 'ssh' | 'wsl' | 'docker',\r\n status: 'connected',\r\n connectionString: newMetadata.connectionString || newMetadata.host || newMetadata.workingDirectory\r\n });\r\n }\r\n }\r\n\r\n return true;\r\n }\r\n\r\n /**\r\n * Handle disconnection from a remote session\r\n */\r\n private handleRemoteDisconnect(connectionString: string, type: string, error?: string): void {\r\n quickLog(`[${new Date().toISOString()}] [Warpify] Disconnected from ${type} session: ${connectionString} (${error || 'clean exit'})\\n`);\r\n\r\n // Pop the context (this was the session that just ended)\r\n const endedContext = this.contextManager.popContext();\r\n\r\n // Now look at the NEW current context (the parent)\r\n const currentContext = this.contextManager.getCurrentContext();\r\n\r\n // Restore CWD from stack - this handles any nesting level\r\n const previousCwd = this.cwdStack.pop();\r\n if (previousCwd) {\r\n this.cwd = previousCwd;\r\n } else if (currentContext.type !== 'local') {\r\n // Fallback: use parent context's CWD if no stack entry\r\n this.cwd = currentContext.metadata.workingDirectory;\r\n }\r\n this.contextManager.updateWorkingDirectory(this.cwd);\r\n\r\n if (currentContext.type === 'local') {\r\n this.connectionCommandStack.pop();\r\n }\r\n\r\n // Notify CWD change\r\n if (this.onCwdChange) {\r\n this.onCwdChange(this.cwd);\r\n }\r\n\r\n // Save chat state\r\n this.saveCurrentChat();\r\n\r\n // Update UI connection status\r\n if (this.onConnectionStatusUpdate) {\r\n if (currentContext.type === 'local') {\r\n this.onConnectionStatusUpdate({\r\n type: type as any,\r\n status: 'disconnected',\r\n connectionString\r\n });\r\n } else {\r\n // Update status to reflect the parent session\r\n let parentConnString = '';\r\n if (currentContext.type === 'ssh') {\r\n parentConnString = `${currentContext.metadata.username}@${currentContext.metadata.hostname}`;\r\n } else if (currentContext.type === 'wsl') {\r\n parentConnString = currentContext.metadata.distroName || '';\r\n } else if (currentContext.type === 'docker') {\r\n parentConnString = currentContext.metadata.containerId || '';\r\n }\r\n\r\n this.onConnectionStatusUpdate({\r\n type: currentContext.type as any,\r\n status: 'connected',\r\n connectionString: parentConnString\r\n });\r\n }\r\n }\r\n\r\n // If there's an active tool execution (shell running), mark it as error\r\n if (this.onToolExecutionUpdate && this.currentInteractiveProcess) {\r\n this.onToolExecutionUpdate({\r\n toolName: 'execute_command',\r\n status: 'error',\r\n error: `Disconnected from ${type}: ${error || 'Connection lost'}`\r\n });\r\n this.currentInteractiveProcess = undefined;\r\n }\r\n }\r\n\r\n private normalizePromptForInputBar(prompt: string): string {\r\n if (!prompt) return '';\r\n\r\n if (prompt.startsWith(CentaurusCLI.INTERRUPT_SYSTEM_NOTE_PREFIX)) {\r\n return prompt\r\n .slice(CentaurusCLI.INTERRUPT_SYSTEM_NOTE_PREFIX.length)\r\n .trimStart();\r\n }\r\n\r\n return prompt;\r\n }\r\n\r\n /**\r\n * Calculate and update token count based on current conversation history\r\n * This ensures UI is always in sync with the actual AI context\r\n * Uses backend's accurate token counting API (Vertex AI countTokens)\r\n */\r\n private async updateTokenCount(): Promise<void> {\r\n const updateVersion = ++this.tokenCountUpdateVersion;\r\n\r\n try {\r\n // Get current model\r\n const currentModel = this.configManager.get('modelName') || 'gemini-2.5-flash';\r\n\r\n // Prepare messages for token counting\r\n // Backend will automatically include system prompt when counting\r\n // We just send the conversation history\r\n const messagesForCounting = [...this.conversationHistory];\r\n\r\n // Call backend API for accurate token counting\r\n const tokenCount = await this.countTokensForMessages(currentModel, messagesForCounting);\r\n\r\n if (updateVersion !== this.tokenCountUpdateVersion) {\r\n quickLog(`[${new Date().toISOString()}] [updateTokenCount] Ignored stale token count update\\n`);\r\n return;\r\n }\r\n\r\n this.applyTokenCount(tokenCount);\r\n\r\n const logMsg = `\\n============================================================\\n` +\r\n `[TOKEN COUNT CHECK] Model: ${currentModel}\\n` +\r\n `[TOKEN COUNT CHECK] Messages: ${messagesForCounting.length}\\n` +\r\n `[TOKEN COUNT CHECK] Count: ${tokenCount} tokens\\n` +\r\n `============================================================\\n`;\r\n\r\n quickLog(`[${new Date().toISOString()}] [updateTokenCount] ${logMsg}`);\r\n } catch (error) {\r\n if (updateVersion !== this.tokenCountUpdateVersion) {\r\n return;\r\n }\r\n\r\n const currentModel = this.configManager.get('modelName') || 'gemini-2.5-flash';\r\n const estimatedTokens = this.estimateTokenCount(this.conversationHistory);\r\n this.applyTokenCount(estimatedTokens);\r\n quickLog(`[${new Date().toISOString()}] [updateTokenCount] Fallback estimate: ${estimatedTokens} tokens for ${currentModel} (API error: ${error})\\n`);\r\n }\r\n }\r\n\r\n /**\r\n * Get current token count for context limit checking\r\n */\r\n private getCurrentTokenCount(): number {\r\n return this.currentTokenCount;\r\n }\r\n\r\n private applyTokenCount(tokens: number): void {\r\n this.currentTokenCount = tokens;\r\n if (this.onTokenCountUpdate) {\r\n this.onTokenCountUpdate(tokens);\r\n }\r\n }\r\n\r\n private setContextLimitReachedState(reached: boolean): void {\r\n if (this.contextLimitReached === reached) {\r\n return;\r\n }\r\n\r\n this.contextLimitReached = reached;\r\n if (this.onContextLimitReached) {\r\n this.onContextLimitReached(reached);\r\n }\r\n }\r\n\r\n private estimateTokenCount(messages: AIMessage[]): number {\r\n const SYSTEM_PROMPT_ESTIMATE = 14000; // Backend injects ~14K char system prompt\r\n let totalCharacters = 0;\r\n\r\n for (const msg of messages) {\r\n if (typeof msg.content === 'string') {\r\n totalCharacters += msg.content.length;\r\n }\r\n\r\n if (msg.thinking) {\r\n totalCharacters += msg.thinking.length;\r\n }\r\n\r\n if (msg.tool_calls) {\r\n for (const tc of msg.tool_calls) {\r\n totalCharacters += tc.name.length;\r\n if (tc.arguments) {\r\n totalCharacters += JSON.stringify(tc.arguments).length;\r\n }\r\n }\r\n }\r\n\r\n if (msg.role === 'tool' && msg.tool_call_id) {\r\n totalCharacters += msg.tool_call_id.length;\r\n }\r\n }\r\n\r\n const systemPromptChars = messages.length > 0 ? SYSTEM_PROMPT_ESTIMATE : 0;\r\n return Math.ceil((totalCharacters + systemPromptChars) / 4);\r\n }\r\n\r\n private async countTokensForMessages(model: string, messages: AIMessage[]): Promise<number> {\r\n try {\r\n return await apiClient.countTokens(model, messages);\r\n } catch (error) {\r\n const estimated = this.estimateTokenCount(messages);\r\n quickLog(`[${new Date().toISOString()}] [countTokensForMessages] Falling back to estimate (${estimated}) due to API error: ${error}\\n`);\r\n return estimated;\r\n }\r\n }\r\n\r\n setOnSessionQuotaUpdate(callback: (remaining: number, canSend: boolean, timeRemaining: string) => void): void {\r\n this.onSessionQuotaUpdate = callback;\r\n }\r\n\r\n // MCP screen callback setters\r\n setOnMCPAddScreenSetup(callback: () => void): void {\r\n this.onShowMCPAddScreen = callback;\r\n }\r\n\r\n setOnMCPRemoveScreenSetup(callback: (servers: { name: string; command: string; args?: string[]; enabled?: boolean }[]) => void): void {\r\n this.onShowMCPRemoveScreen = callback;\r\n }\r\n\r\n setOnMCPEnableScreenSetup(callback: (servers: { name: string; command: string; args?: string[]; enabled?: boolean }[]) => void): void {\r\n this.onShowMCPEnableScreen = callback;\r\n }\r\n\r\n setOnMCPDisableScreenSetup(callback: (servers: { name: string; command: string; args?: string[]; enabled?: boolean }[]) => void): void {\r\n this.onShowMCPDisableScreen = callback;\r\n }\r\n\r\n setOnMCPListScreenSetup(callback: (servers: Array<{ name: string; enabled: boolean; status: 'connected' | 'disconnected' | 'error' | 'connecting'; tools: Array<{ name: string }> }>) => void): void {\r\n this.onShowMCPListScreen = callback;\r\n }\r\n\r\n // MCP server operation methods (called from UI)\r\n mcpAddServer(config: any): { success: boolean; error?: string } {\r\n if (this.mcpCommandHandler) {\r\n return this.mcpCommandHandler.addServer(config);\r\n }\r\n return { success: false, error: 'MCP not initialized' };\r\n }\r\n\r\n mcpRemoveServer(name: string): void {\r\n if (this.mcpCommandHandler) {\r\n this.mcpCommandHandler.removeServer(name);\r\n }\r\n }\r\n\r\n mcpEnableServer(name: string): void {\r\n if (this.mcpCommandHandler) {\r\n this.mcpCommandHandler.enableServer(name);\r\n }\r\n }\r\n\r\n mcpDisableServer(name: string): void {\r\n if (this.mcpCommandHandler) {\r\n this.mcpCommandHandler.disableServer(name);\r\n }\r\n }\r\n\r\n mcpValidateConfig(jsonString: string): { valid: boolean; config?: any; error?: string } {\r\n if (this.mcpCommandHandler) {\r\n return this.mcpCommandHandler.validateServerConfig(jsonString);\r\n }\r\n return { valid: false, error: 'MCP not initialized' };\r\n }\r\n\r\n /**\r\n * Programmatically add an MCP server, connect to it, and register its tools.\r\n * Used by the add_mcp AI tool for automatic MCP provisioning.\r\n */\r\n async mcpAddAndConnect(config: any, timeoutMs?: number): Promise<{\r\n success: boolean;\r\n serverName: string;\r\n tools: string[];\r\n error?: string;\r\n logs?: string;\r\n }> {\r\n if (this.mcpCommandHandler) {\r\n return this.mcpCommandHandler.addAndConnect(config, timeoutMs);\r\n }\r\n return { success: false, serverName: config?.name || 'unknown', tools: [], error: 'MCP not initialized' };\r\n }\r\n\r\n /**\r\n * Get the names of currently connected MCP servers.\r\n * Used by AIContextInjector to inform the AI about available MCP integrations.\r\n */\r\n getConnectedMCPServerNames(): string[] {\r\n if (this.mcpCommandHandler) {\r\n return this.mcpCommandHandler.getConnectedServerNames();\r\n }\r\n return [];\r\n }\r\n\r\n /**\r\n * Notify UI about session quota status\r\n */\r\n private notifySessionQuotaStatus(): void {\r\n if (this.onSessionQuotaUpdate) {\r\n const remaining = sessionQuotaManager.getRemainingMessages();\r\n const canSend = sessionQuotaManager.canSendMessage();\r\n const timeRemaining = sessionQuotaManager.getFormattedTimeRemaining();\r\n this.onSessionQuotaUpdate(remaining, canSend, timeRemaining);\r\n }\r\n }\r\n\r\n private async initializeMCP(): Promise<void> {\r\n try {\r\n const mcpConfigManager = new MCPConfigManager();\r\n const mcpServerManager = new MCPServerManager();\r\n\r\n this.mcpCommandHandler = new MCPCommandHandler(\r\n mcpConfigManager,\r\n mcpServerManager,\r\n this.toolRegistry\r\n );\r\n\r\n // Wire MCP screen callbacks\r\n this.mcpCommandHandler.setOnShowMCPAddScreen(() => {\r\n if (this.onShowMCPAddScreen) {\r\n this.onShowMCPAddScreen();\r\n }\r\n });\r\n this.mcpCommandHandler.setOnShowMCPRemoveScreen((servers) => {\r\n if (this.onShowMCPRemoveScreen) {\r\n this.onShowMCPRemoveScreen(servers);\r\n }\r\n });\r\n this.mcpCommandHandler.setOnShowMCPEnableScreen((servers) => {\r\n if (this.onShowMCPEnableScreen) {\r\n this.onShowMCPEnableScreen(servers);\r\n }\r\n });\r\n this.mcpCommandHandler.setOnShowMCPDisableScreen((servers) => {\r\n if (this.onShowMCPDisableScreen) {\r\n this.onShowMCPDisableScreen(servers);\r\n }\r\n });\r\n this.mcpCommandHandler.setOnShowMCPListScreen((servers) => {\r\n if (this.onShowMCPListScreen) {\r\n this.onShowMCPListScreen(servers);\r\n }\r\n });\r\n\r\n // Initialize MCP servers and tools\r\n await this.mcpCommandHandler.initializeMCP();\r\n } catch (error: any) {\r\n logWarning(`Failed to initialize MCP: ${error?.message || error}`);\r\n }\r\n }\r\n\r\n writeToShellStdin(input: string): void {\r\n if (this.currentInteractiveProcess) {\r\n try {\r\n this.currentInteractiveProcess.write(input);\r\n } catch (error: any) {\r\n // Silently ignore - process might have already exited\r\n }\r\n }\r\n }\r\n\r\n sendSignalToShell(signal: NodeJS.Signals): void {\r\n if (this.currentInteractiveProcess) {\r\n try {\r\n this.currentInteractiveProcess.signal(signal);\r\n } catch (error: any) {\r\n // Silently ignore - process might have already exited\r\n }\r\n }\r\n }\r\n\r\n killCurrentProcess(): void {\r\n if (this.currentInteractiveProcess && this.currentInteractiveProcess.process.pid) {\r\n try {\r\n const { killProcessTree } = require('./utils/shell.js');\r\n killProcessTree(this.currentInteractiveProcess.process.pid, 'SIGKILL');\r\n } catch (error: any) {\r\n // Ignore\r\n }\r\n }\r\n }\r\n\r\n getPlanMode(): boolean {\r\n return this.planMode;\r\n }\r\n\r\n getCommandMode(): boolean {\r\n return this.commandMode;\r\n }\r\n\r\n /**\r\n * Get current conversation history for shell input agent context\r\n * Returns a copy to prevent modification\r\n */\r\n getConversationHistory(): AIMessage[] {\r\n return [...this.conversationHistory];\r\n }\r\n\r\n getCurrentWorkingDirectory(): string {\r\n return this.cwd;\r\n }\r\n\r\n getCurrentSubshellContext(): SubshellContext {\r\n return this.contextManager.getCurrentContext();\r\n }\r\n\r\n getCurrentInteractiveProcess(): shellUtils.InteractiveProcess | undefined {\r\n return this.currentInteractiveProcess;\r\n }\r\n\r\n /**\r\n * Get the current conversation ID for file uploads\r\n */\r\n getCurrentConversationId(): string | null {\r\n return conversationManager.getCurrentConversationId();\r\n }\r\n\r\n async handlePickerSelection(selection: string, pickerType: 'model' | 'local-model'): Promise<void> {\r\n try {\r\n if (pickerType === 'local-model') {\r\n // Local Ollama model selection\r\n // Selection is the model name (e.g., \"llama3:latest\")\r\n const modelName = selection;\r\n\r\n // Store the local model configuration\r\n this.configManager.set('model', modelName);\r\n this.configManager.set('modelName', modelName);\r\n this.configManager.set('isLocalModel', true);\r\n\r\n // Notify UI of model name change\r\n // Local models don't have a fixed context window, use a reasonable default\r\n if (this.onModelChange) {\r\n this.onModelChange(modelName, 128000); // Most local models have 128k context\r\n }\r\n\r\n const responseMessage = `✅ Switched to local Ollama model: ${modelName}`;\r\n\r\n // Send response back to UI\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback(responseMessage);\r\n }\r\n } else {\r\n // Cloud model selection (existing behavior)\r\n // Selection is the index of the model in models array from backend\r\n const modelsConfig = await fetchModelsConfig();\r\n const modelIndex = parseInt(selection, 10);\r\n\r\n if (isNaN(modelIndex) || modelIndex < 0 || modelIndex >= modelsConfig.models.length) {\r\n throw new Error('Invalid model selection');\r\n }\r\n\r\n const selectedModel = modelsConfig.models[modelIndex];\r\n\r\n // Store only the model ID and name (not the full config with thinkingConfig)\r\n // This prevents caching issues when we update model configs\r\n this.configManager.set('model', selectedModel.id);\r\n this.configManager.set('modelName', selectedModel.name);\r\n this.configManager.set('isLocalModel', false);\r\n\r\n // Notify UI of model name change and contextWindow\r\n if (this.onModelChange) {\r\n this.onModelChange(selectedModel.name, selectedModel.contextWindow);\r\n }\r\n\r\n const responseMessage = `✅ Switched to cloud model: ${selectedModel.name}`;\r\n\r\n // Send response back to UI\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback(responseMessage);\r\n }\r\n }\r\n } catch (error: any) {\r\n // Send error message to UI\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback(`❌ ${error.message}`);\r\n }\r\n }\r\n }\r\n\r\n private sshHandler?: SSHHandler;\r\n\r\n /**\r\n * Notify UI about tool execution status\r\n */\r\n private notifyToolStatus(\r\n toolName: string,\r\n status: 'executing' | 'completed' | 'error',\r\n args?: Record<string, any>,\r\n result?: string,\r\n error?: string\r\n ): void {\r\n // Skip UI status updates for enter_remote_session - the password prompt\r\n // and \"Established Wormhole\" message already provide sufficient feedback\r\n if (toolName === 'enter_remote_session') {\r\n return;\r\n }\r\n\r\n if (this.onToolExecutionUpdate) {\r\n // Get current context for remote prefix\r\n const currentContext = this.contextManager.getCurrentContext();\r\n\r\n // Build remote context prefix for SSH/Docker/WSL\r\n let remoteContext: string | undefined;\r\n if (currentContext.type !== 'local') {\r\n const metadata = currentContext.metadata;\r\n if (currentContext.type === 'ssh' && metadata) {\r\n // SSH: user@hostname\r\n const username = metadata.username || 'user';\r\n const hostname = metadata.hostname || 'remote';\r\n remoteContext = `${username}@${hostname}`;\r\n } else if (currentContext.type === 'wsl' && metadata) {\r\n // WSL: distroName or just wsl\r\n remoteContext = `wsl:${metadata.distroName || 'wsl'}`;\r\n } else if (currentContext.type === 'docker' && metadata) {\r\n // Docker: container id (first 12 chars)\r\n remoteContext = `docker:${metadata.containerId?.substring(0, 12) || 'container'}`;\r\n }\r\n }\r\n\r\n // Add cwd and remoteContext to arguments for execute_command tool\r\n let toolArgs = args;\r\n if (toolName === 'execute_command' && args) {\r\n toolArgs = { ...args, cwd: this.cwd, remoteContext };\r\n } else if (remoteContext && args) {\r\n // For other tools, also add remoteContext if in remote environment\r\n toolArgs = { ...args, remoteContext };\r\n } else if (remoteContext) {\r\n toolArgs = { remoteContext };\r\n }\r\n\r\n this.onToolExecutionUpdate({\r\n toolName,\r\n status,\r\n result,\r\n error,\r\n arguments: toolArgs\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Fire-and-forget update of the live file change breadcrumb.\r\n * Called after each file-modifying tool so the UI updates in real time.\r\n */\r\n private updateFileChangeSummary(): void {\r\n if (!this.onFileChangeSummaryCallback || !this.checkpointManager) return;\r\n\r\n const ctx = this.contextManager.getCurrentContext();\r\n let remoteHandler: RemoteFileHandler | undefined;\r\n if (ctx.type !== 'local' && ctx.handler &&\r\n typeof ctx.handler.readFile === 'function' &&\r\n typeof ctx.handler.writeFile === 'function' &&\r\n typeof ctx.handler.executeCommand === 'function' &&\r\n typeof ctx.handler.isConnected === 'function') {\r\n remoteHandler = ctx.handler as RemoteFileHandler;\r\n }\r\n\r\n this.checkpointManager.getAggregatedSessionChanges(remoteHandler)\r\n .then(sessionChanges => {\r\n if (!sessionChanges || !this.onFileChangeSummaryCallback) return;\r\n const { changes, stats } = sessionChanges;\r\n const fileCount = changes.added.length + changes.modified.length + changes.deleted.length;\r\n if (fileCount > 0) {\r\n const totalInsertions = stats.reduce((sum, s) => sum + s.insertions, 0);\r\n const totalDeletions = stats.reduce((sum, s) => sum + s.deletions, 0);\r\n this.onFileChangeSummaryCallback({ filesChanged: fileCount, insertions: totalInsertions, deletions: totalDeletions });\r\n }\r\n })\r\n .catch(() => {}); // Silently ignore errors\r\n }\r\n\r\n /**\r\n * Truncate large results for AI consumption\r\n */\r\n private truncateResult(result: any, maxLength: number = 500000): any {\r\n const resultStr = typeof result === 'string' ? result : JSON.stringify(result);\r\n if (resultStr.length > maxLength) {\r\n const truncated = resultStr.substring(0, maxLength);\r\n const linesRemoved = (resultStr.match(/\\n/g) || []).length - (truncated.match(/\\n/g) || []).length;\r\n return truncated + `\\n\\n[... truncated ${resultStr.length - maxLength} characters, ~${linesRemoved} lines removed for brevity ...]`;\r\n }\r\n return result;\r\n }\r\n\r\n private calculateUsagePercent(tokenCount: number, maxTokens: number): number {\r\n if (!Number.isFinite(maxTokens) || maxTokens <= 0) {\r\n return 0;\r\n }\r\n return (tokenCount / maxTokens) * 100;\r\n }\r\n\r\n private isUserShellCommandMessage(content: string): boolean {\r\n if (!content) {\r\n return false;\r\n }\r\n\r\n return content.startsWith(CentaurusCLI.USER_SHELL_PREFIX) &&\r\n content.includes(CentaurusCLI.USER_SHELL_OUTPUT_MARKER);\r\n }\r\n\r\n private getAutoCompactionCandidates(): AutoCompactionCandidate[] {\r\n const candidates: AutoCompactionCandidate[] = [];\r\n const toolNameByCallId = new Map<string, string>();\r\n\r\n for (let i = 0; i < this.conversationHistory.length; i++) {\r\n const message = this.conversationHistory[i];\r\n\r\n if (message.role === 'assistant' && Array.isArray(message.tool_calls)) {\r\n for (const toolCall of message.tool_calls) {\r\n if (toolCall?.id && toolCall?.name) {\r\n toolNameByCallId.set(toolCall.id, toolCall.name);\r\n }\r\n }\r\n continue;\r\n }\r\n\r\n if (message.role === 'tool' && message.tool_call_id) {\r\n const toolName = toolNameByCallId.get(message.tool_call_id);\r\n if (toolName === 'execute_command') {\r\n candidates.push({\r\n index: i,\r\n kind: 'execute_command_tool_result',\r\n toolCallId: message.tool_call_id\r\n });\r\n } else if (toolName === 'view_file' || toolName === 'read_file') {\r\n candidates.push({\r\n index: i,\r\n kind: 'read_file_tool_result',\r\n toolCallId: message.tool_call_id\r\n });\r\n }\r\n continue;\r\n }\r\n\r\n if (message.role === 'user' && this.isUserShellCommandMessage(message.content)) {\r\n candidates.push({\r\n index: i,\r\n kind: 'user_shell_command_output'\r\n });\r\n }\r\n }\r\n\r\n return candidates;\r\n }\r\n\r\n private compactTerminalOutputToLastLines(content: string): { content: string; changed: boolean } {\r\n if (!content || content.includes(CentaurusCLI.TERMINAL_COMPACTION_NOTICE)) {\r\n return { content, changed: false };\r\n }\r\n\r\n const normalized = content.replace(/\\r\\n/g, '\\n');\r\n const lines = normalized.split('\\n');\r\n\r\n if (lines.length > 0 && lines[lines.length - 1] === '') {\r\n lines.pop();\r\n }\r\n\r\n const tail = lines.slice(-CentaurusCLI.TERMINAL_LINES_TO_KEEP).join('\\n');\r\n const compactedBody = tail || '(no output)';\r\n const compactedContent = `${compactedBody}\\n\\n${CentaurusCLI.TERMINAL_COMPACTION_NOTICE}`;\r\n\r\n return {\r\n content: compactedContent,\r\n changed: compactedContent !== content\r\n };\r\n }\r\n\r\n private compactUserShellCommandMessage(content: string): { content: string; changed: boolean } {\r\n if (!this.isUserShellCommandMessage(content) || content.includes(CentaurusCLI.TERMINAL_COMPACTION_NOTICE)) {\r\n return { content, changed: false };\r\n }\r\n\r\n const markerIndex = content.indexOf(CentaurusCLI.USER_SHELL_OUTPUT_MARKER);\r\n if (markerIndex < 0) {\r\n return { content, changed: false };\r\n }\r\n\r\n const markerEnd = markerIndex + CentaurusCLI.USER_SHELL_OUTPUT_MARKER.length;\r\n const header = content.slice(0, markerEnd);\r\n const output = content.slice(markerEnd);\r\n const compactedOutput = this.compactTerminalOutputToLastLines(output);\r\n\r\n if (!compactedOutput.changed) {\r\n return { content, changed: false };\r\n }\r\n\r\n const compactedContent = `${header}${compactedOutput.content}`;\r\n return {\r\n content: compactedContent,\r\n changed: compactedContent !== content\r\n };\r\n }\r\n\r\n private applyAutoCompactionCandidate(candidate: AutoCompactionCandidate): boolean {\r\n const message = this.conversationHistory[candidate.index];\r\n if (!message || typeof message.content !== 'string') {\r\n return false;\r\n }\r\n\r\n if (candidate.kind === 'read_file_tool_result') {\r\n if (message.content.trim() === CentaurusCLI.READ_FILE_COMPACTION_MESSAGE) {\r\n return false;\r\n }\r\n message.content = CentaurusCLI.READ_FILE_COMPACTION_MESSAGE;\r\n return true;\r\n }\r\n\r\n if (candidate.kind === 'execute_command_tool_result') {\r\n const compacted = this.compactTerminalOutputToLastLines(message.content);\r\n if (!compacted.changed) {\r\n return false;\r\n }\r\n message.content = compacted.content;\r\n return true;\r\n }\r\n\r\n if (candidate.kind === 'user_shell_command_output') {\r\n const compacted = this.compactUserShellCommandMessage(message.content);\r\n if (!compacted.changed) {\r\n return false;\r\n }\r\n message.content = compacted.content;\r\n return true;\r\n }\r\n\r\n return false;\r\n }\r\n\r\n private appendAutoCompactionNoticeForAgent(): boolean {\r\n const lastMessage = this.conversationHistory[this.conversationHistory.length - 1];\r\n if (lastMessage?.role === 'user' && lastMessage.content === CentaurusCLI.COMPACTION_NOTICE_FOR_AGENT) {\r\n return false;\r\n }\r\n\r\n this.conversationHistory.push({\r\n role: 'user',\r\n content: CentaurusCLI.COMPACTION_NOTICE_FOR_AGENT\r\n });\r\n return true;\r\n }\r\n\r\n private async maybeAutoCompactContext(model: string, maxTokens: number): Promise<AutoCompactionOutcome> {\r\n let beforeUsagePercent = 0;\r\n let afterUsagePercent = 0;\r\n let compactedCount = 0;\r\n let historyChanged = false;\r\n\r\n try {\r\n let tokenCount = await this.countTokensForMessages(model, this.conversationHistory);\r\n this.applyTokenCount(tokenCount);\r\n\r\n beforeUsagePercent = this.calculateUsagePercent(tokenCount, maxTokens);\r\n afterUsagePercent = beforeUsagePercent;\r\n\r\n if (beforeUsagePercent < CentaurusCLI.AUTO_COMPACTION_TRIGGER_PERCENT) {\r\n return {\r\n triggered: false,\r\n historyChanged: false,\r\n compactedCount: 0,\r\n beforeUsagePercent,\r\n afterUsagePercent,\r\n targetReached: afterUsagePercent < CentaurusCLI.AUTO_COMPACTION_TARGET_PERCENT\r\n };\r\n }\r\n\r\n this.notifyToolStatus(\r\n CentaurusCLI.AUTO_COMPACTION_TOOL_NAME,\r\n 'executing',\r\n {\r\n usagePercent: Number(beforeUsagePercent.toFixed(2)),\r\n triggerPercent: CentaurusCLI.AUTO_COMPACTION_TRIGGER_PERCENT,\r\n targetPercent: CentaurusCLI.AUTO_COMPACTION_TARGET_PERCENT\r\n }\r\n );\r\n\r\n const candidates = this.getAutoCompactionCandidates();\r\n for (const candidate of candidates) {\r\n const changed = this.applyAutoCompactionCandidate(candidate);\r\n if (!changed) {\r\n continue;\r\n }\r\n\r\n compactedCount += 1;\r\n historyChanged = true;\r\n\r\n tokenCount = await this.countTokensForMessages(model, this.conversationHistory);\r\n this.applyTokenCount(tokenCount);\r\n afterUsagePercent = this.calculateUsagePercent(tokenCount, maxTokens);\r\n\r\n if (afterUsagePercent < CentaurusCLI.AUTO_COMPACTION_TARGET_PERCENT) {\r\n break;\r\n }\r\n }\r\n\r\n if (compactedCount > 0) {\r\n const noticeAdded = this.appendAutoCompactionNoticeForAgent();\r\n historyChanged = historyChanged || noticeAdded;\r\n\r\n tokenCount = await this.countTokensForMessages(model, this.conversationHistory);\r\n this.applyTokenCount(tokenCount);\r\n afterUsagePercent = this.calculateUsagePercent(tokenCount, maxTokens);\r\n }\r\n\r\n const targetReached = afterUsagePercent < CentaurusCLI.AUTO_COMPACTION_TARGET_PERCENT;\r\n const completionSummary = compactedCount > 0\r\n ? `Auto compacted context: compacted ${compactedCount} item(s). Usage ${beforeUsagePercent.toFixed(1)}% -> ${afterUsagePercent.toFixed(1)}%.`\r\n : `Auto compacted context: no eligible read-file or terminal outputs left to compact. Usage ${afterUsagePercent.toFixed(1)}%.`;\r\n\r\n this.notifyToolStatus(\r\n CentaurusCLI.AUTO_COMPACTION_TOOL_NAME,\r\n 'completed',\r\n {\r\n compactedCount,\r\n beforeUsagePercent: Number(beforeUsagePercent.toFixed(2)),\r\n afterUsagePercent: Number(afterUsagePercent.toFixed(2)),\r\n targetPercent: CentaurusCLI.AUTO_COMPACTION_TARGET_PERCENT,\r\n targetReached\r\n },\r\n completionSummary\r\n );\r\n\r\n quickLog(\r\n `[${new Date().toISOString()}] [AutoCompaction] Triggered at ${beforeUsagePercent.toFixed(1)}%, compacted ${compactedCount} item(s), final usage ${afterUsagePercent.toFixed(1)}%\\n`\r\n );\r\n\r\n return {\r\n triggered: true,\r\n historyChanged,\r\n compactedCount,\r\n beforeUsagePercent,\r\n afterUsagePercent,\r\n targetReached\r\n };\r\n } catch (error) {\r\n const errorMessage = error instanceof Error ? error.message : String(error);\r\n this.notifyToolStatus(\r\n CentaurusCLI.AUTO_COMPACTION_TOOL_NAME,\r\n 'error',\r\n undefined,\r\n undefined,\r\n `Auto compaction failed: ${errorMessage}`\r\n );\r\n quickLog(`[${new Date().toISOString()}] [AutoCompaction] Failed: ${errorMessage}\\n`);\r\n\r\n return {\r\n triggered: true,\r\n historyChanged,\r\n compactedCount,\r\n beforeUsagePercent,\r\n afterUsagePercent,\r\n targetReached: afterUsagePercent < CentaurusCLI.AUTO_COMPACTION_TARGET_PERCENT\r\n };\r\n }\r\n }\r\n\r\n async initialize(): Promise<void> {\r\n // Register tools\r\n this.toolRegistry.register(viewFileTool);\r\n this.toolRegistry.register(writeToFileTool);\r\n this.toolRegistry.register(editFileTool);\r\n this.toolRegistry.register(multiEditFileTool);\r\n this.toolRegistry.register(listDirTool);\r\n this.toolRegistry.register(runCommandTool);\r\n this.toolRegistry.register(grepSearchTool);\r\n this.toolRegistry.register(findFilesTool);\r\n this.toolRegistry.register(getDiffTool);\r\n this.toolRegistry.register(inspectSymbolTool);\r\n this.toolRegistry.register(createPlanTool);\r\n this.toolRegistry.register(markTaskCompleteTool);\r\n this.toolRegistry.register(webSearchTool);\r\n this.toolRegistry.register(fetchUrlTool);\r\n this.toolRegistry.register(taskCompleteTool);\r\n this.toolRegistry.register(readBinaryFileTool);\r\n this.toolRegistry.register(createImageTool);\r\n this.toolRegistry.register(backgroundCommandTool);\r\n this.toolRegistry.register(subAgentTool);\r\n this.toolRegistry.register(enterRemoteSessionTool);\r\n this.toolRegistry.register(workflowTool);\r\n this.toolRegistry.register(fastContextTool);\r\n this.toolRegistry.register(addMcpTool);\r\n\r\n // Initialize SubAgentManager with tool registry\r\n SubAgentManager.initialize(this.toolRegistry);\r\n SubAgentManager.setOnSubAgentCountChange((count) => {\r\n if (this.onSubAgentCountChange) {\r\n this.onSubAgentCountChange(count);\r\n }\r\n });\r\n\r\n // Load configuration\r\n const config = this.configManager.load();\r\n\r\n // Enable backend sync if authenticated\r\n if (apiClient.isAuthenticated()) {\r\n this.configManager.enableBackendSync();\r\n }\r\n\r\n // Initialize subshell handlers\r\n this.sshHandler = new SSHHandler();\r\n this.contextManager.registerHandler('ssh', this.sshHandler);\r\n this.commandDetector.registerHandler(this.sshHandler);\r\n\r\n const wslHandler = new WSLHandler();\r\n this.contextManager.registerHandler('wsl', wslHandler);\r\n this.commandDetector.registerHandler(wslHandler);\r\n\r\n const dockerHandler = new DockerHandler();\r\n this.contextManager.registerHandler('docker', dockerHandler);\r\n this.commandDetector.registerHandler(dockerHandler);\r\n\r\n // Fetch rate limits configuration from backend (async, non-blocking)\r\n // Uses cached values if backend is unreachable\r\n sessionQuotaManager.fetchConfigFromBackend().catch(() => {\r\n // Silently fall back to cached/default config\r\n });\r\n\r\n // Note: No need to initialize AI provider - using backend proxy via aiServiceClient\r\n }\r\n\r\n /**\r\n * Get migration message if configuration was migrated\r\n * Returns null if no migration occurred\r\n */\r\n getMigrationMessage(): string | null {\r\n if (this.configManager.wasMigrationPerformed()) {\r\n // Reset flag so message only shows once\r\n this.configManager.resetMigrationFlag();\r\n\r\n return `\r\n╔════════════════════════════════════════════════════════════════════════════╗\r\n║ CONFIGURATION UPDATE NOTICE ║\r\n╚════════════════════════════════════════════════════════════════════════════╝\r\n\r\nYour Centaurus CLI configuration has been automatically updated to the new format.\r\n\r\n📋 What Changed:\r\n • API keys are now managed centrally by the backend service\r\n • Local API key storage has been removed from your configuration\r\n • All AI requests now go through the backend for improved security\r\n\r\n🔐 Authentication Required:\r\n • You must be signed in to use AI features\r\n • Your authentication is already active for this session\r\n\r\n✅ Your Preferences Preserved:\r\n • Model selection: ${this.configManager.get('model') || 'gemini-2.5-flash'}\r\n • Other settings remain unchanged\r\n\r\nℹ️ This is a one-time migration. Your configuration is now up to date.\r\n\r\nPress Enter to continue...\r\n`;\r\n }\r\n\r\n return null;\r\n }\r\n\r\n /**\r\n * Check if user is authenticated\r\n */\r\n isAuthenticated(): boolean {\r\n return apiClient.isAuthenticated();\r\n }\r\n\r\n /**\r\n * Start a new conversation in the backend\r\n * @deprecated Backend conversation creation is no longer needed since messages are stored locally\r\n * This function is kept as a no-op for compatibility\r\n */\r\n private async ensureConversationStarted(): Promise<void> {\r\n // No-op: Backend conversation creation has been disabled\r\n // Conversations are now managed locally via local-chat-storage.ts\r\n return;\r\n }\r\n\r\n /**\r\n * Save a message to the backend\r\n * @deprecated Messages are now stored locally only via saveCurrentChat()\r\n * This function is kept as a no-op for compatibility\r\n */\r\n private async saveMessageToBackend(role: 'user' | 'assistant' | 'system', content: string): Promise<void> {\r\n // No-op: Messages are stored locally only\r\n // Backend message storage has been disabled to avoid \"failed to fetch\" errors\r\n // All conversation history is persisted via local-chat-storage.ts\r\n return;\r\n }\r\n\r\n getModel(): string {\r\n const config = this.configManager.load();\r\n // Return the stored model name, or fall back to model ID\r\n return config.modelName || config.model || 'gemini-2.5-flash';\r\n }\r\n\r\n /**\r\n * Cancel the current AI request\r\n */\r\n cancelCurrentRequest(): void {\r\n if (this.currentAbortController) {\r\n this.requestIntentionallyAborted = true;\r\n this.currentAbortController.abort();\r\n this.currentAbortController = undefined;\r\n }\r\n }\r\n\r\n /**\r\n * Embed file descriptions into conversation history messages.\r\n * When the backend generates a new file description (non-Gemini path),\r\n * it sends a file_descriptions event. We find messages in conversation history\r\n * that contain the matching [FILE: name (gs://...)] marker and append a\r\n * [FILE_DESCRIPTION_CACHE: gs://...] block next to it. On subsequent turns,\r\n * the backend will parse these blocks and skip re-describing the file.\r\n */\r\n private embedFileDescriptionsInHistory(\r\n descriptions: Record<string, { description: string; filename: string; mimeType: string }>\r\n ): void {\r\n const gcsUris = Object.keys(descriptions);\r\n if (gcsUris.length === 0) return;\r\n\r\n try {\r\n quickLog(`[${new Date().toISOString()}] [CLI] Embedding ${gcsUris.length} file description(s) into chat history\\n`);\r\n } catch (e) { }\r\n\r\n for (const gcsUri of gcsUris) {\r\n const { description } = descriptions[gcsUri];\r\n const cacheBlock = `\\n[FILE_DESCRIPTION_CACHE: ${gcsUri}]${description}[/FILE_DESCRIPTION_CACHE]`;\r\n\r\n // Find the message in history that contains this GCS URI marker\r\n for (let i = 0; i < this.conversationHistory.length; i++) {\r\n const msg = this.conversationHistory[i];\r\n if (typeof msg.content !== 'string') continue;\r\n\r\n // Check if this message has the file marker for this gcsUri and doesn't already have a cache block\r\n if (msg.content.includes(gcsUri) && !msg.content.includes(`[FILE_DESCRIPTION_CACHE: ${gcsUri}]`)) {\r\n this.conversationHistory[i] = {\r\n ...msg,\r\n content: msg.content + cacheBlock,\r\n };\r\n try {\r\n quickLog(`[${new Date().toISOString()}] [CLI] Embedded description for ${gcsUri} in message index ${i}\\n`);\r\n } catch (e) { }\r\n break; // Each URI should only appear in one message\r\n }\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Clean up orphaned tool_calls from conversation history.\r\n * This validates the ENTIRE history and removes any assistant messages \r\n * where tool_calls don't have matching tool result messages.\r\n * \r\n * Vertex AI / Claude APIs require that every assistant message with tool_calls\r\n * has matching tool result messages immediately following it.\r\n */\r\n private cleanupOrphanedToolCalls(): void {\r\n if (this.conversationHistory.length === 0) return;\r\n\r\n let cleanedAny = false;\r\n let iterations = 0;\r\n const maxIterations = 20; // Safety limit to prevent infinite loops\r\n\r\n // Keep cleaning until no more orphans are found\r\n // (removing one orphan may expose another)\r\n while (iterations < maxIterations) {\r\n iterations++;\r\n let foundOrphan = false;\r\n\r\n // Scan through history to find ALL assistant messages with tool_calls\r\n for (let i = 0; i < this.conversationHistory.length; i++) {\r\n const msg = this.conversationHistory[i];\r\n\r\n if (msg.role !== 'assistant' || !msg.tool_calls || msg.tool_calls.length === 0) {\r\n continue;\r\n }\r\n\r\n // Collect all tool_call IDs from this assistant message\r\n const expectedToolCallIds = new Set(msg.tool_calls.map((tc: any) => tc.id));\r\n\r\n // Check if ALL tool_calls have matching tool result messages after this message\r\n // Tool results must come AFTER the assistant message, before the next user/assistant message\r\n let j = i + 1;\r\n while (j < this.conversationHistory.length) {\r\n const nextMsg = this.conversationHistory[j];\r\n\r\n // If we hit a user or assistant message, stop looking for tool results\r\n if (nextMsg.role === 'user' || nextMsg.role === 'assistant') {\r\n break;\r\n }\r\n\r\n // If it's a tool result, check if it matches one of our expected IDs\r\n if (nextMsg.role === 'tool' && nextMsg.tool_call_id) {\r\n expectedToolCallIds.delete(nextMsg.tool_call_id);\r\n }\r\n\r\n j++;\r\n }\r\n\r\n // If there are still unmatched tool_calls, this is an orphan\r\n if (expectedToolCallIds.size > 0) {\r\n try {\r\n quickLog(`[${new Date().toISOString()}] [CLI] Found orphaned tool_calls at index ${i}: ${Array.from(expectedToolCallIds).join(', ')}\\n`);\r\n } catch (e) { }\r\n\r\n // Remove this assistant message and all tool results up to (but not including) the next user/assistant message\r\n const removeCount = j - i;\r\n this.conversationHistory.splice(i, removeCount);\r\n\r\n foundOrphan = true;\r\n cleanedAny = true;\r\n break; // Restart scan from beginning since indices changed\r\n }\r\n }\r\n\r\n if (!foundOrphan) {\r\n break; // No more orphans found, we're done\r\n }\r\n }\r\n\r\n if (cleanedAny) {\r\n try {\r\n quickLog(`[${new Date().toISOString()}] [CLI] Completed history cleanup after ${iterations} iteration(s), ${this.conversationHistory.length} messages remaining\\n`);\r\n } catch (e) { }\r\n }\r\n }\r\n\r\n /**\r\n * Strip thinking blocks from ALL assistant messages in the conversation history.\r\n * \r\n * This is called at the START of a new user request to clear thinking from\r\n * previous tasks. During the current agent loop, thinking is preserved for\r\n * all turns to help the AI maintain reasoning context.\r\n * \r\n * Pattern: \r\n * - New user request starts → strip ALL thinking from history\r\n * - During agent loop (multi-turn tool execution) → keep ALL thinking\r\n * - This allows AI to remember reasoning for current task but not old tasks\r\n */\r\n private stripThinkingFromHistory(): void {\r\n for (let i = 0; i < this.conversationHistory.length; i++) {\r\n const msg = this.conversationHistory[i];\r\n if (msg.role === 'assistant' && msg.thinking) {\r\n // Remove thinking from the message in place\r\n delete msg.thinking;\r\n }\r\n }\r\n }\r\n\r\n private notifyCommandQueueUpdate(): void {\r\n if (this.onCommandQueueUpdateCallback) {\r\n this.onCommandQueueUpdateCallback([...this.sessionCommandQueue]);\r\n }\r\n }\r\n\r\n private canProcessSessionCommandQueue(): boolean {\r\n return !this.currentAbortController\r\n && this.interruptQueue.length === 0\r\n && !this.isReconnecting\r\n && !this.isSessionTransitioning;\r\n }\r\n\r\n private enqueueSessionCommand(command: string): void {\r\n const trimmedCommand = command.trim();\r\n if (!trimmedCommand) {\r\n return;\r\n }\r\n\r\n this.sessionCommandQueue.push(trimmedCommand);\r\n this.notifyCommandQueueUpdate();\r\n void this.maybeProcessSessionCommandQueue();\r\n }\r\n\r\n private async maybeProcessSessionCommandQueue(): Promise<void> {\r\n if (this.isProcessingSessionCommandQueue || !this.canProcessSessionCommandQueue()) {\r\n return;\r\n }\r\n\r\n const nextCommand = this.sessionCommandQueue.shift();\r\n if (!nextCommand) {\r\n return;\r\n }\r\n\r\n this.notifyCommandQueueUpdate();\r\n this.isProcessingSessionCommandQueue = true;\r\n\r\n try {\r\n if (this.onQueuedCommandDispatchedCallback) {\r\n this.onQueuedCommandDispatchedCallback(nextCommand);\r\n }\r\n\r\n await this.handleCommandModeExecution(nextCommand);\r\n } finally {\r\n this.isProcessingSessionCommandQueue = false;\r\n\r\n if (this.canProcessSessionCommandQueue() && this.sessionCommandQueue.length > 0) {\r\n setTimeout(() => {\r\n void this.maybeProcessSessionCommandQueue();\r\n }, 0);\r\n }\r\n }\r\n }\r\n\r\n async handleMessage(message: string, options: { isolatedWorkflow?: boolean; interruptCleanText?: string } = {}): Promise<void> {\r\n // Prevent messages from being processed while remote reconnection is ongoing\r\n if (this.isReconnecting) {\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback('⏳ Please wait until the chat is fully reconnected before sending a message.');\r\n }\r\n return;\r\n }\r\n\r\n // Handle command mode - execute commands directly\r\n if (this.commandMode) {\r\n // Record command step if workflow learning mode is active\r\n if (this.workflowLearningActive) {\r\n this.recordWorkflowStep('command', message);\r\n }\r\n this.enqueueSessionCommand(message);\r\n return;\r\n }\r\n\r\n // Handle background mode - execute commands in background\r\n if (this.backgroundMode) {\r\n // Record command step if workflow learning mode is active\r\n if (this.workflowLearningActive) {\r\n this.recordWorkflowStep('command', message);\r\n }\r\n this.handleBackgroundModeExecution(message);\r\n return;\r\n }\r\n\r\n // Handle slash commands\r\n if (message.startsWith('/')) {\r\n await this.handleSlashCommand(message);\r\n return;\r\n }\r\n\r\n const {\r\n augmentedPrompt,\r\n referencedRules,\r\n missingRuleNames\r\n } = resolveRuleReferences(message);\r\n const resolvedInterruptCleanText = options.interruptCleanText\r\n ? resolveRuleReferences(options.interruptCleanText).augmentedPrompt\r\n : undefined;\r\n\r\n if (referencedRules.length > 0) {\r\n quickLog(\r\n `[${new Date().toISOString()}] [Rules] Injected ${referencedRules.length} rule(s): ${referencedRules.map(rule => rule.name).join(', ')}\\n`\r\n );\r\n }\r\n\r\n if (missingRuleNames.length > 0) {\r\n quickLog(\r\n `[${new Date().toISOString()}] [Rules] Missing referenced rules: ${missingRuleNames.join(', ')}\\n`\r\n );\r\n }\r\n\r\n const resolvedMessage = augmentedPrompt.replace(/@([^\\s@]+)/g, (match, relPath) => {\r\n // @rules: references are already handled by resolveRuleReferences above\r\n if (relPath.startsWith('rules:')) return match;\r\n // Resolve relative file/directory paths to absolute paths for the AI\r\n const absPath = path.resolve(this.cwd, relPath);\r\n if (fs.existsSync(absPath)) return absPath;\r\n return match;\r\n });\r\n\r\n // Record step if workflow learning mode is active\r\n // AI prompts are recorded as instruction steps\r\n if (this.workflowLearningActive) {\r\n this.recordWorkflowStep('instruction', message);\r\n }\r\n\r\n // Check authentication\r\n if (!apiClient.isAuthenticated()) {\r\n throw new Error('Authentication required. Please sign in to use AI features.');\r\n }\r\n\r\n // Check session quota before making any AI request\r\n if (!sessionQuotaManager.canSendMessage()) {\r\n const timeRemaining = sessionQuotaManager.getFormattedTimeRemaining();\r\n const message = `\\n⚠️ Session quota reached. You have used all ${sessionQuotaManager.getCurrentConfig().maxMessagesPerSession} messages for this session.\\n\\nYour quota will reset in ${timeRemaining}.\\n\\nYou can still use:\\n • Slash commands (e.g., /help, /session-limits, /exit)\\n • Terminal commands (in Command mode)\\n\\nUse /session-limits to check your quota status.`;\r\n\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(message);\r\n }\r\n\r\n // Notify UI about quota status\r\n this.notifySessionQuotaStatus();\r\n return;\r\n }\r\n\r\n // Check context window limit before accepting new messages\r\n // Get current model's context window\r\n const currentModel = this.configManager.get('modelName') || 'gemini-2.5-flash';\r\n const { getModelContextWindowSync } = await import('./config/models.js');\r\n const maxTokens = getModelContextWindowSync(currentModel);\r\n\r\n // Calculate current token usage percentage\r\n // We need to estimate tokens for the new message too\r\n const newMessageChars = resolvedMessage.length;\r\n const estimatedNewMessageTokens = Math.ceil(newMessageChars / 4);\r\n\r\n // Get current token count from state (updated by updateTokenCount)\r\n const currentTokens = this.getCurrentTokenCount();\r\n const projectedTokens = currentTokens + estimatedNewMessageTokens;\r\n const usagePercent = (projectedTokens / maxTokens) * 100;\r\n\r\n // Always allow sending messages. If usage is high, auto-compaction runs first.\r\n this.setContextLimitReachedState(false);\r\n if (usagePercent >= CentaurusCLI.AUTO_COMPACTION_TRIGGER_PERCENT) {\r\n quickLog(\r\n `[${new Date().toISOString()}] [handleMessage] High context before user send (${usagePercent.toFixed(1)}%, ${projectedTokens}/${maxTokens}). Running auto-compaction.\\n`\r\n );\r\n\r\n const compactionOutcome = await this.maybeAutoCompactContext(currentModel, maxTokens);\r\n const refreshedTokens = this.getCurrentTokenCount();\r\n const refreshedProjectedTokens = refreshedTokens + estimatedNewMessageTokens;\r\n const refreshedUsagePercent = this.calculateUsagePercent(refreshedProjectedTokens, maxTokens);\r\n\r\n quickLog(\r\n `[${new Date().toISOString()}] [handleMessage] Pre-send compaction: triggered=${compactionOutcome.triggered}, compacted=${compactionOutcome.compactedCount}, projected usage now ${refreshedUsagePercent.toFixed(1)}% (${refreshedProjectedTokens}/${maxTokens})\\n`\r\n );\r\n }\r\n\r\n // Cancel any active request when a new message comes in\r\n // This enables \"interrupt and replace\" - new message takes priority\r\n if (this.currentAbortController) {\r\n // INSTEAD of aborting, enqueue the message as an interrupt.\r\n const queuedMessage = options.interruptCleanText || message;\r\n this.interruptQueue.push(queuedMessage);\r\n\r\n if (this.onInterruptQueueUpdateCallback) {\r\n this.onInterruptQueueUpdateCallback(this.interruptQueue);\r\n }\r\n\r\n quickLog(`[${new Date().toISOString()}] [handleMessage] Added message to interrupt queue. Queue length: ${this.interruptQueue.length}\\n`);\r\n return;\r\n }\r\n\r\n\r\n // Store original request if in planning mode (for execution phase after approval)\r\n if (this.planMode && !this.pendingPlanRequest) {\r\n this.pendingPlanRequest = resolvedMessage;\r\n }\r\n\r\n // Notify UI that this message is now actively being processed.\r\n // Only fire for DIRECT (non-queued) messages here.\r\n // For queued interrupts, the finally block fires this BEFORE calling handleMessage,\r\n // so we must not fire it again here — that would cause the user bubble to appear twice.\r\n if (!options.interruptCleanText && this.onQueuedMessageDispatchedCallback) {\r\n this.onQueuedMessageDispatchedCallback(message);\r\n }\r\n\r\n // Build the user message content - inject plan mode instructions if active\r\n let userMessageContent = resolvedMessage;\r\n\r\n // When plan mode is active, explicitly inject planning instructions into the user message\r\n // This ensures the AI creates a plan even when toggled mid-conversation\r\n if (this.planMode && !getCurrentPlan()?.isActive) {\r\n const planModePrefix = `[PLAN MODE ACTIVE - You MUST call create_plan first before any implementation tools]\r\n\r\nUser Request: ${resolvedMessage}\r\n\r\nCRITICAL INSTRUCTIONS:\r\n1. You are in PLANNING MODE - DO NOT implement anything directly\r\n2. First explore the codebase using view_file, list_dir, grep_search to understand the context\r\n3. Then call create_plan with a detailed plan including:\r\n - designSummary: Your understanding of the codebase\r\n - tasks: Hierarchical tasks with subtasks\r\n4. Wait for user approval before implementing\r\n\r\nDO NOT use write_to_file, edit_file, or execute_command until the plan is approved.`;\r\n userMessageContent = planModePrefix;\r\n }\r\n\r\n // NEW USER REQUEST: Strip thinking from previous tasks\r\n // This clears thinking from the previous agent loop but thinking will be\r\n // preserved for all turns within the current agent loop\r\n this.stripThinkingFromHistory();\r\n\r\n // Add user message to history, using clean text if it's an interrupt\r\n const messageToStore = resolvedInterruptCleanText || userMessageContent;\r\n this.conversationHistory.push({\r\n role: 'user',\r\n content: messageToStore,\r\n });\r\n\r\n // Capture the abort controller for THIS request locally so we are entirely immune to race conditions\r\n if (!this.currentAbortController) {\r\n this.currentAbortController = new AbortController();\r\n }\r\n const myAbortController = this.currentAbortController;\r\n\r\n // Calculate start index for AI context (0 for normal, current index for isolated workflow)\r\n const contextStartIndex = options.isolatedWorkflow ? this.conversationHistory.length - 1 : 0;\r\n\r\n // Helper to get messages for AI context respecting isolation\r\n const getMessagesForContext = () => {\r\n const msgs = options.isolatedWorkflow ? this.conversationHistory.slice(contextStartIndex) : [...this.conversationHistory];\r\n\r\n // If this is an interrupt, we must ensure the AI sees the wrapped message for this specific turn\r\n if (options.interruptCleanText && msgs.length > 0) {\r\n // Swap the last user message's content with the wrapped message just for the AI request\r\n const lastMsg = msgs[msgs.length - 1];\r\n if (lastMsg.role === 'user') {\r\n // We map over to not mutate the original history array object\r\n msgs[msgs.length - 1] = { ...lastMsg, content: userMessageContent };\r\n }\r\n }\r\n return msgs;\r\n };\r\n\r\n // Messages are stored locally only - no backend persistence needed\r\n // Local storage is handled by saveCurrentChat() which saves to ~/.centaurus/chats/\r\n\r\n // Start logging session and log user message\r\n conversationLogger.startSession();\r\n conversationLogger.logUserMessage(message);\r\n\r\n // Initialize and start checkpoint for revert functionality\r\n if (!this.checkpointManager) {\r\n this.checkpointManager = new CheckpointManager();\r\n }\r\n\r\n // Ensure we have a chat ID for checkpoints (even for the first message)\r\n if (!this.currentChatId) {\r\n this.currentChatId = localChatStorage.generateChatId();\r\n }\r\n\r\n if (this.currentChatId) {\r\n this.ensureCheckpointManagerForChat(this.currentChatId);\r\n this.currentCheckpointId = undefined;\r\n // Start checkpoint snapshot for this user message\r\n const currentContext = this.contextManager.getCurrentContext();\r\n\r\n // Build remote session info and handler for non-local contexts\r\n let remoteSessionInfo: RemoteSessionInfo | undefined;\r\n let remoteHandler: RemoteFileHandler | undefined;\r\n if (currentContext.type !== 'local' && currentContext.handler) {\r\n const metadata = currentContext.metadata;\r\n remoteSessionInfo = {\r\n hostname: metadata.hostname,\r\n username: metadata.username,\r\n sessionId: currentContext.sessionId,\r\n connectionString: metadata.hostname\r\n ? `${metadata.username || 'user'}@${metadata.hostname}`\r\n : metadata.distroName || metadata.containerId || undefined,\r\n };\r\n // Use the handler if it implements the RemoteFileHandler interface\r\n if (typeof currentContext.handler.readFile === 'function' &&\r\n typeof currentContext.handler.writeFile === 'function' &&\r\n typeof currentContext.handler.executeCommand === 'function' &&\r\n typeof currentContext.handler.isConnected === 'function') {\r\n remoteHandler = currentContext.handler as RemoteFileHandler;\r\n }\r\n }\r\n\r\n try {\r\n const checkpointPrompt = options.interruptCleanText || message;\r\n const checkpointStartPromise = this.checkpointManager.startCheckpoint({\r\n prompt: checkpointPrompt,\r\n cwd: this.cwd,\r\n contextType: currentContext.type as 'local' | 'ssh' | 'wsl' | 'docker',\r\n conversationIndex: this.conversationHistory.length - 1,\r\n uiMessageIndex: this.uiMessageHistory.length,\r\n remoteSessionInfo,\r\n handler: remoteHandler,\r\n });\r\n let checkpoint: CheckpointMeta | null = null;\r\n\r\n if (currentContext.type !== 'local') {\r\n const remoteCheckpointTimeoutMs = 2000;\r\n const timeoutMarker = '__checkpoint_timeout__' as const;\r\n let timeoutHandle: NodeJS.Timeout | null = null;\r\n\r\n const timeoutPromise: Promise<typeof timeoutMarker> = new Promise((resolve) => {\r\n timeoutHandle = setTimeout(() => resolve(timeoutMarker), remoteCheckpointTimeoutMs);\r\n });\r\n\r\n const checkpointOrTimeout = await Promise.race([\r\n checkpointStartPromise,\r\n timeoutPromise,\r\n ]);\r\n\r\n if (timeoutHandle) {\r\n clearTimeout(timeoutHandle);\r\n }\r\n\r\n if (checkpointOrTimeout === timeoutMarker) {\r\n quickLog(\r\n `[${new Date().toISOString()}] [Checkpoint] Remote checkpoint start exceeded ${remoteCheckpointTimeoutMs}ms (${currentContext.type}); continuing without blocking AI turn\\n`\r\n );\r\n\r\n void checkpointStartPromise\r\n .then((lateCheckpoint) => {\r\n if (!lateCheckpoint || !this.checkpointManager) {\r\n return;\r\n }\r\n\r\n // Keep the late checkpoint — it's still valid for diff/revert\r\n // The AI turn was already started without blocking, but this checkpoint\r\n // data will be available for any subsequent get_diff or /revert operations\r\n this.currentCheckpointId = lateCheckpoint.id;\r\n quickLog(\r\n `[${new Date().toISOString()}] [Checkpoint] Late checkpoint ${lateCheckpoint.id} resolved after timeout — keeping for diff/revert\\n`\r\n );\r\n })\r\n .catch((lateError: any) => {\r\n quickLog(\r\n `[${new Date().toISOString()}] [Checkpoint] Late checkpoint creation failed after timeout: ${lateError?.message || lateError}\\n`\r\n );\r\n });\r\n } else {\r\n checkpoint = checkpointOrTimeout;\r\n }\r\n } else {\r\n checkpoint = await checkpointStartPromise;\r\n }\r\n\r\n if (checkpoint) {\r\n this.currentCheckpointId = checkpoint.id;\r\n quickLog(`[${new Date().toISOString()}] [Checkpoint] Started checkpoint ${checkpoint.id} (${currentContext.type}) for: \"${message.slice(0, 50)}...\"\\n`);\r\n }\r\n } catch (error: any) {\r\n quickLog(`[${new Date().toISOString()}] [Checkpoint] Failed to start checkpoint: ${error.message}\\n`);\r\n }\r\n }\r\n\r\n try {\r\n const tools = this.toolRegistry.getSchemas();\r\n const context = {\r\n cwd: this.cwd,\r\n contextManager: this.contextManager,\r\n cliAdapter: this, // Pass CLI adapter reference for interactive process management\r\n checkpointManager: this.checkpointManager, // For session-aware diff tool\r\n currentCheckpointId: this.currentCheckpointId, // Active checkpoint for this request\r\n currentChatId: this.currentChatId || undefined, // Current chat session ID (for session-wide diffs)\r\n requireApproval: async (message: string, risky: boolean, preview?: { type: 'code' | 'diff'; content: string; language?: string; fullDiff?: string }, operationType?: 'write_file' | 'edit_file' | 'execute_command', operationDetails?: Record<string, any>) => {\r\n // Special bypass for shell input to running processes:\r\n // If the AI is sending input to an existing shell (via shell_input), we bypass the separate approval step.\r\n // The user already implicitly approved the interaction by running the command in agent control mode.\r\n if (operationType === 'execute_command' && operationDetails?.shell_input) {\r\n return true;\r\n }\r\n\r\n if (this.onToolApprovalRequest) {\r\n return await this.onToolApprovalRequest({ message, risky, preview, operationType, operationDetails });\r\n }\r\n return true; // Auto-approve if no callback registered\r\n },\r\n onStreamingOutput: (chunk: string, type: 'stdout' | 'stderr', toolName?: string) => {\r\n // Forward streaming output to UI callback\r\n if (this.onToolStreamingOutput) {\r\n this.onToolStreamingOutput({ toolName: toolName || 'execute_command', chunk, type });\r\n }\r\n },\r\n };\r\n\r\n // Get selected model ID from config, then look up full config from models-config.json\r\n const config = this.configManager.load();\r\n const selectedModelId = config.model || 'gemini-2.5-flash';\r\n\r\n // Look up the full model config (including thinkingConfig) from backend\r\n // This ensures we always use the latest config\r\n const selectedModelConfig = await getModelConfigByIdAndName(selectedModelId, config.modelName || '');\r\n const selectedModel = selectedModelId;\r\n const selectedModelThinkingConfig = selectedModelConfig?.thinkingConfig;\r\n const modelContextWindow = selectedModelConfig?.contextWindow || maxTokens;\r\n\r\n // Build messages array WITHOUT system prompt - backend will inject it\r\n // The backend uses cli-system-prompt.md for CLI clients\r\n // We pass environmentContext and mode separately so backend can inject them\r\n\r\n // SAFETY: Clean up any orphaned tool calls before making AI request\r\n // This prevents \"improperly formed request\" errors from corrupted history\r\n this.cleanupOrphanedToolCalls();\r\n\r\n let messages: AIMessage[] = getMessagesForContext();\r\n\r\n // Inject subshell context if in a subshell environment\r\n const currentContext = this.contextManager.getCurrentContext();\r\n messages = this.aiContextInjector.injectSubshellContext(messages, currentContext);\r\n\r\n // Inject MCP auto-provisioning context so the AI knows it can dynamically add MCP servers\r\n const connectedMcpServers = this.getConnectedMCPServerNames();\r\n messages = this.aiContextInjector.injectMCPContext(messages, connectedMcpServers);\r\n\r\n // Build environment context to send to backend\r\n // Initial context - will be updated in loop\r\n let environmentContext = this.getEnvironmentContext();\r\n const mode = this.getMode();\r\n\r\n let finalAssistantMessage = '';\r\n const MAX_TURNS = 500; // Allow up to 500 turns for complex tasks\r\n let turnCount = 0;\r\n let thoughtStartTime: number | null = null; // Track when thinking started\r\n let thoughtContent = ''; // Accumulate thought content during streaming\r\n let currentTurnThinking = ''; // Persist thinking for the current turn to attach to assistant message\r\n let currentTurnThinkingSignature = ''; // Persist thinking signature for Claude extended thinking\r\n\r\n // ANTI-LOOP: Track duplicate tool calls to detect infinite loops\r\n const MAX_DUPLICATE_CALLS = 2; // Max times same operation allowed on same target\r\n const fileWriteTracker: Map<string, number> = new Map(); // Track writes per file\r\n const recentToolCalls: Array<{ name: string; targetFile: string; turn: number }> = [];\r\n\r\n // ANTI-LOOP: Track ALL duplicate tool calls (not just file ops)\r\n const toolCallTracker: Map<string, number> = new Map(); // Hash -> count\r\n const MAX_IDENTICAL_TOOL_CALLS = 3; // Max times exact same tool call allowed\r\n\r\n // Create AbortController for this request (if not already created during interruption handling)\r\n if (!this.currentAbortController) {\r\n this.currentAbortController = new AbortController();\r\n }\r\n\r\n // Clean up any orphaned tool_calls from a previous aborted request\r\n // This prevents 400 Bad Request errors when sending to the backend\r\n this.cleanupOrphanedToolCalls();\r\n\r\n // Multi-turn tool execution loop\r\n while (turnCount < MAX_TURNS) {\r\n turnCount++;\r\n\r\n // Track session quota - each AI call in the agent loop counts as 1 message\r\n sessionQuotaManager.incrementMessageCount();\r\n this.notifySessionQuotaStatus();\r\n\r\n // Check if session quota is now exhausted after incrementing\r\n if (!sessionQuotaManager.canSendMessage() && turnCount > 1) {\r\n // Quota exhausted mid-loop, stop and inform user\r\n const timeRemaining = sessionQuotaManager.getFormattedTimeRemaining();\r\n const quotaMessage = `\\n\\n⚠️ **Session quota reached** during agent execution.\\n\\nYou have used all ${sessionQuotaManager.getCurrentConfig().maxMessagesPerSession} messages for this session.\\nQuota will reset in ${timeRemaining}.\\n\\nYour current task may be incomplete. You can resume when your quota resets.\\n\\nUse /session-limits to check your quota status.`;\r\n\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback(quotaMessage);\r\n }\r\n\r\n logWarning('Agent loop stopped due to session quota exhaustion');\r\n return;\r\n }\r\n\r\n // Refresh environment context to capture any CWD changes from previous turns\r\n // This is sent to backend which will inject it into the system prompt\r\n environmentContext = this.getEnvironmentContext();\r\n\r\n let assistantMessage = '';\r\n let toolCalls: any[] = [];\r\n\r\n // REAL-TIME TOOL EXECUTION: Track execution state and results during streaming\r\n const inStreamToolResults: any[] = []; // Results from tools executed during streaming\r\n const inStreamHandledIds = new Set<string>(); // IDs of tools already executed in-stream\r\n let toolsExecutedInStream = false; // Flag to indicate tools were executed during stream\r\n let pendingTextBuffer = ''; // Buffer for text while tool is executing\r\n let isToolExecuting = false; // Flag to pause text streaming during tool execution\r\n\r\n // DEBUG: Log message history state before AI call\r\n const messageStats = {\r\n totalMessages: messages.length,\r\n totalCharacters: messages.reduce((sum, m) => {\r\n let len = typeof m.content === 'string' ? m.content.length : 0;\r\n // Include thinking content (internal reasoning)\r\n if (m.thinking) {\r\n len += m.thinking.length;\r\n }\r\n // Include tool calls (name + arguments)\r\n if (m.tool_calls) {\r\n m.tool_calls.forEach(tc => {\r\n len += tc.name.length;\r\n // Arguments are JSON stringified in the payload\r\n if (tc.arguments) {\r\n len += JSON.stringify(tc.arguments).length;\r\n }\r\n });\r\n }\r\n // Include tool_call_id for tool messages\r\n if (m.role === 'tool' && m.tool_call_id) {\r\n len += m.tool_call_id.length;\r\n }\r\n return sum + len;\r\n }, 0),\r\n byRole: {\r\n system: messages.filter(m => m.role === 'system').length,\r\n user: messages.filter(m => m.role === 'user').length,\r\n assistant: messages.filter(m => m.role === 'assistant').length,\r\n tool: messages.filter(m => m.role === 'tool').length\r\n },\r\n assistantWithToolCalls: messages.filter(m => m.role === 'assistant' && m.tool_calls && m.tool_calls.length > 0).length\r\n };\r\n try {\r\n quickLog(`[${new Date().toISOString()}] [CLI] === TURN ${turnCount} AI CALL ===\\n`);\r\n quickLog(`[${new Date().toISOString()}] [CLI] Message history: ${messageStats.totalMessages} messages, ${messageStats.totalCharacters} chars\\n`);\r\n quickLog(`[${new Date().toISOString()}] [CLI] By role: system=${messageStats.byRole.system}, user=${messageStats.byRole.user}, assistant=${messageStats.byRole.assistant}, tool=${messageStats.byRole.tool}\\n`);\r\n quickLog(`[${new Date().toISOString()}] [CLI] Assistant messages with tool_calls: ${messageStats.assistantWithToolCalls}\\n`);\r\n } catch (e) { }\r\n\r\n // Update token count using accurate API\r\n // This will use backend's Vertex AI countTokens for precision\r\n this.updateTokenCount().catch(err => {\r\n quickLog(`[${new Date().toISOString()}] [CLI] Failed to update token count: ${err}\\n`);\r\n });\r\n\r\n // Stream AI response from backend\r\n // Backend will inject system prompt automatically with environment context\r\n for await (const chunk of aiServiceClient.streamChat(selectedModel, messages, tools, environmentContext, mode, selectedModelThinkingConfig, myAbortController.signal)) {\r\n // Handle file_descriptions chunks — embed descriptions into conversation history\r\n // so they are sent back on subsequent turns and the backend can skip re-describing.\r\n if (chunk.type === 'file_descriptions') {\r\n this.embedFileDescriptionsInHistory(chunk.descriptions);\r\n continue;\r\n }\r\n\r\n // Handle error chunks\r\n if (chunk.type === 'error') {\r\n // Check if this is an abort situation (user cancelled or sent new message)\r\n if (chunk.code === 'TIMEOUT' && (myAbortController as any).isIntentionalAbort) {\r\n if (!(myAbortController as any).isReplacement) {\r\n // Clean up orphaned tool_calls from conversation history only if NOT a replacement.\r\n // If it's a replacement, the newer handleMessage already cleaned up before starting.\r\n this.cleanupOrphanedToolCalls();\r\n }\r\n // Gracefully exit - request was intentionally cancelled\r\n return;\r\n }\r\n conversationLogger.logError('AI Stream', new Error(chunk.message));\r\n throw new Error(chunk.message);\r\n }\r\n\r\n // Handle thought chunks (internal reasoning)\r\n if (chunk.type === 'thought') {\r\n conversationLogger.logThoughtChunk(chunk.content);\r\n\r\n if (!thoughtStartTime) {\r\n thoughtStartTime = Date.now();\r\n }\r\n thoughtContent += chunk.content;\r\n\r\n // Send thought to UI callback if available\r\n if (this.onThoughtStreamCallback) {\r\n this.onThoughtStreamCallback(chunk.content);\r\n }\r\n continue;\r\n }\r\n\r\n // Handle thinking_signature chunks (Claude extended thinking)\r\n if (chunk.type === 'thinking_signature') {\r\n // Store the signature for this turn - it must be passed back with thinking content\r\n currentTurnThinkingSignature = chunk.signature;\r\n continue;\r\n }\r\n\r\n // Handle text chunks\r\n if (chunk.type === 'text') {\r\n // If we were thinking and now got text, finalize the thought\r\n if (thoughtStartTime) {\r\n const thinkingDuration = Math.round((Date.now() - thoughtStartTime) / 1000);\r\n conversationLogger.logThoughtComplete(thinkingDuration);\r\n if (this.onThoughtCompleteCallback) {\r\n this.onThoughtCompleteCallback(thinkingDuration);\r\n }\r\n // Capture thinking for this turn before clearing\r\n currentTurnThinking = thoughtContent;\r\n thoughtStartTime = null;\r\n thoughtContent = '';\r\n }\r\n\r\n // Filter out <TASK_COMPLETE> markers (AI shouldn't output these)\r\n let filteredContent = chunk.content;\r\n filteredContent = filteredContent.replace(/<\\/?TASK_COMPLETE>/gi, '');\r\n filteredContent = filteredContent.trim();\r\n\r\n if (filteredContent) {\r\n assistantMessage += filteredContent;\r\n conversationLogger.logAITextChunk(filteredContent);\r\n\r\n // REAL-TIME TOOL EXECUTION: If a tool is executing, accumulate text\r\n // This text will be flushed after the tool completes\r\n if (isToolExecuting) {\r\n pendingTextBuffer += filteredContent;\r\n } else {\r\n // Normal streaming - send chunk to UI in real-time\r\n if (this.onResponseStreamCallback) {\r\n this.onResponseStreamCallback(filteredContent);\r\n }\r\n }\r\n }\r\n }\r\n\r\n // Handle tool call chunks\r\n if (chunk.type === 'tool_call') {\r\n const toolCall = chunk.toolCall;\r\n\r\n // Kiro/Claude compatibility: Parse string arguments early so they are objects throughout the pipeline\r\n // This ensures logging, UI updates, and tool execution all see the parsed object\r\n if (toolCall.arguments && typeof toolCall.arguments === 'string') {\r\n try {\r\n toolCall.arguments = JSON.parse(toolCall.arguments);\r\n } catch (e) {\r\n // Ignore parsing error, will be handled by downstream logic\r\n }\r\n }\r\n\r\n // Debug: Log every tool_call chunk received\r\n try {\r\n quickLog(`[${new Date().toISOString()}] [CLI] *** TOOL_CALL CHUNK RECEIVED (REAL-TIME): ${toolCall?.name || 'unknown'}\\n`);\r\n } catch (e) { }\r\n\r\n conversationLogger.logToolCall(toolCall?.name || 'unknown', toolCall?.id || 'unknown', toolCall?.arguments || {});\r\n\r\n // If we were thinking and now got a tool call, finalize the thought\r\n if (thoughtStartTime) {\r\n const thinkingDuration = Math.round((Date.now() - thoughtStartTime) / 1000);\r\n conversationLogger.logThoughtComplete(thinkingDuration);\r\n if (this.onThoughtCompleteCallback) {\r\n this.onThoughtCompleteCallback(thinkingDuration);\r\n }\r\n // Capture thinking for this turn before clearing\r\n currentTurnThinking = thoughtContent;\r\n thoughtStartTime = null;\r\n thoughtContent = '';\r\n }\r\n toolCalls.push(chunk.toolCall);\r\n\r\n // SPECIAL TOOLS: Skip in-stream execution for tools that need post-stream handling\r\n // These tools have special logic (setting flags, clearing state, etc.) that must run post-stream\r\n const SPECIAL_TOOLS = ['task_complete', 'create_plan', 'mark_task_complete'];\r\n if (SPECIAL_TOOLS.includes(toolCall.name)) {\r\n // Just notify UI with pending status, execute in post-stream loop\r\n if (this.onToolExecutionUpdate) {\r\n this.onToolExecutionUpdate({\r\n toolName: toolCall.name,\r\n status: 'pending',\r\n arguments: toolCall.arguments\r\n });\r\n }\r\n continue; // Skip to next chunk, handle this tool in post-stream loop\r\n }\r\n\r\n // Mark that we're executing a tool (text will accumulate)\r\n isToolExecuting = true;\r\n toolsExecutedInStream = true;\r\n\r\n // REAL-TIME EXECUTION: Execute tool immediately during streaming\r\n // This reduces latency by not waiting for the entire stream to finish\r\n try {\r\n\r\n\r\n // Extract and display reason_text if present (skip for task_complete and shell_input)\r\n const reasonText = toolCall.arguments.reason_text;\r\n // Don't show reason text for shell inputs (hidden from history per user request)\r\n const isShellInput = toolCall.name === 'execute_command' && toolCall.arguments.shell_input;\r\n\r\n if (reasonText && !isShellInput && this.onResponseStreamCallback) {\r\n this.onResponseStreamCallback(reasonText + '\\n\\n');\r\n }\r\n\r\n // Show 'executing' status immediately\r\n this.notifyToolStatus(toolCall.name, 'executing', toolCall.arguments);\r\n\r\n // Log tool execution start\r\n conversationLogger.logToolExecutionStart(toolCall.name, toolCall.id);\r\n\r\n // Execute the tool (it will request approval if needed via requireApproval callback)\r\n // SPECIAL: Intercept sub_agent spawn to enforce approval\r\n if (toolCall.name === 'sub_agent' && toolCall.arguments?.action === 'spawn') {\r\n const approved = await context.requireApproval(\r\n `Spawn Sub-Agent`,\r\n true, // risky\r\n undefined,\r\n 'execute_command',\r\n { command: `spawn sub-agent` }\r\n );\r\n if (!approved) {\r\n throw new Error('User rejected sub-agent spawn request');\r\n }\r\n }\r\n\r\n const result = await this.toolRegistry.execute(toolCall.name, toolCall.arguments, context);\r\n\r\n if (result.success) {\r\n conversationLogger.logToolResult(toolCall.name, toolCall.id, result.result, true);\r\n\r\n // Notify UI: tool succeeded\r\n this.notifyToolStatus(toolCall.name, 'completed', toolCall.arguments, result.result);\r\n\r\n // Update live file change breadcrumb after file-modifying tools\r\n if (['write_to_file', 'edit_file', 'multi_edit_file'].includes(toolCall.name)) {\r\n this.updateFileChangeSummary();\r\n }\r\n\r\n // Parse and truncate result for AI\r\n let parsedResult = result.result;\r\n if (typeof result.result === 'string') {\r\n try {\r\n parsedResult = JSON.parse(result.result);\r\n } catch {\r\n parsedResult = result.result;\r\n }\r\n }\r\n\r\n // Sanitize context for AI consumption (strip ANSI, compact file writes)\r\n const sanitizedResult = sanitizeForContext(toolCall.name, parsedResult, toolCall.arguments);\r\n\r\n // Log the sanitized version for debugging purposes\r\n conversationLogger.logToolResult(`${toolCall.name} (SANITIZED_CONTEXT)`, toolCall.id, sanitizedResult, true);\r\n\r\n inStreamToolResults.push({\r\n tool_call_id: toolCall.id,\r\n name: toolCall.name,\r\n result: this.truncateResult(sanitizedResult),\r\n });\r\n } else {\r\n conversationLogger.logToolResult(toolCall.name, toolCall.id, null, false, result.error);\r\n\r\n // Notify UI: tool failed\r\n this.notifyToolStatus(toolCall.name, 'error', toolCall.arguments, undefined, result.error);\r\n\r\n inStreamToolResults.push({\r\n tool_call_id: toolCall.id,\r\n name: toolCall.name,\r\n result: `Error: ${result.error}`,\r\n });\r\n }\r\n\r\n inStreamHandledIds.add(toolCall.id);\r\n } catch (error: any) {\r\n conversationLogger.logError(`Tool execution: ${toolCall.name}`, error);\r\n this.notifyToolStatus(toolCall.name, 'error', toolCall.arguments, undefined, error.message);\r\n\r\n inStreamToolResults.push({\r\n tool_call_id: toolCall.id,\r\n name: toolCall.name,\r\n result: `Error: ${error.message}`,\r\n });\r\n inStreamHandledIds.add(toolCall.id);\r\n }\r\n\r\n // Tool execution complete - flush pending text\r\n isToolExecuting = false;\r\n if (pendingTextBuffer && this.onResponseStreamCallback) {\r\n this.onResponseStreamCallback(pendingTextBuffer);\r\n pendingTextBuffer = '';\r\n }\r\n\r\n // Debug: Log after execution\r\n try {\r\n quickLog(`[${new Date().toISOString()}] [CLI] *** TOOL EXECUTED IN-STREAM: ${toolCall?.name || 'unknown'}\\n`);\r\n } catch (e) { }\r\n }\r\n\r\n // Handle done chunk\r\n if (chunk.type === 'done') {\r\n // If we were thinking and stream ended, finalize the thought\r\n if (thoughtStartTime) {\r\n const thinkingDuration = Math.round((Date.now() - thoughtStartTime) / 1000);\r\n conversationLogger.logThoughtComplete(thinkingDuration);\r\n if (this.onThoughtCompleteCallback) {\r\n this.onThoughtCompleteCallback(thinkingDuration);\r\n }\r\n // Capture thinking for this turn before clearing\r\n currentTurnThinking = thoughtContent;\r\n thoughtStartTime = null;\r\n thoughtContent = '';\r\n }\r\n\r\n // Log AI text completion\r\n conversationLogger.logAITextComplete();\r\n conversationLogger.logStreamEnd('done');\r\n break;\r\n }\r\n } // End of stream loop\r\n\r\n // Log loop state after stream ends\r\n conversationLogger.logLoopState(turnCount, {\r\n toolCallCount: toolCalls.length,\r\n assistantMessageLength: assistantMessage.length,\r\n hasToolCalls: toolCalls.length > 0,\r\n willContinue: toolCalls.length > 0,\r\n });\r\n\r\n // If there are tool calls, execute them\r\n if (toolCalls.length > 0) {\r\n // CRITICAL: AI should ONLY communicate via reason_text and task_complete summary\r\n // Any text output alongside tool calls should be suppressed\r\n // EXCEPTION: If task_complete is in the tool calls, preserve assistantMessage \r\n // because task_complete can have an empty summary and rely on streamed text\r\n const hasTaskComplete = toolCalls.some(tc => tc.name === 'task_complete');\r\n if (assistantMessage && assistantMessage.trim() && !hasTaskComplete) {\r\n // Suppress text output - AI should only use reason_text\r\n assistantMessage = ''; // Clear ALL text output - AI should only use reason_text\r\n }\r\n\r\n // Tool call limit removed - let AI use as many tools as needed per turn\r\n\r\n const toolResults: any[] = [...inStreamToolResults]; // Start with in-stream results\r\n const handledToolCallIds = new Set<string>(); // Only for special tools (create_plan, mark_task_complete)\r\n let userCancelledOperation = false;\r\n let taskCompleted = false;\r\n let taskCompleteSummary = '';\r\n\r\n for (let i = 0; i < toolCalls.length; i++) {\r\n const toolCall = toolCalls[i];\r\n\r\n // REAL-TIME EXECUTION: Skip tools that were already executed in-stream\r\n if (inStreamHandledIds.has(toolCall.id)) {\r\n try {\r\n quickLog(`[${new Date().toISOString()}] [CLI] *** SKIPPING TOOL (already executed in-stream): ${toolCall.name}\\n`);\r\n } catch (e) { }\r\n continue;\r\n }\r\n\r\n // Debug: Log which tool we're about to execute\r\n try {\r\n quickLog(`[${new Date().toISOString()}] [CLI] *** ABOUT TO EXECUTE TOOL [${i + 1}/${toolCalls.length}]: ${toolCalls[i].name}\\n`);\r\n } catch (e) { }\r\n try {\r\n // Check if this is task_complete FIRST (before displaying anything)\r\n if (toolCall.name === 'task_complete') {\r\n // SUBAGENT BLOCKING: Check if any sub-agents are still running\r\n const runningSubAgents = SubAgentManager.getRunningSubAgents();\r\n if (runningSubAgents.length > 0) {\r\n // Block task_complete and provide feedback\r\n const agentIds = runningSubAgents.map(a => a.id).join(', ');\r\n toolResults.push({\r\n tool_call_id: toolCall.id,\r\n name: toolCall.name,\r\n result: `Cannot complete task: ${runningSubAgents.length} sub-agent(s) still running. IDs: ${agentIds}. Check their status periodically with sub_agent(action=\"status\", agent_id=\"...\") and wait for completion before calling task_complete.`,\r\n });\r\n handledToolCallIds.add(toolCall.id);\r\n continue; // Skip task_complete execution, keep loop running\r\n }\r\n\r\n taskCompleted = true;\r\n conversationLogger.logTaskComplete('');\r\n\r\n // task_complete no longer has a summary parameter\r\n // The AI streams all response text BEFORE calling task_complete()\r\n // So we just preserve whatever assistantMessage was already streamed\r\n\r\n // Execute the tool for proper result handling\r\n\r\n\r\n await this.toolRegistry.execute(toolCall.name, toolCall.arguments, context);\r\n\r\n // Clear the plan when task is complete\r\n clearPlan();\r\n\r\n // Stop processing remaining tools\r\n break;\r\n }\r\n\r\n if (toolCall.name === 'create_plan') {\r\n // Execute the tool to create the plan\r\n const execResult = await this.toolRegistry.execute(toolCall.name, toolCall.arguments, context);\r\n // Extract the actual result string (toolRegistry.execute returns { success, result })\r\n const result = execResult.success ? String(execResult.result) : `Error: ${execResult.error}`;\r\n\r\n // Parse the PLAN_CREATED response to get the plan\r\n if (typeof result === 'string' && result.startsWith('PLAN_CREATED:')) {\r\n const planJson = result.substring('PLAN_CREATED:'.length);\r\n try {\r\n const plan = JSON.parse(planJson) as Plan;\r\n\r\n // Notify UI that a plan was created\r\n if (this.onPlanCreated) {\r\n this.onPlanCreated(plan);\r\n }\r\n\r\n // If we have approval callback, ask for approval\r\n if (this.onPlanApprovalRequest) {\r\n const approved = await this.onPlanApprovalRequest(plan);\r\n\r\n if (approved) {\r\n // Approve and activate the plan\r\n approvePlan();\r\n\r\n // Suppress any text output\r\n assistantMessage = '';\r\n\r\n // Switch out of plan mode to execution mode\r\n this.planMode = false;\r\n if (this.onPlanModeChange) {\r\n this.onPlanModeChange(false);\r\n }\r\n\r\n // Add assistant message with plan tool call to history\r\n const planAssistantMsg: AIMessage = {\r\n role: 'assistant',\r\n content: '',\r\n tool_calls: [toolCall],\r\n };\r\n if (currentTurnThinking) {\r\n planAssistantMsg.thinking = currentTurnThinking;\r\n }\r\n if (currentTurnThinkingSignature) {\r\n planAssistantMsg.thinkingSignature = currentTurnThinkingSignature;\r\n }\r\n this.conversationHistory.push(planAssistantMsg);\r\n\r\n // Add plan approval response\r\n this.conversationHistory.push({\r\n role: 'tool',\r\n tool_call_id: toolCall.id,\r\n content: 'Plan approved by user. Now switching to execution mode.',\r\n });\r\n\r\n // Mark this tool call as handled so it's not duplicated\r\n handledToolCallIds.add(toolCall.id);\r\n\r\n // Add user message that includes ONLY current phase context for execution\r\n // This implements phased execution - AI only sees one task at a time\r\n const phaseContext = getPhaseContextForPrompt();\r\n const originalRequest = this.pendingPlanRequest || message;\r\n const executionPrompt = `${phaseContext}\\n\\nOriginal Request: ${originalRequest}\\n\\nComplete the current task. After finishing each subtask, call mark_task_complete with the subtask number (e.g., \"1.1\"). When all subtasks are done, the main task will automatically complete and you'll receive the next task.`;\r\n\r\n this.conversationHistory.push({\r\n role: 'user',\r\n content: executionPrompt,\r\n });\r\n\r\n // Clear pending plan request\r\n this.pendingPlanRequest = null;\r\n\r\n // Update messages array for this turn\r\n messages = getMessagesForContext();\r\n\r\n // Continue the loop - AI will now execute with plan context\r\n continue;\r\n } else {\r\n // User wants to edit - stop the loop, they'll provide feedback\r\n clearPlan();\r\n finalAssistantMessage = 'Plan editing requested. Please provide your feedback or modifications.';\r\n taskCompleted = true;\r\n break;\r\n }\r\n } else {\r\n // No approval callback - add the tool result to history and wait for user response\r\n // This ensures the AI doesn't get stuck in a silent loop\r\n const planAssistantMsg: AIMessage = {\r\n role: 'assistant',\r\n content: '',\r\n tool_calls: [toolCall],\r\n };\r\n if (currentTurnThinking) {\r\n planAssistantMsg.thinking = currentTurnThinking;\r\n }\r\n if (currentTurnThinkingSignature) {\r\n planAssistantMsg.thinkingSignature = currentTurnThinkingSignature;\r\n }\r\n this.conversationHistory.push(planAssistantMsg);\r\n\r\n this.conversationHistory.push({\r\n role: 'tool',\r\n tool_call_id: toolCall.id,\r\n content: `Plan created: \"${plan.title}\" with ${plan.steps.length} tasks. Waiting for user approval.`,\r\n });\r\n\r\n // Mark this tool call as handled so it's not duplicated\r\n handledToolCallIds.add(toolCall.id);\r\n\r\n // Update messages for next iteration\r\n messages = [...this.conversationHistory];\r\n\r\n // Stop and wait for user to approve\r\n finalAssistantMessage = `Plan created: ${plan.title}. Please approve to continue.`;\r\n taskCompleted = true;\r\n break;\r\n }\r\n } catch (parseError: any) {\r\n // Log error and add error result to history so AI knows\r\n logWarning(`Failed to parse plan: ${parseError?.message || parseError}`);\r\n\r\n // CRITICAL: Add tool result even on parse error to prevent silent loop\r\n const errorAssistantMsg: AIMessage = {\r\n role: 'assistant',\r\n content: '',\r\n tool_calls: [toolCall],\r\n };\r\n if (currentTurnThinking) {\r\n errorAssistantMsg.thinking = currentTurnThinking;\r\n }\r\n if (currentTurnThinkingSignature) {\r\n errorAssistantMsg.thinkingSignature = currentTurnThinkingSignature;\r\n }\r\n this.conversationHistory.push(errorAssistantMsg);\r\n\r\n this.conversationHistory.push({\r\n role: 'tool',\r\n tool_call_id: toolCall.id,\r\n content: `Error parsing plan: ${parseError}. Please try again with valid plan format.`,\r\n });\r\n\r\n // Mark this tool call as handled so it's not duplicated\r\n handledToolCallIds.add(toolCall.id);\r\n\r\n messages = getMessagesForContext();\r\n }\r\n } else {\r\n // Tool returned non-PLAN_CREATED result - add it to history\r\n const resultAssistantMsg: AIMessage = {\r\n role: 'assistant',\r\n content: '',\r\n tool_calls: [toolCall],\r\n };\r\n if (currentTurnThinking) {\r\n resultAssistantMsg.thinking = currentTurnThinking;\r\n }\r\n if (currentTurnThinkingSignature) {\r\n resultAssistantMsg.thinkingSignature = currentTurnThinkingSignature;\r\n }\r\n this.conversationHistory.push(resultAssistantMsg);\r\n\r\n this.conversationHistory.push({\r\n role: 'tool',\r\n tool_call_id: toolCall.id,\r\n content: result || 'create_plan executed but returned empty result.',\r\n });\r\n\r\n // Mark this tool call as handled so it's not duplicated\r\n handledToolCallIds.add(toolCall.id);\r\n\r\n messages = getMessagesForContext();\r\n }\r\n continue;\r\n }\r\n\r\n if (toolCall.name === 'mark_task_complete') {\r\n // Execute the tool\r\n const execResult = await this.toolRegistry.execute(toolCall.name, toolCall.arguments, context);\r\n // Extract the actual result string (toolRegistry.execute returns { success, result })\r\n const result = execResult.success ? String(execResult.result) : `Error: ${execResult.error}`;\r\n\r\n // Parse the TASK_COMPLETED response\r\n if (typeof result === 'string' && result.startsWith('TASK_COMPLETED:')) {\r\n const completionJson = result.substring('TASK_COMPLETED:'.length);\r\n try {\r\n const completion = JSON.parse(completionJson);\r\n const currentPlanData = getCurrentPlan();\r\n\r\n if (currentPlanData && this.onTaskCompleted) {\r\n // Handle both main task completion and subtask completion\r\n const taskNumParts = String(completion.taskNumber).split('.');\r\n const mainTaskNum = parseInt(taskNumParts[0], 10) - 1;\r\n const task = currentPlanData.steps[mainTaskNum];\r\n if (task) {\r\n this.onTaskCompleted(\r\n task,\r\n completion.taskNumber,\r\n completion.totalCount,\r\n completion.completionNote,\r\n completion.taskDescription // Pass the actual task/subtask description\r\n );\r\n }\r\n }\r\n\r\n // Notify UI about completed task/subtask\r\n const displayType = completion.type === 'subtask' ? 'Subtask' : 'Task';\r\n this.notifyToolStatus(toolCall.name, 'completed', toolCall.arguments,\r\n `${displayType} ${completion.taskNumber} of ${completion.totalCount} completed: ${completion.taskDescription}`);\r\n\r\n // Check if we need to advance to next phase (when main task is complete)\r\n if (completion.mainTaskComplete || completion.type === 'task') {\r\n // A main task is complete - check if there's a next phase\r\n const nextPhase = getCurrentPhase();\r\n\r\n if (nextPhase && !completion.allComplete) {\r\n // Inject next phase context for the AI\r\n const phaseContext = getPhaseContextForPrompt();\r\n\r\n // Add tool result for current task\r\n toolResults.push({\r\n tool_call_id: toolCall.id,\r\n name: toolCall.name,\r\n result: `Task completed. Moving to Task ${nextPhase.taskNumber}: ${nextPhase.task.description}`,\r\n });\r\n\r\n // Add the tool call and result to history\r\n const nextPhaseAssistantMsg: AIMessage = {\r\n role: 'assistant',\r\n content: '',\r\n tool_calls: [toolCall],\r\n };\r\n if (currentTurnThinking) {\r\n nextPhaseAssistantMsg.thinking = currentTurnThinking;\r\n }\r\n if (currentTurnThinkingSignature) {\r\n nextPhaseAssistantMsg.thinkingSignature = currentTurnThinkingSignature;\r\n }\r\n this.conversationHistory.push(nextPhaseAssistantMsg);\r\n this.conversationHistory.push({\r\n role: 'tool',\r\n tool_call_id: toolCall.id,\r\n content: `Task completed. Now starting Task ${nextPhase.taskNumber}.`,\r\n });\r\n\r\n // Add next phase instructions\r\n this.conversationHistory.push({\r\n role: 'user',\r\n content: `${phaseContext}\\n\\nContinue with the next task. Complete each subtask and call mark_task_complete for each one.`,\r\n });\r\n\r\n // Mark as handled\r\n handledToolCallIds.add(toolCall.id);\r\n\r\n // Update messages and continue\r\n messages = getMessagesForContext();\r\n continue;\r\n }\r\n }\r\n\r\n // Add to tool results for subtask completion or final completion\r\n toolResults.push({\r\n tool_call_id: toolCall.id,\r\n name: toolCall.name,\r\n result: completion.allComplete\r\n ? 'All tasks completed! Output your summary of what was accomplished, then call task_complete().'\r\n : completion.nextSubtask\r\n ? `Subtask ${completion.taskNumber} completed. Next subtask: ${completion.nextSubtask}`\r\n : completion.nextTask\r\n ? `Task ${completion.taskNumber} completed. Next: ${completion.nextTask}`\r\n : `Task ${completion.taskNumber} completed.`,\r\n });\r\n\r\n // If all tasks are complete, prompt AI to call task_complete\r\n if (completion.allComplete) {\r\n toolResults[toolResults.length - 1].result =\r\n 'All tasks in the plan are now completed! Output your summary of what was accomplished, then call task_complete().';\r\n }\r\n } catch (parseError: any) {\r\n logWarning(`Failed to parse task completion: ${parseError?.message || parseError}`);\r\n }\r\n }\r\n continue;\r\n }\r\n\r\n // ANTI-LOOP: Only detect EXACT identical tool calls (same name + same args)\r\n // This is a safety net - the backend fix (functionCall in messages) should prevent loops\r\n // We use a HIGH threshold to avoid blocking legitimate multi-step operations\r\n const toolArgsToTrack = { ...toolCall.arguments };\r\n delete toolArgsToTrack.reason_text; // Ignore reason_text - only matters for params\r\n const toolCallHash = `${toolCall.name}:${JSON.stringify(toolArgsToTrack)}`;\r\n const toolCallCount = (toolCallTracker.get(toolCallHash) || 0) + 1;\r\n toolCallTracker.set(toolCallHash, toolCallCount);\r\n\r\n // Only stop after 5 IDENTICAL calls (same tool + same exact args)\r\n if (toolCallCount > 5) {\r\n // Log the loop detection\r\n conversationLogger.logNarrationDetection('duplicate_tool_loop', {\r\n toolName: toolCall.name,\r\n callCount: toolCallCount,\r\n maxAllowed: 5\r\n });\r\n\r\n // Force task completion with a helpful message\r\n const loopMessage = `⚠️ **Loop Detected**: The AI called \\`${toolCall.name}\\` with identical parameters ${toolCallCount} times.\\n\\n` +\r\n `The system has stopped to prevent an infinite loop.\\n\\n` +\r\n `**Tip**: Try rephrasing your request or ask about a specific aspect of the task.`;\r\n\r\n if (this.onResponseStreamCallback) {\r\n this.onResponseStreamCallback(loopMessage);\r\n }\r\n\r\n // Set as completed and break\r\n finalAssistantMessage = loopMessage;\r\n taskCompleted = true;\r\n break;\r\n }\r\n\r\n // NOTE: File-specific loop detection REMOVED\r\n // The backend now includes functionCall parts in assistant messages,\r\n // so the AI should properly remember its previous actions and not repeat them.\r\n // Extract and display reason_text if present (but skip for task_complete)\r\n const reasonText = toolCall.arguments.reason_text;\r\n if (reasonText && this.onResponseStreamCallback) {\r\n this.onResponseStreamCallback(reasonText + '\\n\\n');\r\n }\r\n\r\n // Determine the effective CWD for this command (use remote context CWD if applicable)\r\n const currentCtx = this.contextManager.getCurrentContext();\r\n const effectiveCwd = currentCtx.type !== 'local'\r\n ? currentCtx.metadata?.workingDirectory || '~'\r\n : this.cwd;\r\n\r\n // Build remote context prefix for SSH/Docker/WSL (for path display in UI)\r\n let remoteContext: string | undefined;\r\n if (currentCtx.type !== 'local') {\r\n const metadata = currentCtx.metadata;\r\n if (currentCtx.type === 'ssh' && metadata) {\r\n remoteContext = `${metadata.username || 'user'}@${metadata.hostname || 'remote'}`;\r\n } else if (currentCtx.type === 'wsl' && metadata) {\r\n remoteContext = `wsl:${metadata.distroName || 'wsl'}`;\r\n } else if (currentCtx.type === 'docker' && metadata) {\r\n remoteContext = `docker:${metadata.containerId?.substring(0, 12) || 'container'}`;\r\n }\r\n }\r\n\r\n // Notify UI: tool executing\r\n if (this.onToolExecutionUpdate) {\r\n let toolArgs = { ...toolCall.arguments, remoteContext };\r\n\r\n // Special handling for execute_command\r\n if (toolCall.name === 'execute_command') {\r\n // Add effective CWD\r\n toolArgs.cwd = effectiveCwd;\r\n }\r\n\r\n this.onToolExecutionUpdate({\r\n toolName: toolCall.name,\r\n status: 'executing',\r\n arguments: toolArgs\r\n });\r\n }\r\n\r\n // Log tool execution start\r\n conversationLogger.logToolExecutionStart(toolCall.name, toolCall.id);\r\n\r\n // Execute the tool (it will request approval if needed)\r\n // SPECIAL: Intercept sub_agent spawn to enforce approval\r\n if (toolCall.name === 'sub_agent' && toolCall.arguments?.action === 'spawn') {\r\n const approved = await context.requireApproval(\r\n `Spawn Sub-Agent`,\r\n true, // risky\r\n undefined,\r\n 'execute_command',\r\n { command: `spawn sub-agent` }\r\n );\r\n if (!approved) {\r\n // User rejected - log result as error and skip execution\r\n conversationLogger.logToolResult(toolCall.name, toolCall.id, null, false, 'User rejected');\r\n\r\n // Notify UI: tool failed\r\n if (this.onToolExecutionUpdate) {\r\n this.onToolExecutionUpdate({\r\n toolName: toolCall.name,\r\n status: 'error',\r\n error: 'User rejected',\r\n arguments: toolCall.arguments\r\n });\r\n }\r\n\r\n toolResults.push({\r\n tool_call_id: toolCall.id,\r\n name: toolCall.name,\r\n result: 'User rejected sub-agent spawn request',\r\n error: 'User rejected'\r\n });\r\n continue;\r\n }\r\n }\r\n const result = await this.toolRegistry.execute(toolCall.name, toolCall.arguments, context);\r\n\r\n if (result.success) {\r\n // Log successful tool result\r\n conversationLogger.logToolResult(toolCall.name, toolCall.id, result.result, true);\r\n\r\n // Notify UI: tool succeeded (send full result to UI)\r\n if (this.onToolExecutionUpdate) {\r\n // Add cwd to arguments for execute_command tool, and remoteContext for all tools\r\n const toolArgs = toolCall.name === 'execute_command'\r\n ? { ...toolCall.arguments, cwd: effectiveCwd, remoteContext }\r\n : { ...toolCall.arguments, remoteContext };\r\n\r\n this.onToolExecutionUpdate({\r\n toolName: toolCall.name,\r\n status: 'completed',\r\n result: result.result,\r\n arguments: toolArgs\r\n });\r\n }\r\n\r\n // Parse result if it's a string (avoid double-stringification)\r\n let parsedResult = result.result;\r\n if (typeof result.result === 'string') {\r\n try {\r\n parsedResult = JSON.parse(result.result);\r\n } catch {\r\n // Keep as string if not valid JSON\r\n parsedResult = result.result;\r\n }\r\n }\r\n\r\n // Sanitize context for AI consumption (strip ANSI, compact file writes)\r\n const sanitizedResult = sanitizeForContext(toolCall.name, parsedResult, toolCall.arguments);\r\n\r\n // Log the sanitized version for debugging purposes\r\n conversationLogger.logToolResult(`${toolCall.name} (SANITIZED_CONTEXT)`, toolCall.id, sanitizedResult, true);\r\n\r\n // Truncate result before sending to AI (to avoid exceeding message size limits)\r\n const truncatedResult = this.truncateResult(sanitizedResult);\r\n\r\n toolResults.push({\r\n tool_call_id: toolCall.id,\r\n name: toolCall.name,\r\n result: truncatedResult,\r\n });\r\n } else {\r\n // Log failed tool result\r\n conversationLogger.logToolResult(toolCall.name, toolCall.id, null, false, result.error);\r\n\r\n // Check if operation was cancelled by user\r\n if (result.error && result.error.includes('Operation cancelled by user')) {\r\n userCancelledOperation = true;\r\n }\r\n\r\n // Notify UI: tool failed\r\n if (this.onToolExecutionUpdate) {\r\n // Add cwd to arguments for execute_command tool, and remoteContext for all tools\r\n const toolArgs = toolCall.name === 'execute_command'\r\n ? { ...toolCall.arguments, cwd: effectiveCwd, remoteContext }\r\n : { ...toolCall.arguments, remoteContext };\r\n\r\n this.onToolExecutionUpdate({\r\n toolName: toolCall.name,\r\n status: 'error',\r\n error: result.error,\r\n arguments: toolArgs\r\n });\r\n }\r\n\r\n toolResults.push({\r\n tool_call_id: toolCall.id,\r\n name: toolCall.name,\r\n result: `Error: ${result.error}`,\r\n });\r\n\r\n // If user cancelled, stop processing remaining tools\r\n if (userCancelledOperation) {\r\n break;\r\n }\r\n }\r\n } catch (error: any) {\r\n // Log tool execution error\r\n conversationLogger.logError(`Tool execution: ${toolCall.name}`, error);\r\n\r\n // Check if operation was cancelled by user\r\n if (error.message && error.message.includes('Operation cancelled by user')) {\r\n userCancelledOperation = true;\r\n }\r\n\r\n // Build remote context for error notification\r\n const catchCtx = this.contextManager.getCurrentContext();\r\n let catchRemoteContext: string | undefined;\r\n if (catchCtx.type !== 'local') {\r\n const metadata = catchCtx.metadata;\r\n if (catchCtx.type === 'ssh' && metadata) {\r\n catchRemoteContext = `${metadata.username || 'user'}@${metadata.hostname || 'remote'}`;\r\n } else if (catchCtx.type === 'wsl' && metadata) {\r\n catchRemoteContext = `wsl:${metadata.distroName || 'wsl'}`;\r\n } else if (catchCtx.type === 'docker' && metadata) {\r\n catchRemoteContext = `docker:${metadata.containerId?.substring(0, 12) || 'container'}`;\r\n }\r\n }\r\n\r\n // Notify UI: tool failed\r\n if (this.onToolExecutionUpdate) {\r\n // Add cwd to arguments for execute_command tool, and remoteContext for all tools\r\n const toolArgs = toolCall.name === 'execute_command'\r\n ? { ...toolCall.arguments, cwd: this.cwd, remoteContext: catchRemoteContext }\r\n : { ...toolCall.arguments, remoteContext: catchRemoteContext };\r\n\r\n this.onToolExecutionUpdate({\r\n toolName: toolCall.name,\r\n status: 'error',\r\n error: error.message,\r\n arguments: toolArgs\r\n });\r\n }\r\n\r\n toolResults.push({\r\n tool_call_id: toolCall.id,\r\n name: toolCall.name,\r\n result: `Error: ${error.message}`,\r\n });\r\n\r\n // If user cancelled, stop processing remaining tools\r\n if (userCancelledOperation) {\r\n break;\r\n }\r\n }\r\n }\r\n\r\n // STOP AGENT LOOP if shell_input was provided\r\n // Interactive shell input implies handing control back to the shell/user\r\n const hasShellInput = toolCalls.some(tc =>\r\n tc.name === 'execute_command' && tc.arguments && tc.arguments.shell_input\r\n );\r\n\r\n if (hasShellInput) {\r\n try {\r\n quickLog(`[${new Date().toISOString()}] [CLI] Input sent to shell. Stopping agent loop to await output.\\n`);\r\n } catch (e) { }\r\n taskCompleted = true;\r\n }\r\n\r\n // If task_complete was called, stop the agentic loop immediately\r\n if (taskCompleted) {\r\n // Set the final message: use summary if provided, otherwise use the streamed assistantMessage\r\n finalAssistantMessage = taskCompleteSummary || assistantMessage;\r\n break;\r\n }\r\n\r\n // If user cancelled an operation, stop the agentic loop immediately\r\n if (userCancelledOperation) {\r\n // Add assistant message to history with thinking if available\r\n const cancelledAssistantMsg: AIMessage = {\r\n role: 'assistant',\r\n content: assistantMessage || '',\r\n tool_calls: toolCalls, // Store tool calls for MaaS models\r\n };\r\n if (currentTurnThinking) {\r\n cancelledAssistantMsg.thinking = currentTurnThinking;\r\n }\r\n if (currentTurnThinkingSignature) {\r\n cancelledAssistantMsg.thinkingSignature = currentTurnThinkingSignature;\r\n }\r\n this.conversationHistory.push(cancelledAssistantMsg);\r\n\r\n // Add tool results to history\r\n for (const toolResult of toolResults) {\r\n this.conversationHistory.push({\r\n role: 'tool',\r\n tool_call_id: toolResult.tool_call_id,\r\n content: typeof toolResult.result === 'string' ? toolResult.result : JSON.stringify(toolResult.result),\r\n });\r\n }\r\n\r\n // Set final message indicating cancellation\r\n finalAssistantMessage = 'Operation cancelled by user. The task was not completed.';\r\n break;\r\n }\r\n\r\n // Send assistant message to UI first (if there's text)\r\n if (assistantMessage && this.onResponseStreamCallback) {\r\n // Send the message that came with the tool calls\r\n this.onResponseStreamCallback(assistantMessage);\r\n }\r\n\r\n // Add assistant message with tool calls to conversation history\r\n if (toolCalls && toolCalls.length > 0) {\r\n try {\r\n quickLog(`[${new Date().toISOString()}] [CLI] Adapting assistant message: has toolCalls=${toolCalls.length}, first=${JSON.stringify(toolCalls[0])}\\n`);\r\n } catch (e) { }\r\n } else {\r\n try {\r\n quickLog(`[${new Date().toISOString()}] [CLI] Adapting assistant message: NO toolCalls\\n`);\r\n } catch (e) { }\r\n }\r\n\r\n // Filter out tool calls that were already handled directly (e.g., create_plan, mark_task_complete)\r\n const unhandledToolCalls = toolCalls.filter(tc => !handledToolCallIds.has(tc.id));\r\n\r\n // Also filter toolResults to only include results for unhandled tool calls\r\n // CRITICAL: Vertex AI requires equal number of function calls and function responses\r\n const unhandledToolResults = toolResults.filter(tr => !handledToolCallIds.has(tr.tool_call_id));\r\n\r\n // Only add assistant message if there are unhandled tool calls\r\n if (unhandledToolCalls.length > 0) {\r\n const assistantHistoryMsg: AIMessage = {\r\n role: 'assistant',\r\n content: assistantMessage || '',\r\n tool_calls: unhandledToolCalls, // Only include unhandled tool calls\r\n };\r\n // Include thinking from this turn if available (Extended Thinking pattern)\r\n if (currentTurnThinking) {\r\n assistantHistoryMsg.thinking = currentTurnThinking;\r\n }\r\n // Include thinking signature from this turn (required for Claude extended thinking)\r\n if (currentTurnThinkingSignature) {\r\n assistantHistoryMsg.thinkingSignature = currentTurnThinkingSignature;\r\n }\r\n\r\n // Log signature info for debugging multi-turn flows\r\n const geminiSigCount = unhandledToolCalls.filter(tc => !!tc.thoughtSignature).length;\r\n try {\r\n quickLog(`[${new Date().toISOString()}] [CLI] *** STORING ASSISTANT MSG: ${unhandledToolCalls.length} tool_calls, Gemini signatures: ${geminiSigCount}, Claude thinking: ${!!currentTurnThinking}, Claude sig: ${!!currentTurnThinkingSignature}\\n`);\r\n } catch (e) { }\r\n\r\n this.conversationHistory.push(assistantHistoryMsg);\r\n\r\n // Add tool results to conversation history as tool messages\r\n // Format: { tool_call_id, name, result: <object or string> }\r\n // Only add results for unhandled tool calls (handled ones already added their own results)\r\n for (const toolResult of unhandledToolResults) {\r\n // Add tool result to conversation history as tool message\r\n // IMPORTANT: tool_call_id must be a top-level property\r\n this.conversationHistory.push({\r\n role: 'tool',\r\n tool_call_id: toolResult.tool_call_id,\r\n content: typeof toolResult.result === 'string' ? toolResult.result : JSON.stringify(toolResult.result),\r\n });\r\n }\r\n }\r\n\r\n // Rebuild messages array with updated history\r\n // During agent loop: keep ALL thinking for current task\r\n // (Thinking from previous tasks was already stripped at request start)\r\n messages = getMessagesForContext();\r\n\r\n // No need to reset currentTurnThinking - keep accumulating for the task\r\n\r\n // Re-inject subshell context\r\n messages = this.aiContextInjector.injectSubshellContext(messages, currentContext);\r\n\r\n // ── Mid-turn interrupt check ──────────────────────────────────\r\n // If the user sent a message while this turn was executing, inject\r\n // it into the conversation now so the AI sees it on the very next\r\n // turn — instead of waiting for the entire multi-turn task to end.\r\n if (this.interruptQueue.length > 0) {\r\n const nextInterrupt = this.interruptQueue.shift()!;\r\n\r\n // Update queue UI\r\n if (this.onInterruptQueueUpdateCallback) {\r\n this.onInterruptQueueUpdateCallback(this.interruptQueue);\r\n }\r\n\r\n // Notify App.tsx to add the user bubble to messageHistory\r\n if (this.onQueuedMessageDispatchedCallback) {\r\n this.onQueuedMessageDispatchedCallback(nextInterrupt);\r\n }\r\n\r\n quickLog(`[${new Date().toISOString()}] [handleMessage] Processing mid-turn interrupt from user.\\n`);\r\n\r\n // Create a checkpoint for this interrupt so it appears in /revert.\r\n // Store the clean user text (without system wrapper) for autocomplete/input prefill.\r\n if (this.checkpointManager && this.currentChatId) {\r\n try {\r\n const checkpointContext = this.contextManager.getCurrentContext();\r\n let remoteSessionInfo: RemoteSessionInfo | undefined;\r\n let remoteHandler: RemoteFileHandler | undefined;\r\n\r\n if (checkpointContext.type !== 'local' && checkpointContext.handler) {\r\n const metadata = checkpointContext.metadata;\r\n remoteSessionInfo = {\r\n hostname: metadata.hostname,\r\n username: metadata.username,\r\n sessionId: checkpointContext.sessionId,\r\n connectionString: metadata.hostname\r\n ? `${metadata.username || 'user'}@${metadata.hostname}`\r\n : metadata.distroName || metadata.containerId || undefined,\r\n };\r\n\r\n if (typeof checkpointContext.handler.readFile === 'function' &&\r\n typeof checkpointContext.handler.writeFile === 'function' &&\r\n typeof checkpointContext.handler.executeCommand === 'function' &&\r\n typeof checkpointContext.handler.isConnected === 'function') {\r\n remoteHandler = checkpointContext.handler as RemoteFileHandler;\r\n }\r\n }\r\n\r\n // conversationIndex points to the next message slot (the interrupt user message).\r\n const interruptCheckpoint = await this.checkpointManager.startCheckpoint({\r\n prompt: nextInterrupt,\r\n cwd: this.cwd,\r\n contextType: checkpointContext.type as 'local' | 'ssh' | 'wsl' | 'docker',\r\n conversationIndex: this.conversationHistory.length,\r\n remoteSessionInfo,\r\n handler: remoteHandler,\r\n });\r\n\r\n if (interruptCheckpoint) {\r\n this.currentCheckpointId = interruptCheckpoint.id;\r\n context.currentCheckpointId = interruptCheckpoint.id;\r\n quickLog(\r\n `[${new Date().toISOString()}] [Checkpoint] Started mid-turn checkpoint ${interruptCheckpoint.id} for interrupt: \"${nextInterrupt.slice(0, 50)}...\"\\n`\r\n );\r\n }\r\n } catch (checkpointError: any) {\r\n quickLog(\r\n `[${new Date().toISOString()}] [Checkpoint] Failed to create mid-turn interrupt checkpoint: ${checkpointError?.message || checkpointError}\\n`\r\n );\r\n }\r\n }\r\n\r\n // Wrap the interrupt with context for the AI\r\n const wrappedInterrupt = `[SYSTEM NOTE: The user interrupted you with the following message. Please address it, adjust your actions, and proceed with the task.]\\n\\n${nextInterrupt}`;\r\n\r\n // Push the interrupt into conversation history so the AI\r\n // receives both the tool results from THIS turn and the\r\n // user's new message together on the next AI call.\r\n this.conversationHistory.push({\r\n role: 'user',\r\n content: wrappedInterrupt,\r\n });\r\n\r\n // Rebuild messages to include the interrupt\r\n messages = getMessagesForContext();\r\n messages = this.aiContextInjector.injectSubshellContext(messages, currentContext);\r\n }\r\n\r\n // Auto-compact context when usage grows too large during long-running agent loops.\r\n const compactionOutcome = await this.maybeAutoCompactContext(selectedModel, modelContextWindow);\r\n if (compactionOutcome.historyChanged) {\r\n messages = getMessagesForContext();\r\n messages = this.aiContextInjector.injectSubshellContext(messages, currentContext);\r\n }\r\n\r\n continue; // Loop back to AI service\r\n } else {\r\n // No tool calls - check for silent stop or text-only response\r\n\r\n // Case 1: Silent Stop (No tool calls AND no text)\r\n if (!assistantMessage || !assistantMessage.trim()) {\r\n // No tool calls and no message - AI stopped silently\r\n // This usually means the AI thinks it's done but didn't call task_complete\r\n // Prompt it to either continue or complete\r\n conversationLogger.logNarrationDetection('silent_stop', {\r\n turn: turnCount,\r\n assistantMessageLength: 0,\r\n });\r\n\r\n const silentStopPrompt = '⚠️ **SILENT STOP DETECTED**: You ended your turn without any output or tool calls.\\n\\n' +\r\n '**This is not allowed.** You must either:\\n' +\r\n '1. Execute a tool call if more work is needed, OR\\n' +\r\n '2. Output your response text, then call task_complete()\\n\\n' +\r\n '**If you have completed the task**, output your summary now, then call task_complete().\\n' +\r\n '**If more work is needed**, execute the next tool call immediately.';\r\n\r\n conversationLogger.logSystemPrompt('silent_stop_prompt', silentStopPrompt);\r\n this.conversationHistory.push({\r\n role: 'user',\r\n content: silentStopPrompt,\r\n });\r\n }\r\n // Case 2: Text-only response - accept it immediately as final\r\n else {\r\n // Log that we're accepting this as a final answer\r\n conversationLogger.logNarrationDetection('final_answer', {\r\n turn: turnCount,\r\n messagePreview: assistantMessage.substring(0, 200),\r\n action: 'accepting_immediately',\r\n });\r\n\r\n // Accept the text as the final message and break\r\n finalAssistantMessage = assistantMessage;\r\n break;\r\n }\r\n\r\n // Rebuild messages array with updated history\r\n // Backend will inject system prompt\r\n messages = getMessagesForContext();\r\n\r\n // Re-inject subshell context\r\n messages = this.aiContextInjector.injectSubshellContext(messages, currentContext);\r\n\r\n const compactionOutcome = await this.maybeAutoCompactContext(selectedModel, modelContextWindow);\r\n if (compactionOutcome.historyChanged) {\r\n messages = getMessagesForContext();\r\n messages = this.aiContextInjector.injectSubshellContext(messages, currentContext);\r\n }\r\n\r\n // Continue loop to get AI's response (removed 500ms delay for faster response)\r\n continue;\r\n }\r\n\r\n // No tool calls and no message - AI stopped silently\r\n // This usually means the AI thinks it's done but didn't call task_complete\r\n // Prompt it to either continue or complete\r\n const silentStopPrompt = '⚠️ **SILENT STOP DETECTED**: You ended your turn without any output or tool calls.\\n\\n' +\r\n '**This is not allowed.** You must either:\\n' +\r\n '1. Execute a tool call if more work is needed, OR\\n' +\r\n '2. Output your response text, then call task_complete()\\n\\n' +\r\n '**If you have completed the task**, output your summary now, then call task_complete().\\n' +\r\n '**If more work is needed**, execute the next tool call immediately.';\r\n\r\n this.conversationHistory.push({\r\n role: 'user',\r\n content: silentStopPrompt,\r\n });\r\n\r\n // Rebuild messages array with updated history\r\n // Backend will inject system prompt\r\n messages = getMessagesForContext();\r\n\r\n // Re-inject subshell context\r\n messages = this.aiContextInjector.injectSubshellContext(messages, currentContext);\r\n\r\n // Add delay before prompting\r\n await new Promise(resolve => setTimeout(resolve, 500));\r\n\r\n // Increment turn count and continue (give AI one more chance)\r\n turnCount++;\r\n if (turnCount >= MAX_TURNS) {\r\n // If we've hit max turns, force completion\r\n finalAssistantMessage = '⚠️ **Task Incomplete**: The AI stopped responding after multiple prompts.\\n\\n' +\r\n 'The task may be partially complete. Please review the work done and provide more specific instructions if needed.';\r\n break;\r\n }\r\n\r\n\r\n const compactionOutcome = await this.maybeAutoCompactContext(selectedModel, modelContextWindow);\r\n if (compactionOutcome.historyChanged) {\r\n messages = getMessagesForContext();\r\n messages = this.aiContextInjector.injectSubshellContext(messages, currentContext);\r\n }\r\n continue;\r\n }\r\n\r\n // Check if max turns was reached\r\n if (turnCount >= MAX_TURNS) {\r\n // Add a warning message to the final response\r\n const warningMessage = '\\n\\n⚠️ Note: The task reached the maximum number of processing turns. The work may be incomplete. Please review the results and let me know if you need me to continue.';\r\n finalAssistantMessage = (finalAssistantMessage || 'Task processing limit reached.') + warningMessage;\r\n }\r\n\r\n // Send final message to user (without tool execution logs)\r\n // If finalAssistantMessage is empty (task_complete with no summary), don't show anything\r\n const finalMessage = finalAssistantMessage;\r\n\r\n // Only save and display if there's a message\r\n if (finalMessage) {\r\n // Save to conversation history\r\n this.conversationHistory.push({\r\n role: 'assistant',\r\n content: finalMessage,\r\n });\r\n\r\n // Messages are stored locally only via saveCurrentChat() below\r\n } // End of while loop\r\n\r\n // Auto-save conversation to local storage\r\n this.saveCurrentChat();\r\n\r\n\r\n // Log session end\r\n conversationLogger.endSession(finalAssistantMessage, turnCount);\r\n\r\n // Send response back to UI (only if there's a message)\r\n if (this.onResponseCallback && finalAssistantMessage) {\r\n this.onResponseCallback(finalAssistantMessage);\r\n }\r\n\r\n // Fire file change summary callback after every successful completion\r\n // (not gated on task_complete — works for all model types)\r\n if (this.onFileChangeSummaryCallback && this.checkpointManager) {\r\n try {\r\n let remoteHandler: RemoteFileHandler | undefined;\r\n const summaryCtx = this.contextManager.getCurrentContext();\r\n if (summaryCtx.type !== 'local' && summaryCtx.handler) {\r\n const h = summaryCtx.handler;\r\n if (typeof h.readFile === 'function' &&\r\n typeof h.writeFile === 'function' &&\r\n typeof h.executeCommand === 'function' &&\r\n typeof h.isConnected === 'function') {\r\n remoteHandler = h as RemoteFileHandler;\r\n }\r\n }\r\n\r\n const sessionChanges = await this.checkpointManager.getAggregatedSessionChanges(remoteHandler);\r\n if (sessionChanges) {\r\n const { changes, stats } = sessionChanges;\r\n const fileCount = changes.added.length + changes.modified.length + changes.deleted.length;\r\n\r\n if (fileCount > 0) {\r\n const totalInsertions = stats.reduce((sum: number, s: { insertions: number; deletions: number }) => sum + s.insertions, 0);\r\n const totalDeletions = stats.reduce((sum: number, s: { insertions: number; deletions: number }) => sum + s.deletions, 0);\r\n\r\n this.onFileChangeSummaryCallback({ filesChanged: fileCount, insertions: totalInsertions, deletions: totalDeletions });\r\n }\r\n }\r\n } catch {\r\n // Silently ignore errors in file change summary\r\n }\r\n }\r\n\r\n } catch (error: any) {\r\n // Log the error\r\n conversationLogger.logError('handleMessage', error);\r\n\r\n // Check if this was an abort/cancellation (including timeout errors from aborted requests)\r\n if (error.name === 'AbortError' || error.message?.includes('aborted') || error.message?.includes('timed out') || (myAbortController as any).isIntentionalAbort) {\r\n // If intentionally aborted for replacement by new message, return silently\r\n // The new message will take over - no need to show cancellation message\r\n if ((myAbortController as any).isIntentionalAbort && (myAbortController as any).isReplacement) {\r\n return;\r\n }\r\n\r\n conversationLogger.logError('handleMessage', new Error('Request cancelled by user'));\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback('⚠️ Request cancelled by user.');\r\n }\r\n return;\r\n }\r\n throw new Error(`AI Error: ${error.message}`);\r\n } finally {\r\n // Finalize checkpoint if one was created for this message\r\n if (this.currentCheckpointId && this.checkpointManager) {\r\n try {\r\n // Resolve remote handler so finalizeCheckpoint can correctly calculate\r\n // changes for remote checkpoints (new/modified files live on the remote).\r\n let finalizeHandler: RemoteFileHandler | undefined;\r\n const finalizeCtx = this.contextManager.getCurrentContext();\r\n if (finalizeCtx.type !== 'local' && finalizeCtx.handler) {\r\n const h = finalizeCtx.handler;\r\n if (typeof h.readFile === 'function' &&\r\n typeof h.writeFile === 'function' &&\r\n typeof h.executeCommand === 'function' &&\r\n typeof h.isConnected === 'function') {\r\n finalizeHandler = h as RemoteFileHandler;\r\n }\r\n }\r\n await this.checkpointManager.finalizeCheckpoint(this.currentCheckpointId, finalizeHandler);\r\n quickLog(`[${new Date().toISOString()}] [Checkpoint] Finalized checkpoint ${this.currentCheckpointId}\\n`);\r\n } catch (error: any) {\r\n quickLog(`[${new Date().toISOString()}] [Checkpoint] Failed to finalize checkpoint: ${error.message}\\n`);\r\n }\r\n this.currentCheckpointId = undefined;\r\n }\r\n // Clean up abort controller\r\n this.currentAbortController = undefined;\r\n const wasIntentionalAbort = this.requestIntentionallyAborted;\r\n // Always reset after this request fully unwinds so subsequent\r\n // queue dispatch decisions are not blocked by stale abort state.\r\n this.requestIntentionallyAborted = false;\r\n\r\n // Process queued interrupts even after an intentional cancel.\r\n // ESC should cancel the CURRENT turn, not strand queued user prompts.\r\n if (this.interruptQueue.length > 0) {\r\n if (wasIntentionalAbort) {\r\n quickLog(`[${new Date().toISOString()}] [handleMessage] Cancelled current turn; continuing with queued interrupt(s).\\n`);\r\n }\r\n // We aren't checking `myAbortController.isIntentionalAbort` here because we don't set it anymore\r\n // Next message in queue\r\n const nextInterrupt = this.interruptQueue.shift()!;\r\n if (this.onInterruptQueueUpdateCallback) {\r\n this.onInterruptQueueUpdateCallback(this.interruptQueue);\r\n }\r\n\r\n // Notify UI that this queued message is now being dispatched for processing.\r\n // App.tsx uses this to add the user message to messageHistory at the correct time\r\n // (i.e., when the message actually starts being processed, not when it was first queued).\r\n if (this.onQueuedMessageDispatchedCallback) {\r\n this.onQueuedMessageDispatchedCallback(nextInterrupt);\r\n }\r\n\r\n quickLog(`[${new Date().toISOString()}] [handleMessage] Processing queued interrupt from user.\\n`);\r\n\r\n const wrappedMessage = `[SYSTEM NOTE: The user interrupted you with the following message. Please address it, adjust your actions, and proceed with the task.]\\n\\n${nextInterrupt}`;\r\n\r\n // Start next message processing asynchronously so we don't block finally block returning\r\n setTimeout(() => {\r\n this.handleMessage(wrappedMessage, { interruptCleanText: nextInterrupt }).catch(err => {\r\n conversationLogger.logError('Queued interrupt handleMessage', err);\r\n });\r\n }, 0);\r\n } else {\r\n void this.maybeProcessSessionCommandQueue();\r\n }\r\n }\r\n }\r\n\r\n private async handleSlashCommand(command: string): Promise<void> {\r\n const parts = command.slice(1).split(' ');\r\n const cmd = parts[0].toLowerCase();\r\n const args = parts.slice(1);\r\n\r\n let responseMessage = '';\r\n\r\n switch (cmd) {\r\n case 'help':\r\n responseMessage = `Available Commands:\\n\\n` +\r\n `/help - Show this help message\\n` +\r\n `/init - Analyze project and create/load centaurus.md context file\\n` +\r\n `/chat - Manage chat sessions (resume previous chats)\\n` +\r\n `/clear - Clear conversation and start a new chat\\n` +\r\n `/sync - Sync data to/from cloud (upload/restore)\\n` +\r\n `/config - View current configuration\\n` +\r\n `/model - Select from available Google models\\n` +\r\n `/plan - Toggle plan mode for complex implementations\\n` +\r\n `/mcp - Manage configured MCP servers and tools\\n` +\r\n `/docs - Open Centaurus documentation in browser\\n` +\r\n `/copy-chat-context - Copy chat as readable text to clipboard\\n` +\r\n `/session-limits - View session quota usage and limits\\n` +\r\n `/quality - Toggle enhanced quality features (thinking protocol, validation)\\n` +\r\n `/autonomous - Toggle autonomous mode (Silent Operator with task_complete)\\n` +\r\n `/sign-in - Sign in with Google (if not already signed in)\\n` +\r\n `/logout - Sign out, clear session, and exit CLI\\n` +\r\n `/exit - Exit the application\\n\\n` +\r\n `Keyboard Shortcuts:\\n\\n` +\r\n `Ctrl+D - Cycle modes (Agent → Terminal → Auto)\\n` +\r\n `Ctrl+T - Toggle auto-accept mode\\n` +\r\n `Ctrl+C - Cancel operation / Exit (press twice)\\n` +\r\n `Tab - Autocomplete files/directories (in command mode)\\n` +\r\n `Ctrl+Z - Undo last input change\\n` +\r\n `Ctrl+A - Select all text`;\r\n break;\r\n\r\n case 'session-limits': {\r\n const config = sessionQuotaManager.getCurrentConfig();\r\n const messagesUsed = sessionQuotaManager.getMessagesUsed();\r\n const remaining = sessionQuotaManager.getRemainingMessages();\r\n const timeRemaining = sessionQuotaManager.getFormattedTimeRemaining();\r\n const maxMessages = config.maxMessagesPerSession;\r\n\r\n // Calculate percentage used (cap at 100% for display)\r\n const percentUsed = maxMessages > 0 ? Math.min(100, Math.round((messagesUsed / maxMessages) * 100)) : 0;\r\n\r\n // Create a visual progress bar (clamp to valid range)\r\n const barLength = 20;\r\n const filledLength = Math.min(barLength, Math.max(0, Math.round((messagesUsed / maxMessages) * barLength)));\r\n const emptyLength = barLength - filledLength;\r\n const progressBar = '█'.repeat(filledLength) + '░'.repeat(emptyLength);\r\n\r\n // Status message based on quota\r\n const quotaStatus = remaining <= 0\r\n ? '\\n\\n⚠️ Session quota exhausted! AI requests are blocked until reset.'\r\n : '';\r\n\r\n responseMessage = `📊 Session Limits\\n\\n` +\r\n `Plan: free\\n` +\r\n `Session Window: ${config.sessionDurationMs / (60 * 60 * 1000)} hours\\n\\n` +\r\n `Messages Used: ${messagesUsed} / ${maxMessages} (${percentUsed}%)\\n` +\r\n `Messages Left: ${Math.max(0, remaining)}\\n` +\r\n `Progress: [${progressBar}]\\n` +\r\n `Time Remaining: ${timeRemaining || 'Session not started'}${quotaStatus}`;\r\n break;\r\n }\r\n\r\n case 'init':\r\n try {\r\n // Define the context file names in priority order\r\n const contextFiles = ['centaurus.md', 'gemini.md', 'claude.md'];\r\n let foundFile: string | null = null;\r\n let foundFilePath: string | null = null;\r\n\r\n // Check for existing context files in priority order\r\n for (const fileName of contextFiles) {\r\n const filePath = path.join(this.cwd, fileName);\r\n if (fs.existsSync(filePath)) {\r\n foundFile = fileName;\r\n foundFilePath = filePath;\r\n break;\r\n }\r\n }\r\n\r\n if (foundFile) {\r\n // Context file exists - read and load it\r\n try {\r\n const content = fs.readFileSync(foundFilePath!, 'utf-8');\r\n\r\n // Check if content is empty\r\n if (!content.trim()) {\r\n responseMessage = `⚠️ Found ${foundFile} but it's empty.\\n\\n` +\r\n `Please either:\\n` +\r\n `1. Delete the file and run /init again to generate new documentation\\n` +\r\n `2. Add content to the file manually`;\r\n break;\r\n }\r\n\r\n // If the found file is not centaurus.md, create a copy\r\n if (foundFile !== 'centaurus.md') {\r\n const centaurusPath = path.join(this.cwd, 'centaurus.md');\r\n\r\n // Check if centaurus.md already exists\r\n if (fs.existsSync(centaurusPath)) {\r\n responseMessage = `✅ Found both ${foundFile} and centaurus.md. Loaded project context from centaurus.md`;\r\n } else {\r\n // Create a copy as centaurus.md\r\n fs.copyFileSync(foundFilePath!, centaurusPath);\r\n responseMessage = `✅ Found ${foundFile} and created a copy as centaurus.md. Loaded project context.`;\r\n }\r\n } else {\r\n // Already centaurus.md\r\n responseMessage = `✅ Loaded project context from centaurus.md`;\r\n }\r\n\r\n // Add the context to conversation history so the AI actually has it\r\n this.conversationHistory.push({\r\n role: 'user',\r\n content: `Here is the project context from ${foundFile}:\\n\\n${content}`\r\n });\r\n\r\n } catch (readError: any) {\r\n responseMessage = `❌ Error reading ${foundFile}: ${readError.message}\\n\\n` +\r\n `Please check file permissions and try again.`;\r\n }\r\n } else {\r\n // No context file exists - trigger AI analysis\r\n responseMessage = `🔍 I will now analyze the contents of this directory and then create a centaurus.md`;\r\n\r\n // Send initial response\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback(responseMessage);\r\n }\r\n\r\n // Create a specialized prompt for project analysis\r\n const initPrompt = `You are tasked with analyzing the current project directory and creating a comprehensive documentation file called \"centaurus.md\".\r\n\r\n**Your Task:**\r\n1. Use the list_directory tool to explore the project structure starting from the current directory\r\n2. Use read_file to examine key files such as:\r\n - package.json, requirements.txt, or similar dependency files\r\n - README files\r\n - Configuration files (tsconfig.json, .eslintrc, etc.)\r\n - Main entry points (index.js, main.py, App.tsx, etc.)\r\n3. Use grep_search to find important patterns and understand the codebase\r\n4. Create a comprehensive centaurus.md file using the write_file tool\r\n\r\n**The centaurus.md file should include:**\r\n\r\n# [Project Name]\r\n\r\n## Overview\r\nBrief description of what this project does and its purpose\r\n\r\n## Technology Stack\r\n- **Languages**: List programming languages used\r\n- **Frameworks**: Main frameworks (React, Express, Django, etc.)\r\n- **Key Dependencies**: Important libraries and tools\r\n- **Build Tools**: Webpack, Vite, Maven, etc.\r\n\r\n## Directory Structure\r\n\\`\\`\\`\r\n/\r\n /src - Main source code\r\n /tests - Test files\r\n /config - Configuration files\r\n ...\r\n\\`\\`\\`\r\n\r\nBrief explanation of each major directory's purpose\r\n\r\n## Key Components\r\nDescribe the main modules, classes, or components:\r\n- **Component Name**: What it does, where it's located\r\n- **Component Name**: What it does, where it's located\r\n\r\n## Architecture\r\nExplain how the components interact:\r\n- Data flow\r\n- Communication patterns (REST APIs, GraphQL, event-driven, etc.)\r\n- State management\r\n- Database architecture (if applicable)\r\n\r\n## Development Workflow\r\n- How to install dependencies\r\n- How to run the development server\r\n- How to run tests\r\n- How to build for production\r\n\r\n## Important Conventions\r\n- Code organization patterns\r\n- Naming conventions\r\n- File structure patterns\r\n- Any special patterns or best practices used\r\n\r\n## Entry Points\r\n- Main files where execution starts\r\n- API endpoints (if applicable)\r\n- CLI commands (if applicable)\r\n\r\n## Quick Reference\r\nUseful commands, key files to know about, common tasks\r\n\r\n**IMPORTANT INSTRUCTIONS:**\r\n- Be thorough but concise\r\n- Use clear, professional language\r\n- Include code examples where helpful\r\n- Create the file in the current working directory: ${this.cwd}\r\n- After creating the file, call task_complete with a summary\r\n\r\n**Current working directory**: ${this.cwd}\r\n\r\nStart by listing the directory structure to understand what you're working with.`;\r\n\r\n // Add to conversation history\r\n this.conversationHistory.push({\r\n role: 'user',\r\n content: initPrompt\r\n });\r\n\r\n // Trigger the AI to process this\r\n await this.handleMessage('');\r\n\r\n // Don't send another response - the AI will respond\r\n return;\r\n }\r\n } catch (error: any) {\r\n responseMessage = `❌ Error executing /init command: ${error.message}\\n\\n` +\r\n `Please try again or check file permissions.`;\r\n }\r\n break;\r\n case 'sign-in':\r\n // Check if already authenticated\r\n if (apiClient.isAuthenticated()) {\r\n try {\r\n // Verify session is valid\r\n const user = await apiClient.getCurrentUser();\r\n responseMessage = `✅ You are already signed in as ${user.fullName} (${user.email})`;\r\n } catch (error) {\r\n // Session is invalid, proceed with sign-in\r\n responseMessage = '🔐 Starting authentication process...\\n';\r\n\r\n // Send initial response\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback(responseMessage);\r\n }\r\n\r\n // Attempt authentication\r\n const result = await authenticateWithGoogle();\r\n\r\n if (result.success) {\r\n // Enable backend sync after successful authentication\r\n this.configManager.enableBackendSync();\r\n this.conversationStarted = false; // Reset conversation to start fresh\r\n\r\n responseMessage = `✅ Successfully signed in as ${result.user?.fullName || 'User'}!\\n\\n` +\r\n `Cloud sync is now enabled. Your conversations and settings will be saved.`;\r\n } else {\r\n responseMessage = `❌ Authentication failed: ${result.error || 'Unknown error'}`;\r\n }\r\n }\r\n } else {\r\n // Not authenticated, proceed with sign-in\r\n responseMessage = '🔐 Starting authentication process...\\n';\r\n\r\n // Send initial response\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback(responseMessage);\r\n }\r\n\r\n // Attempt authentication\r\n const result = await authenticateWithGoogle();\r\n\r\n if (result.success) {\r\n // Enable backend sync after successful authentication\r\n this.configManager.enableBackendSync();\r\n this.conversationStarted = false; // Reset conversation to start fresh\r\n\r\n responseMessage = `✅ Successfully signed in as ${result.user?.fullName || 'User'}!\\n\\n` +\r\n `Cloud sync is now enabled. Your conversations and settings will be saved.`;\r\n } else {\r\n responseMessage = `❌ Authentication failed: ${result.error || 'Unknown error'}`;\r\n }\r\n }\r\n break;\r\n case 'logout':\r\n try {\r\n // Cancel workflow learning if active before logout\r\n const logoutCancelMsg = this.cancelWorkflowLearning('Logging out');\r\n if (logoutCancelMsg && this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(logoutCancelMsg);\r\n }\r\n\r\n await apiClient.logout();\r\n responseMessage = '✅ Logged out successfully.\\n\\n' +\r\n 'Your session has been cleared.\\n' +\r\n 'Exiting CLI...';\r\n\r\n // Send response to UI\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback(responseMessage);\r\n }\r\n\r\n // Wait a moment for the message to display, then exit\r\n setTimeout(() => {\r\n process.exit(0);\r\n }, 1000);\r\n\r\n return; // Don't send response again at the end\r\n } catch (error: any) {\r\n responseMessage = `❌ Logout failed: ${error.message}\\n\\nExiting CLI anyway...`;\r\n\r\n // Send response to UI\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback(responseMessage);\r\n }\r\n\r\n // Exit even if logout failed\r\n setTimeout(() => {\r\n process.exit(0);\r\n }, 1000);\r\n\r\n return; // Don't send response again at the end\r\n }\r\n case 'plan':\r\n // Don't allow toggling plan mode if in command mode\r\n if (this.commandMode) {\r\n responseMessage = '❌ Cannot toggle plan mode while in command mode. Press Ctrl+D to exit command mode first.';\r\n break;\r\n }\r\n\r\n this.planMode = !this.planMode;\r\n\r\n // Notify UI of plan mode change\r\n if (this.onPlanModeChange) {\r\n this.onPlanModeChange(this.planMode);\r\n }\r\n\r\n // Don't send any response message - the status bar shows the mode\r\n return;\r\n case 'quality':\r\n // Toggle enhanced quality features\r\n const currentQuality = this.configManager.get('enhancedQuality');\r\n const newQuality = currentQuality === false; // Toggle (default is true)\r\n\r\n this.configManager.set('enhancedQuality', newQuality);\r\n\r\n responseMessage = newQuality\r\n ? '✅ Enhanced quality features enabled\\n\\n' +\r\n 'The AI will now use:\\n' +\r\n '• Structured thinking protocol before actions\\n' +\r\n '• Enhanced command execution hygiene\\n' +\r\n '• Intelligent file editing validation\\n' +\r\n '• Professional SRE/Architect persona'\r\n : '⚠️ Enhanced quality features disabled\\n\\n' +\r\n 'The AI will use the basic system prompt without:\\n' +\r\n '• Thinking protocol\\n' +\r\n '• Advanced validation\\n' +\r\n '• Enhanced tool descriptions';\r\n break;\r\n case 'autonomous':\r\n // Toggle autonomous mode (Silent Operator)\r\n const currentAutonomous = this.configManager.get('autonomousMode');\r\n const newAutonomous = !currentAutonomous; // Toggle (default is false)\r\n\r\n this.configManager.set('autonomousMode', newAutonomous);\r\n\r\n responseMessage = newAutonomous\r\n ? '✅ Autonomous Mode enabled (Silent Operator)\\n\\n' +\r\n 'The AI will now:\\n' +\r\n '• Work silently without narrating actions\\n' +\r\n '• Use Touch-First safety (never guess file paths)\\n' +\r\n '• Apply surgical precision to file edits\\n' +\r\n '• Output summary text, then call task_complete() when done\\n' +\r\n '• Inject intelligent error recovery hints\\n\\n' +\r\n 'This is the industry-standard autonomous agent mode.'\r\n : '⚠️ Autonomous Mode disabled\\n\\n' +\r\n 'The AI will use the standard enhanced prompt with:\\n' +\r\n '• Verbose communication after each action\\n' +\r\n '• Standard tool descriptions\\n' +\r\n '• Manual completion detection';\r\n break;\r\n case 'clear':\r\n // Cancel workflow learning if active before clearing\r\n const clearCancelMsg = this.cancelWorkflowLearning('Clearing chat session');\r\n if (clearCancelMsg && this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(clearCancelMsg);\r\n }\r\n\r\n // Start a new chat session (clears history and generates new chat ID)\r\n this.startNewChat();\r\n // Don't send any response message - the UI will handle clearing\r\n return;\r\n case 'config':\r\n if (args.length >= 2 && args[0].toLowerCase() === 'set') {\r\n // Set config value: /config set <key> <value>\r\n const configKey = args[1];\r\n const configValue = args.slice(2).join(' ');\r\n\r\n if (!configValue) {\r\n responseMessage = `❌ Error: No value provided.\\nUsage: /config set <key> <value>`;\r\n } else if (configKey === 'model') {\r\n // Validate model\r\n if (!isValidModel(configValue)) {\r\n responseMessage = `❌ ${getInvalidModelError(configValue)}`;\r\n break;\r\n }\r\n\r\n try {\r\n this.configManager.set('model', configValue);\r\n responseMessage = `✅ Configuration updated: ${configKey} = ${configValue}`;\r\n } catch (error: any) {\r\n responseMessage = `❌ Failed to update configuration: ${error.message}`;\r\n }\r\n } else if (configKey === 'enhancedQuality' || configKey === 'autonomousMode') {\r\n // Parse boolean value\r\n const boolValue = configValue.toLowerCase() === 'true' || configValue === '1';\r\n\r\n try {\r\n this.configManager.set(configKey, boolValue);\r\n responseMessage = `✅ Configuration updated: ${configKey} = ${boolValue}`;\r\n } catch (error: any) {\r\n responseMessage = `❌ Failed to update configuration: ${error.message}`;\r\n }\r\n } else {\r\n responseMessage = `❌ Error: Unknown config key: ${configKey}\\nValid keys: model, enhancedQuality, autonomousMode`;\r\n }\r\n } else {\r\n // View config\r\n const config = this.configManager.load();\r\n\r\n // Import version checker to get current version\r\n const { getCurrentVersion } = await import('./utils/version-checker.js');\r\n const currentVersion = getCurrentVersion();\r\n\r\n responseMessage = `Current Configuration:\\n\\n` +\r\n `Version: ${currentVersion}\\n` +\r\n `Model: ${config.model || 'gemini-2.5-flash (default)'}\\n` +\r\n `AI Auto-Suggest: ${config.aiAutoSuggest === true ? '✅ Enabled' : '❌ Disabled'}\\n` +\r\n `Authentication: ${apiClient.isAuthenticated() ? '✅ Signed in' : '❌ Not signed in'}`;\r\n }\r\n break;\r\n\r\n case 'settings':\r\n if (args.length >= 2 && args[0].toLowerCase() === 'auto-suggest') {\r\n // Handle /settings auto-suggest <on/off>\r\n const value = args[1].toLowerCase();\r\n\r\n if (value === 'on') {\r\n this.configManager.set('aiAutoSuggest', true);\r\n if (this.onAiAutoSuggestChange) {\r\n this.onAiAutoSuggestChange(true);\r\n }\r\n responseMessage = '✅ **AI Auto-Suggestions Enabled**\\n\\n' +\r\n 'From now on, I will suggest commands after 5 seconds of inactivity.\\n' +\r\n 'Suggestions will appear in grey text. Use the **Right Arrow** key to accept them.';\r\n } else if (value === 'off') {\r\n this.configManager.set('aiAutoSuggest', false);\r\n if (this.onAiAutoSuggestChange) {\r\n this.onAiAutoSuggestChange(false);\r\n }\r\n responseMessage = '✅ **AI Auto-Suggestions Disabled**\\n\\n' +\r\n 'I will no longer provide AI-powered command suggestions.';\r\n } else {\r\n responseMessage = '❌ Invalid option. Usage: `/settings auto-suggest on` or `/settings auto-suggest off`';\r\n }\r\n } else {\r\n\r\n responseMessage = '❌ Invalid command format.\\n\\nUsage:\\n- `/settings auto-suggest on`\\n- `/settings auto-suggest off`';\r\n }\r\n break;\r\n\r\n case 'model':\r\n case 'models':\r\n // Handle subcommands: local, cloud\r\n const modelSubCommand = args[0]?.toLowerCase();\r\n\r\n if (modelSubCommand === 'local') {\r\n // Local Ollama models\r\n try {\r\n // Check if Ollama is running\r\n const status = await ollamaService.isOllamaRunning();\r\n if (!status.available) {\r\n responseMessage = `❌ Cannot connect to Ollama\r\n\r\n${status.error || 'Ollama is not running.'}\r\n\r\nTo use local models:\r\n1. Install Ollama from: https://ollama.ai\r\n2. Start Ollama by running: ollama serve\r\n3. Pull a model: ollama pull llama3\r\n\r\nThen try /models local again.`;\r\n break;\r\n }\r\n\r\n // Get available local models\r\n const localModels = await ollamaService.getLocalModels();\r\n\r\n if (localModels.length === 0) {\r\n responseMessage = `📭 No local models found\r\n\r\nOllama is running (v${status.version}) but no models are downloaded.\r\n\r\nTo download models, run:\r\n ollama pull llama3\r\n ollama pull codellama\r\n ollama pull mistral\r\n\r\nThen try /models local again.`;\r\n break;\r\n }\r\n\r\n // Show picker for local model selection\r\n if (this.onShowPickerCallback) {\r\n const config = this.configManager.load();\r\n const currentModelName = config.modelName || '';\r\n const isCurrentLocal = config.isLocalModel === true;\r\n\r\n this.onShowPickerCallback({\r\n message: 'Select Local Model (Ollama)',\r\n type: 'local-model' as any, // Cast to bypass type check, will be handled in handlePickerSelection\r\n choices: localModels.map((model) => {\r\n const size = OllamaService.formatModelSize(model.size);\r\n const isCurrent = isCurrentLocal && currentModelName === model.name;\r\n const supportsTools = OllamaService.modelSupportsTools(model.name);\r\n const toolsBadge = supportsTools ? ' [Tools]' : '';\r\n return {\r\n label: `${model.name} (${size})${toolsBadge}${isCurrent ? ' [CURRENT]' : ''}`,\r\n value: model.name\r\n };\r\n })\r\n });\r\n return; // Don't send a text response, picker will handle it\r\n }\r\n } catch (error: any) {\r\n responseMessage = OllamaService.getHelpfulErrorMessage(error);\r\n }\r\n break;\r\n }\r\n\r\n if (modelSubCommand === 'cloud' || args.length === 0) {\r\n // Cloud models (default behavior when no subcommand or 'cloud' specified)\r\n if (this.onShowPickerCallback) {\r\n const config = this.configManager.load();\r\n const currentModelName = config.modelName || '';\r\n const isCurrentCloud = config.isLocalModel !== true;\r\n\r\n // Fetch models from backend\r\n const modelsConfig = await fetchModelsConfig();\r\n\r\n this.onShowPickerCallback({\r\n message: 'Select Cloud Model',\r\n type: 'model',\r\n choices: modelsConfig.models.map((modelConfig, index) => {\r\n const isCurrent = isCurrentCloud && currentModelName === modelConfig.name;\r\n return {\r\n label: `${modelConfig.name} - ${modelConfig.description}${isCurrent ? ' [CURRENT]' : ''}`,\r\n value: `${index}` // Use index as unique identifier\r\n };\r\n })\r\n });\r\n return; // Don't send a text response, picker will handle it\r\n }\r\n } else {\r\n // Unrecognized subcommand - show help\r\n responseMessage = `Usage: /models [local|cloud]\r\n\r\n /models local - Select from locally installed Ollama models\r\n /models cloud - Select from cloud models (Centaurus backend)\r\n /models - Default: show cloud models`;\r\n }\r\n break;\r\n\r\n case 'mcp':\r\n if (this.mcpCommandHandler) {\r\n responseMessage = await this.mcpCommandHandler.handleCommand(args);\r\n } else {\r\n responseMessage = '❌ MCP is not initialized. Please restart the CLI.';\r\n }\r\n break;\r\n\r\n case 'docs':\r\n // Open documentation URL in default browser\r\n const docsUrl = 'https://centauruslabs.in/docs';\r\n const { exec } = await import('child_process');\r\n const platform = process.platform;\r\n\r\n if (platform === 'win32') {\r\n exec(`start \"\" \"${docsUrl}\"`);\r\n } else if (platform === 'darwin') {\r\n exec(`open \"${docsUrl}\"`);\r\n } else {\r\n exec(`xdg-open \"${docsUrl}\"`);\r\n }\r\n\r\n responseMessage = `📖 Opening documentation in your browser...\\n\\n${docsUrl}`;\r\n break;\r\n\r\n case 'chat':\r\n // Chat management commands\r\n const chatSubCommand = args[0]?.toLowerCase();\r\n\r\n if (chatSubCommand === 'resume' || !chatSubCommand) {\r\n // Show list of saved chats for resuming (interactive picker)\r\n const chats = localChatStorage.listChats();\r\n\r\n if (chats.length === 0) {\r\n responseMessage = 'No saved chats found.\\n\\nStart a new conversation and it will be automatically saved!';\r\n } else {\r\n // Format chat list for picker display\r\n if (this.onShowChatPickerCallback) {\r\n this.onShowChatPickerCallback(chats, this.currentChatId);\r\n return; // Don't send a text response, picker will handle it\r\n } else {\r\n // Fallback: show as text if no picker callback\r\n responseMessage = 'Saved Chats:\\n\\n' +\r\n chats.slice(0, 10).map((chat, i) => {\r\n const date = new Date(chat.updatedAt).toLocaleDateString();\r\n const time = new Date(chat.updatedAt).toLocaleTimeString();\r\n return `${i + 1}. ${chat.title}\\n ${date} ${time} | ${chat.messageCount} messages`;\r\n }).join('\\n\\n') +\r\n (chats.length > 10 ? `\\n\\n...and ${chats.length - 10} more chats` : '');\r\n }\r\n }\r\n } else if (chatSubCommand === 'list') {\r\n // Show read-only list of saved chats (no selection)\r\n const chats = localChatStorage.listChats();\r\n\r\n if (chats.length === 0) {\r\n responseMessage = 'No saved chats found.\\n\\nStart a new conversation and it will be automatically saved!';\r\n } else {\r\n // Format chat list for read-only display\r\n if (this.onShowChatListCallback) {\r\n this.onShowChatListCallback(chats, this.currentChatId);\r\n return; // Don't send a text response, UI will handle it\r\n } else {\r\n // Fallback: show as text\r\n responseMessage = '📚 Saved Chats:\\\\n\\\\n' +\r\n chats.slice(0, 10).map((chat, i) => {\r\n const date = new Date(chat.updatedAt).toLocaleDateString();\r\n const time = new Date(chat.updatedAt).toLocaleTimeString();\r\n const isCurrent = chat.id === this.currentChatId ? ' (current)' : '';\r\n return `${i + 1}. ${chat.title}${isCurrent}\\\\n 📅 ${date} ${time} | 💬 ${chat.messageCount} messages`;\r\n }).join('\\\\n\\\\n') +\r\n (chats.length > 10 ? `\\\\n\\\\n...and ${chats.length - 10} more chats` : '');\r\n }\r\n }\r\n } else if (chatSubCommand === 'delete') {\r\n // Show list of saved chats for deletion\r\n const chats = localChatStorage.listChats();\r\n\r\n if (chats.length === 0) {\r\n responseMessage = 'No saved chats to delete.';\r\n } else {\r\n // Format chat list for delete picker display\r\n if (this.onShowChatDeletePickerCallback) {\r\n this.onShowChatDeletePickerCallback(chats, this.currentChatId);\r\n return; // Don't send a text response, picker will handle it\r\n } else {\r\n responseMessage = '❌ Delete picker not available. Please use the UI to delete chats.';\r\n }\r\n }\r\n } else if (chatSubCommand === 'new') {\r\n // Start a new chat session (old one is already saved)\r\n this.startNewChat();\r\n // Clear UI messages\r\n if (this.onRestoreMessagesCallback) {\r\n this.onRestoreMessagesCallback([]);\r\n }\r\n responseMessage = '✅ Started a new chat session!\\n\\nYour previous conversation has been saved and can be resumed with /chat resume.';\r\n } else if (chatSubCommand === 'rename') {\r\n // Show list of saved chats for renaming\r\n const chats = localChatStorage.listChats();\r\n\r\n if (chats.length === 0) {\r\n responseMessage = 'No saved chats to rename.';\r\n } else {\r\n // Format chat list for rename picker display\r\n if (this.onShowChatRenamePickerCallback) {\r\n this.onShowChatRenamePickerCallback(chats, this.currentChatId);\r\n return; // Don't send a text response, picker will handle it\r\n } else {\r\n responseMessage = '❌ Rename picker not available. Please use the UI to rename chats.';\r\n }\r\n }\r\n } else {\r\n responseMessage = `Unknown /chat subcommand: ${chatSubCommand}\\\\n\\\\nUsage:\\\\n /chat resume - Resume a previous chat session\\\\n /chat list - List all saved chats\\\\n /chat delete - Delete a saved chat\\\\n /chat new - Start a new chat session\\\\n /chat rename - Rename a saved chat`;\r\n }\r\n break;\r\n\r\n case 'copy-chat-context':\r\n try {\r\n const { formatChatForClipboard } = await import('./utils/chat-formatter.js');\r\n const { copyTextToClipboard } = await import('./utils/text-clipboard.js');\r\n\r\n if (this.uiMessageHistory.length === 0) {\r\n responseMessage = 'No messages in current chat to copy.';\r\n } else {\r\n const formattedChat = formatChatForClipboard(this.uiMessageHistory);\r\n const success = await copyTextToClipboard(formattedChat);\r\n\r\n if (success) {\r\n responseMessage = '✅ Chat content copied to clipboard!';\r\n } else {\r\n responseMessage = '❌ Failed to copy to clipboard.\\n\\n' +\r\n 'This might happen if clipboard access is not available in your environment.\\n' +\r\n 'Try running the CLI in a terminal with clipboard access.';\r\n }\r\n }\r\n } catch (error: any) {\r\n responseMessage = `❌ Error copying chat content: ${error.message}`;\r\n }\r\n break;\r\n\r\n case 'exit':\r\n process.exit(0);\r\n break;\r\n\r\n case 'revert': {\r\n const checkpointArg = args[0];\r\n if (!checkpointArg) {\r\n responseMessage = '❌ Usage: `/revert <checkpoint-id>`\\nType `/revert ` and use autocomplete to select a checkpoint.';\r\n break;\r\n }\r\n\r\n if (!this.checkpointManager) {\r\n responseMessage = '❌ No checkpoints available in this session.';\r\n break;\r\n }\r\n\r\n try {\r\n // Smart context matching: determine if we can revert based on current vs checkpoint context\r\n const currentContext = this.contextManager.getCurrentContext();\r\n const allCheckpoints = this.checkpointManager.list();\r\n const targetCheckpoint = allCheckpoints.find(c => c.id === checkpointArg);\r\n\r\n if (!targetCheckpoint) {\r\n responseMessage = `❌ Checkpoint \"${checkpointArg}\" not found.`;\r\n break;\r\n }\r\n\r\n // Context validation for revert\r\n const cpContextType = targetCheckpoint.contextType;\r\n const currentContextType = currentContext.type;\r\n\r\n if (cpContextType !== 'local' && currentContextType === 'local') {\r\n // Remote checkpoint but we're local — can't reconnect to SSH to revert files\r\n const sessionType = cpContextType.toUpperCase();\r\n const sessionInfo = targetCheckpoint.remoteSessionInfo;\r\n const target = sessionInfo?.connectionString || sessionInfo?.hostname || 'the remote machine';\r\n responseMessage = `❌ This checkpoint was created during a ${sessionType} session (${target}).\\n` +\r\n `You are not currently connected to that session.\\n\\n` +\r\n `Please reconnect to the ${sessionType} session first, then retry /revert.`;\r\n break;\r\n }\r\n\r\n if (cpContextType === 'local' && currentContextType !== 'local') {\r\n // Local checkpoint but we're in a remote session — wrong context\r\n responseMessage = `❌ This checkpoint was created in a local session.\\n` +\r\n `You are currently in a ${currentContextType.toUpperCase()} session.\\n\\n` +\r\n `Please exit your remote session first, then retry /revert.`;\r\n break;\r\n }\r\n\r\n if (cpContextType !== 'local' && currentContextType !== 'local' && cpContextType !== currentContextType) {\r\n // Both remote but different types (e.g., SSH checkpoint but in Docker now)\r\n responseMessage = `❌ This checkpoint was created in a ${cpContextType.toUpperCase()} session, ` +\r\n `but you are currently in a ${currentContextType.toUpperCase()} session.\\n\\n` +\r\n `Please connect to the correct ${cpContextType.toUpperCase()} session first.`;\r\n break;\r\n }\r\n\r\n // For remote checkpoints with mismatched hosts, validate the connection target\r\n if (cpContextType !== 'local' && targetCheckpoint.remoteSessionInfo) {\r\n const cpHost = targetCheckpoint.remoteSessionInfo.hostname;\r\n const currentHost = currentContext.metadata.hostname;\r\n if (cpHost && currentHost && cpHost !== currentHost) {\r\n responseMessage = `❌ This checkpoint was created on ${cpHost}, ` +\r\n `but you are currently connected to ${currentHost}.\\n\\n` +\r\n `Please connect to ${cpHost} first, then retry /revert.`;\r\n break;\r\n }\r\n }\r\n\r\n // Build handler for remote revert if needed\r\n let revertHandler: RemoteFileHandler | undefined;\r\n if (cpContextType !== 'local' && currentContext.handler) {\r\n if (typeof currentContext.handler.readFile === 'function' &&\r\n typeof currentContext.handler.writeFile === 'function' &&\r\n typeof currentContext.handler.executeCommand === 'function' &&\r\n typeof currentContext.handler.isConnected === 'function') {\r\n revertHandler = currentContext.handler as RemoteFileHandler;\r\n }\r\n }\r\n\r\n const result = await this.checkpointManager.revertToCheckpoint(checkpointArg, revertHandler);\r\n\r\n // Find checkpoint index for UI truncation\r\n const checkpoints = this.checkpointManager.list();\r\n const checkpointIndex = checkpoints.findIndex(c => c.id === checkpointArg);\r\n\r\n // Truncate conversation history to the checkpoint\r\n if (checkpointIndex >= 0) {\r\n const checkpoint = result.checkpoint;\r\n const checkpointPromptForUi = this.normalizePromptForInputBar(checkpoint.prompt);\r\n\r\n // Populate the input bar with the reverted prompt\r\n if (this.onSetInputCallback) {\r\n this.onSetInputCallback(checkpointPromptForUi);\r\n }\r\n\r\n // Use conversationIndex from metadata if available (robust)\r\n // This avoids issues with duplicate prompts getting truncated at the wrong occurrence\r\n if (typeof checkpoint.conversationIndex === 'number' &&\r\n checkpoint.conversationIndex >= 0 &&\r\n checkpoint.conversationIndex < this.conversationHistory.length) {\r\n\r\n // Truncate to index (exclusive) to remove the message from history\r\n // This allows the user to \"edit\" the message in the input bar\r\n this.conversationHistory = this.conversationHistory.slice(0, checkpoint.conversationIndex);\r\n quickLog(`[${new Date().toISOString()}] [Revert] Truncated conversation history to index ${checkpoint.conversationIndex} (exclusive)\\n`);\r\n\r\n } else {\r\n // Fallback to string matching (legacy or missing index)\r\n const targetPrompt = checkpointPromptForUi;\r\n // Find the user message with this prompt in conversationHistory\r\n let foundIndex = -1;\r\n // Use a broader search to find the matching message\r\n // The prompt might be a substring or exact match\r\n const searchPrompt = targetPrompt.slice(0, 50);\r\n\r\n for (let i = 0; i < this.conversationHistory.length; i++) {\r\n const msg = this.conversationHistory[i];\r\n if (msg.role === 'user' && msg.content && msg.content.includes(searchPrompt)) {\r\n foundIndex = i;\r\n break;\r\n }\r\n }\r\n\r\n if (foundIndex >= 0) {\r\n // Keep messages up to but NOT including this user message\r\n this.conversationHistory = this.conversationHistory.slice(0, foundIndex);\r\n quickLog(`[${new Date().toISOString()}] [Revert] Truncated conversation history to index ${foundIndex} (exclusive, using string match)\\n`);\r\n }\r\n }\r\n\r\n // Fix: Also truncate UI message history to ensure consistency\r\n // Use uiMessageIndex from metadata if available (robust)\r\n if (typeof checkpoint.uiMessageIndex === 'number' &&\r\n checkpoint.uiMessageIndex >= 0 &&\r\n checkpoint.uiMessageIndex < this.uiMessageHistory.length) {\r\n\r\n // Truncate to index (exclusive), subtract 1 to exclude the user's message\r\n // (uiMessageIndex was recorded AFTER the user message was added, so it includes it)\r\n // The user's message is placed into the input bar instead (via onSetInputCallback)\r\n const truncateUiIndex = Math.max(0, checkpoint.uiMessageIndex - 1);\r\n this.uiMessageHistory = this.uiMessageHistory.slice(0, truncateUiIndex);\r\n quickLog(`[${new Date().toISOString()}] [Revert] Truncated UI message history to index ${truncateUiIndex} (exclusive, adjusted from ${checkpoint.uiMessageIndex})\\n`);\r\n\r\n } else {\r\n // Fallback to string matching (legacy or missing index)\r\n const targetPrompt = checkpointPromptForUi;\r\n const searchPrompt = targetPrompt.slice(0, 50);\r\n\r\n let foundUiIndex = -1;\r\n for (let i = 0; i < this.uiMessageHistory.length; i++) {\r\n const msg = this.uiMessageHistory[i];\r\n if (msg.role === 'user' && msg.content && msg.content.includes(searchPrompt)) {\r\n foundUiIndex = i;\r\n break;\r\n }\r\n }\r\n\r\n if (foundUiIndex >= 0) {\r\n // Keep messages up to but NOT including this user message\r\n this.uiMessageHistory = this.uiMessageHistory.slice(0, foundUiIndex);\r\n quickLog(`[${new Date().toISOString()}] [Revert] Truncated UI message history to index ${foundUiIndex} (exclusive, using string match)\\n`);\r\n }\r\n }\r\n\r\n // Recompute token count from the truncated history.\r\n // This keeps the context indicator in sync even before any follow-up prompt.\r\n await this.updateTokenCount().catch(err => {\r\n quickLog(`[${new Date().toISOString()}] [Revert] Failed to update token count after revert: ${err}\\n`);\r\n });\r\n\r\n // After revert, re-check full reverted context and auto-compact again if needed.\r\n // This ensures reverted chats also honor the same context thresholds.\r\n try {\r\n const modelConfig = this.configManager.load();\r\n const modelForCompaction = modelConfig.model || modelConfig.modelName || 'gemini-2.5-flash';\r\n const { getModelContextWindowSync } = await import('./config/models.js');\r\n const maxTokensForRevert = getModelContextWindowSync(modelForCompaction);\r\n const revertCompaction = await this.maybeAutoCompactContext(modelForCompaction, maxTokensForRevert);\r\n\r\n quickLog(\r\n `[${new Date().toISOString()}] [Revert] Post-revert compaction: triggered=${revertCompaction.triggered}, compacted=${revertCompaction.compactedCount}, usage=${revertCompaction.afterUsagePercent.toFixed(1)}%\\n`\r\n );\r\n } catch (compactionError: any) {\r\n quickLog(\r\n `[${new Date().toISOString()}] [Revert] Post-revert auto-compaction check failed: ${compactionError?.message || compactionError}\\n`\r\n );\r\n }\r\n\r\n // Remove the reverted checkpoint AND all checkpoints created after it\r\n // They should not appear in autocomplete anymore\r\n this.checkpointManager.removeCheckpointsFrom(checkpointArg);\r\n\r\n // Build the success message BEFORE calling handleChatPickerSelection\r\n // This will be passed as a parameter and included in the same React setState call\r\n quickLog(`[${new Date().toISOString()}] [Revert] Building success message for checkpoint \"${checkpointArg}\"\\n`);\r\n const truncatedPrompt = checkpointPromptForUi.length > 50\r\n ? checkpointPromptForUi.slice(0, 50) + '...'\r\n : checkpointPromptForUi;\r\n let revertSuccessMessage = `✅ Reverted to: \"${truncatedPrompt}\"\\n` +\r\n `Restored ${result.restored} files, removed ${result.removed} files.`;\r\n if (result.errors.length > 0) {\r\n revertSuccessMessage += `\\n⚠️ Warnings: ${result.errors.join(', ')}`;\r\n }\r\n\r\n // Save the truncated state to disk\r\n if (this.currentChatId) {\r\n quickLog(`[${new Date().toISOString()}] [Revert] Saving truncated chat to disk (chatId: ${this.currentChatId})\\n`);\r\n quickLog(`[${new Date().toISOString()}] [Revert] uiMessageHistory.length BEFORE save: ${this.uiMessageHistory.length}\\n`);\r\n this.saveCurrentChat({ allowEmpty: true });\r\n\r\n // Reload the chat to force UI update ensures consistency\r\n // This simulates a /chat resume command which correctly populates the UI\r\n // Pass skipLoadedMessage=true and the revert success message\r\n // The success message is appended to restoredMessages in handleChatPickerSelection\r\n // This ensures it's part of the same React setState call, avoiding race conditions\r\n quickLog(`[${new Date().toISOString()}] [Revert] Calling handleChatPickerSelection with revertSuccessMessage=\"${revertSuccessMessage.substring(0, 50)}...\"\\n`);\r\n const currentContext = this.contextManager.getCurrentContext();\r\n const preserveRemote = currentContext.type !== 'local' && typeof currentContext.handler?.isConnected === 'function'\r\n ? currentContext.handler.isConnected()\r\n : currentContext.type !== 'local';\r\n await this.handleChatPickerSelection(this.currentChatId, true, revertSuccessMessage, preserveRemote);\r\n\r\n // Clear responseMessage so it doesn't get sent via onDirectMessageCallback\r\n // (the message was already included in the restored messages above)\r\n quickLog(`[${new Date().toISOString()}] [Revert] handleChatPickerSelection completed, clearing responseMessage\\n`);\r\n responseMessage = '';\r\n } else {\r\n // Fallback: if no current chat, show the message via normal callback\r\n quickLog(`[${new Date().toISOString()}] [Revert] No currentChatId, using fallback responseMessage\\n`);\r\n responseMessage = revertSuccessMessage;\r\n }\r\n }\r\n } catch (error: any) {\r\n responseMessage = `❌ Failed to revert: ${error.message}`;\r\n }\r\n break;\r\n }\r\n\r\n case 'add-command':\r\n case 'add-command-auto-detect':\r\n // Handle custom command auto-detect management\r\n const { CustomCommandsManager } = await import('./utils/custom-commands-manager.js');\r\n const customCmdManager = CustomCommandsManager.getInstance();\r\n await customCmdManager.initialize();\r\n\r\n const addCmdSubCommand = args[0]?.toLowerCase();\r\n\r\n if (!addCmdSubCommand || addCmdSubCommand === 'list') {\r\n // List all custom commands\r\n const commands = customCmdManager.listCommands();\r\n if (commands.length === 0) {\r\n responseMessage = '📋 No custom commands configured.\\n\\n' +\r\n 'Add commands with: /add-command add <command>\\n' +\r\n 'These commands will be detected as terminal commands in Auto mode.';\r\n } else {\r\n responseMessage = `📋 Custom Terminal Commands (${commands.length}):\\n\\n` +\r\n commands.map(cmd => ` • ${cmd}`).join('\\n') +\r\n '\\n\\nUsage:\\n' +\r\n ' /add-command add <command> - Add a command\\n' +\r\n ' /add-command delete <command> - Delete a command';\r\n }\r\n } else if (addCmdSubCommand === 'add') {\r\n // Add a custom command\r\n const cmdToAdd = args.slice(1).join(' ').trim();\r\n if (!cmdToAdd) {\r\n responseMessage = '❌ Please specify a command to add.\\n\\nUsage: /add-command add <command>';\r\n } else {\r\n const result = await customCmdManager.addCommand(cmdToAdd);\r\n if (result.success) {\r\n responseMessage = `✅ ${result.message}\\n\\nThis word will now be detected as a terminal command in Auto mode.`;\r\n } else {\r\n responseMessage = `❌ ${result.message}`;\r\n }\r\n }\r\n } else if (addCmdSubCommand === 'delete') {\r\n // Delete a custom command\r\n const cmdToDelete = args.slice(1).join(' ').trim();\r\n if (!cmdToDelete) {\r\n responseMessage = '❌ Please specify a command to delete.\\n\\nUsage: /add-command delete <command>';\r\n } else {\r\n const result = await customCmdManager.deleteCommand(cmdToDelete);\r\n if (result.success) {\r\n responseMessage = `✅ ${result.message}`;\r\n } else {\r\n responseMessage = `❌ ${result.message}`;\r\n }\r\n }\r\n } else {\r\n responseMessage = `Unknown /add-command subcommand: ${addCmdSubCommand}\\n\\n` +\r\n 'Usage:\\n' +\r\n ' /add-command list - List all custom commands\\n' +\r\n ' /add-command add <command> - Add a custom command\\n' +\r\n ' /add-command delete <command> - Delete a custom command';\r\n }\r\n break;\r\n\r\n case 'background-task':\r\n case 'bkg':\r\n case 'bg-task':\r\n // Background task management commands\r\n const bkgSubCommand = args[0]?.toLowerCase();\r\n\r\n if (bkgSubCommand === 'list' || !bkgSubCommand) {\r\n // Show list of running background tasks\r\n const runningTasks = BackgroundTaskManager.getRunningTasks();\r\n\r\n if (runningTasks.length === 0) {\r\n responseMessage = '📭 No background tasks running.\\n\\nSwitch to Background mode (Ctrl+D) to run commands in the background.';\r\n } else {\r\n // Show picker for task selection\r\n if (this.onShowBackgroundTaskPickerCallback) {\r\n this.onShowBackgroundTaskPickerCallback(runningTasks);\r\n return; // Don't send text response, picker will handle it\r\n } else {\r\n // Fallback: show as text\r\n responseMessage = '🔄 Running Background Tasks:\\n\\n' +\r\n runningTasks.map((task, i) => {\r\n const durationSec = Math.round(task.durationMs / 1000);\r\n return `${i + 1}. ${task.command}\\n 📁 ${task.cwd} | ⏱️ ${durationSec}s running`;\r\n }).join('\\n\\n');\r\n }\r\n }\r\n } else if (bkgSubCommand === 'cancel') {\r\n // Show list of running tasks for cancellation\r\n const runningTasks = BackgroundTaskManager.getRunningTasks();\r\n\r\n if (runningTasks.length === 0) {\r\n responseMessage = '📭 No background tasks to cancel.';\r\n } else {\r\n // Show picker for task cancellation\r\n if (this.onShowBackgroundTaskCancelPickerCallback) {\r\n this.onShowBackgroundTaskCancelPickerCallback(runningTasks);\r\n return; // Don't send text response, picker will handle it\r\n } else {\r\n responseMessage = '❌ Cancel picker not available. Use /background-task list to see tasks.';\r\n }\r\n }\r\n } else {\r\n responseMessage = `Unknown /background-task subcommand: ${bkgSubCommand}\\n\\n` +\r\n 'Usage:\\n' +\r\n ' /background-task list - List running background tasks\\n' +\r\n ' /background-task cancel - Cancel a running background task';\r\n }\r\n break;\r\n\r\n case 'workflow':\r\n case 'wf':\r\n // Workflow management commands\r\n const wfSubCommand = args[0]?.toLowerCase();\r\n\r\n if (!wfSubCommand) {\r\n // Show workflow help\r\n responseMessage = `📋 **Workflow Commands:**\r\n\r\n **/workflow new** - Create a new workflow\r\n **/workflow list** - List saved workflows\r\n **/workflow run <name>** - Run a workflow\r\n **/workflow view <name>** - View workflow steps\r\n **/workflow delete <name>** - Delete a workflow\r\n\r\n**What are workflows?**\r\nWorkflows let you save and replay sequences of commands and instructions.\r\nCreate once, run many times across different machines.`;\r\n } else if (wfSubCommand === 'new' || wfSubCommand === 'create') {\r\n // Check for nested subcommand (manual or learn-workflow)\r\n const newSubCommand = args[1]?.toLowerCase();\r\n\r\n if (newSubCommand === 'manual') {\r\n // If learning mode is active, cancel it instead of starting manual mode\r\n if (this.workflowLearningActive) {\r\n responseMessage = this.cancelWorkflowLearning('Switching to manual mode');\r\n break; // Don't start manual mode, just show cancellation message\r\n }\r\n\r\n // Show workflow creator screen (manual mode)\r\n if (this.onShowWorkflowCreatorCallback) {\r\n this.onShowWorkflowCreatorCallback();\r\n return; // Don't send text response, UI will handle it\r\n } else {\r\n responseMessage = '❌ Workflow creator not available. Please update the CLI.';\r\n }\r\n } else if (newSubCommand === 'learn-workflow') {\r\n // Toggle workflow learning mode\r\n const result = this.toggleWorkflowLearning();\r\n if (result === null) {\r\n return; // UI will handle the screen change\r\n }\r\n responseMessage = result;\r\n } else if (newSubCommand) {\r\n // Unknown subcommand\r\n responseMessage = `❌ Unknown workflow new subcommand: ${newSubCommand}. Use 'manual' or 'learn-workflow'.`;\r\n } else {\r\n // No subcommand - show help for new options\r\n responseMessage = `📋 **Create a New Workflow:**\r\n\r\n **/workflow new manual** - Manually create by typing steps\r\n **/workflow new learn-workflow** - Learn from your commands and prompts\r\n\r\n**Manual Mode:** Type each step one at a time, toggle between command/instruction mode.\r\n\r\n**Learn Mode:** Start learning, then run commands and prompts naturally. Run the command again to save.`;\r\n }\r\n } else if (wfSubCommand === 'list' || wfSubCommand === 'ls') {\r\n // List saved workflows\r\n const workflows = workflowStorage.list();\r\n\r\n if (workflows.length === 0) {\r\n responseMessage = '📭 No workflows saved yet.\\n\\nCreate one with: /workflow new';\r\n } else {\r\n responseMessage = `📋 **Saved Workflows (${workflows.length}):**\\n\\n`;\r\n for (const wf of workflows) {\r\n const date = new Date(wf.updatedAt).toLocaleDateString();\r\n responseMessage += `• **${wf.name}** (${wf.stepCount} steps)\\n`;\r\n if (wf.description) {\r\n responseMessage += ` ${wf.description}\\n`;\r\n }\r\n responseMessage += ` Last updated: ${date}\\n\\n`;\r\n }\r\n responseMessage += 'Use `/workflow run <name>` to execute a workflow.';\r\n }\r\n } else if (wfSubCommand === 'run' || wfSubCommand === 'execute') {\r\n // Run a workflow\r\n const wfName = args.slice(1).join(' ').trim();\r\n\r\n if (!wfName) {\r\n responseMessage = 'Usage: /workflow run <name>\\n\\nUse /workflow list to see available workflows.';\r\n } else {\r\n // Cancel workflow learning if active before running a different workflow\r\n const runCancelMsg = this.cancelWorkflowLearning('Running another workflow');\r\n if (runCancelMsg && this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(runCancelMsg);\r\n }\r\n\r\n const workflow = workflowStorage.load(wfName);\r\n\r\n if (!workflow) {\r\n responseMessage = `❌ Workflow '${wfName}' not found.\\n\\nUse /workflow list to see available workflows.`;\r\n } else {\r\n // Start workflow execution\r\n responseMessage = `🚀 Starting workflow: **${workflow.name}**\\n\\n` +\r\n `${workflow.steps.length} steps to execute...\\n\\n` +\r\n workflowStorage.formatWorkflowForDisplay(workflow);\r\n\r\n // Send initial response\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(responseMessage);\r\n }\r\n\r\n // Trigger workflow execution by sending the workflow to the AI\r\n // Format the workflow as a prompt for the AI to execute\r\n const workflowPrompt = this.buildWorkflowPrompt(workflow);\r\n\r\n // Use setTimeout to allow the UI to update before starting execution\r\n setTimeout(() => {\r\n this.handleMessage(workflowPrompt, { isolatedWorkflow: true }).catch((err: any) => {\r\n quickLog(`[${new Date().toISOString()}] [Workflow] Error executing workflow: ${err.message}\\n`);\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(`❌ Error executing workflow: ${err.message}`);\r\n }\r\n });\r\n }, 100);\r\n\r\n return;\r\n }\r\n }\r\n } else if (wfSubCommand === 'view' || wfSubCommand === 'show') {\r\n // View a workflow's steps\r\n const wfName = args.slice(1).join(' ').trim();\r\n\r\n if (!wfName) {\r\n responseMessage = 'Usage: /workflow view <name>';\r\n } else {\r\n const workflow = workflowStorage.load(wfName);\r\n\r\n if (!workflow) {\r\n responseMessage = `❌ Workflow '${wfName}' not found.\\n\\nUse /workflow list to see available workflows.`;\r\n } else {\r\n responseMessage = workflowStorage.formatWorkflowForDisplay(workflow);\r\n }\r\n }\r\n } else if (wfSubCommand === 'delete' || wfSubCommand === 'rm' || wfSubCommand === 'remove') {\r\n // Delete a workflow\r\n const wfName = args.slice(1).join(' ').trim();\r\n\r\n if (!wfName) {\r\n responseMessage = 'Usage: /workflow delete <name>';\r\n } else {\r\n const result = workflowStorage.delete(wfName);\r\n\r\n if (result.success) {\r\n responseMessage = `✅ Workflow '${wfName}' deleted successfully.`;\r\n } else {\r\n responseMessage = `❌ ${result.error}`;\r\n }\r\n }\r\n } else {\r\n responseMessage = `Unknown /workflow subcommand: ${wfSubCommand}\\n\\n` +\r\n 'Usage:\\n' +\r\n ' /workflow new - Create a new workflow\\n' +\r\n ' /workflow list - List saved workflows\\n' +\r\n ' /workflow run <name> - Run a workflow\\n' +\r\n ' /workflow view <name> - View workflow steps\\n' +\r\n ' /workflow delete <name> - Delete a workflow';\r\n }\r\n break;\r\n\r\n case 'rules':\r\n const rulesSubCommand = args[0]?.toLowerCase();\r\n\r\n if (!rulesSubCommand) {\r\n responseMessage = `📜 **Rule Commands:**\r\n\r\n **/rules list** - List saved rules\r\n **/rules add** - Create a new reusable rule\r\n **/rules edit <name>** - Edit an existing rule\r\n **/rules delete <name>** - Delete a saved rule\r\n\r\n**How to use rules**\r\nAfter saving a rule, reference it in any prompt with \\`@rules:<name>\\`.`;\r\n } else if (rulesSubCommand === 'list' || rulesSubCommand === 'ls') {\r\n const rules = rulesStorage.list();\r\n\r\n if (rules.length === 0) {\r\n responseMessage = '📭 No rules saved yet.\\n\\nCreate one with: /rules add';\r\n } else {\r\n responseMessage = `📜 **Saved Rules (${rules.length}):**\\n\\n`;\r\n for (const rule of rules) {\r\n const date = new Date(rule.updatedAt).toLocaleDateString();\r\n responseMessage += `• **${rule.name}**\\n`;\r\n responseMessage += ` ${rule.preview}\\n`;\r\n responseMessage += ` Last updated: ${date}\\n`;\r\n responseMessage += ` Use in prompts: @rules:${rule.name}\\n\\n`;\r\n }\r\n }\r\n } else if (rulesSubCommand === 'add' || rulesSubCommand === 'new' || rulesSubCommand === 'create') {\r\n if (this.onShowRulesEditorCallback) {\r\n this.onShowRulesEditorCallback({ mode: 'add' });\r\n return;\r\n }\r\n\r\n responseMessage = '❌ Rules editor not available. Please update the CLI.';\r\n } else if (rulesSubCommand === 'edit') {\r\n const ruleName = args.slice(1).join(' ').trim();\r\n\r\n if (!ruleName) {\r\n responseMessage = 'Usage: /rules edit <name>';\r\n break;\r\n }\r\n\r\n const rule = rulesStorage.load(ruleName);\r\n if (!rule) {\r\n responseMessage = `❌ Rule '${ruleName}' not found.\\n\\nUse /rules list to see available rules.`;\r\n break;\r\n }\r\n\r\n if (this.onShowRulesEditorCallback) {\r\n this.onShowRulesEditorCallback({\r\n mode: 'edit',\r\n initialName: rule.name,\r\n initialContent: rule.content\r\n });\r\n return;\r\n }\r\n\r\n responseMessage = '❌ Rules editor not available. Please update the CLI.';\r\n } else if (rulesSubCommand === 'delete' || rulesSubCommand === 'rm' || rulesSubCommand === 'remove') {\r\n const ruleName = args.slice(1).join(' ').trim();\r\n\r\n if (!ruleName) {\r\n responseMessage = 'Usage: /rules delete <name>';\r\n break;\r\n }\r\n\r\n const result = rulesStorage.delete(ruleName);\r\n if (result.success) {\r\n const deletedRuleName = rulesStorage.sanitizeName(ruleName);\r\n responseMessage = `✅ Rule '${deletedRuleName}' deleted successfully.`;\r\n } else {\r\n responseMessage = `❌ ${result.error}`;\r\n }\r\n } else {\r\n responseMessage = `Unknown /rules subcommand: ${rulesSubCommand}\\n\\n` +\r\n 'Usage:\\n' +\r\n ' /rules list - List saved rules\\n' +\r\n ' /rules add - Create a new rule\\n' +\r\n ' /rules edit <name> - Edit a saved rule\\n' +\r\n ' /rules delete <name> - Delete a saved rule';\r\n }\r\n break;\r\n\r\n case 'sync':\r\n // Sync local data to/from cloud\r\n if (!apiClient.isAuthenticated()) {\r\n responseMessage = '❌ You must be signed in to sync data.\\n\\nUse /sign-in to authenticate first.';\r\n break;\r\n }\r\n\r\n // Parse subcommand\r\n const syncSubcommand = args[0]?.toLowerCase();\r\n\r\n if (!syncSubcommand) {\r\n // Show sync help\r\n responseMessage = '☁️ Sync Commands:\\n\\n' +\r\n ' /sync upload - Upload local data to cloud (overwrites cloud data)\\n' +\r\n ' /sync restore - Download cloud data and restore locally (overwrites local data)\\n\\n' +\r\n 'Use these commands to backup or restore your chat history and settings.';\r\n break;\r\n }\r\n\r\n if (syncSubcommand === 'upload') {\r\n try {\r\n responseMessage = '☁️ Uploading data to cloud...';\r\n\r\n // Send initial response\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(responseMessage);\r\n }\r\n\r\n // Gather all local data\r\n const config = this.configManager.load();\r\n const chats = localChatStorage.listChats();\r\n\r\n // Load full chat data for each chat\r\n const fullChats = chats.map(chatMeta => {\r\n const fullChat = localChatStorage.loadChat(chatMeta.id);\r\n return fullChat;\r\n }).filter(chat => chat !== null);\r\n\r\n // Create sync data structure\r\n const syncData = {\r\n version: 1,\r\n exportedAt: new Date().toISOString(),\r\n config: {\r\n model: config.model,\r\n modelName: config.modelName,\r\n enhancedQuality: config.enhancedQuality,\r\n autonomousMode: config.autonomousMode,\r\n },\r\n chats: fullChats,\r\n metadata: {\r\n totalChats: fullChats.length,\r\n totalMessages: fullChats.reduce((sum, chat) => sum + (chat?.messageCount || 0), 0),\r\n }\r\n };\r\n\r\n // Upload to backend\r\n const result = await apiClient.uploadSyncData(syncData);\r\n\r\n responseMessage = `✅ Data uploaded successfully!\\n\\n` +\r\n `📊 Upload Summary:\\n` +\r\n ` • Chats: ${fullChats.length}\\n` +\r\n ` • Messages: ${syncData.metadata.totalMessages}\\n` +\r\n ` • Version: ${result.version}\\n` +\r\n ` • Updated: ${new Date(result.updatedAt).toLocaleString()}\\n\\n` +\r\n `Your local data is now backed up to the cloud.`;\r\n } catch (error: any) {\r\n responseMessage = `❌ Upload failed: ${error.message}\\n\\nPlease try again later.`;\r\n }\r\n } else if (syncSubcommand === 'restore') {\r\n try {\r\n responseMessage = '☁️ Downloading data from cloud...';\r\n\r\n // Send initial response\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(responseMessage);\r\n }\r\n\r\n // Get sync data from backend\r\n const cloudData = await apiClient.getSyncData();\r\n\r\n if (!cloudData) {\r\n responseMessage = '❌ No cloud data found.\\n\\nUse /sync upload first to backup your data.';\r\n break;\r\n }\r\n\r\n const syncData = cloudData.syncData;\r\n\r\n // Validate sync data structure\r\n if (!syncData || typeof syncData !== 'object') {\r\n responseMessage = '❌ Invalid cloud data format.\\n\\nPlease try uploading again with /sync upload.';\r\n break;\r\n }\r\n\r\n // Restore config if present\r\n if (syncData.config) {\r\n const currentConfig = this.configManager.load();\r\n if (syncData.config.model) {\r\n currentConfig.model = syncData.config.model;\r\n }\r\n if (syncData.config.modelName) {\r\n currentConfig.modelName = syncData.config.modelName;\r\n }\r\n if (typeof syncData.config.enhancedQuality === 'boolean') {\r\n currentConfig.enhancedQuality = syncData.config.enhancedQuality;\r\n }\r\n if (typeof syncData.config.autonomousMode === 'boolean') {\r\n currentConfig.autonomousMode = syncData.config.autonomousMode;\r\n }\r\n this.configManager.save(currentConfig);\r\n }\r\n\r\n // Restore chats if present\r\n let restoredChats = 0;\r\n let restoredMessages = 0;\r\n if (syncData.chats && Array.isArray(syncData.chats)) {\r\n for (const chat of syncData.chats) {\r\n if (chat && chat.id) {\r\n // Import each chat to local storage\r\n const success = localChatStorage.importChat(chat);\r\n if (success) {\r\n restoredChats++;\r\n restoredMessages += chat.messageCount || chat.messages?.length || 0;\r\n }\r\n }\r\n }\r\n }\r\n\r\n responseMessage = `✅ Data restored successfully!\\n\\n` +\r\n `📊 Restore Summary:\\n` +\r\n ` • Chats restored: ${restoredChats}\\n` +\r\n ` • Messages restored: ${restoredMessages}\\n` +\r\n ` • Cloud version: ${cloudData.version}\\n` +\r\n ` • Last updated: ${new Date(cloudData.updatedAt).toLocaleString()}\\n\\n` +\r\n `Your local data has been restored from the cloud.\\n` +\r\n `Note: Existing local chats with the same ID were overwritten.`;\r\n } catch (error: any) {\r\n responseMessage = `❌ Restore failed: ${error.message}\\n\\nPlease try again later.`;\r\n }\r\n } else {\r\n responseMessage = `❌ Unknown sync subcommand: ${syncSubcommand}\\n\\n` +\r\n 'Available subcommands:\\n' +\r\n ' /sync upload - Upload local data to cloud\\n' +\r\n ' /sync restore - Download and restore cloud data locally';\r\n }\r\n break;\r\n\r\n default:\r\n responseMessage = `Unknown command: /${cmd}\\nType /help for available commands.`;\r\n }\r\n\r\n // Send command response directly to history (not streaming)\r\n if (responseMessage && this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(responseMessage);\r\n }\r\n }\r\n\r\n /**\r\n * Set callback for showing chat picker\r\n */\r\n setOnShowChatPickerCallback(callback: (chats: LocalChatMeta[], currentChatId: string | null) => void): void {\r\n this.onShowChatPickerCallback = callback;\r\n }\r\n\r\n /**\r\n * Set callback for showing chat delete picker\r\n */\r\n setOnShowChatDeletePickerCallback(callback: (chats: LocalChatMeta[], currentChatId: string | null) => void): void {\r\n this.onShowChatDeletePickerCallback = callback;\r\n }\r\n\r\n /**\r\n * Set callback for showing chat list (read-only view)\r\n */\r\n setOnShowChatListCallback(callback: (chats: LocalChatMeta[], currentChatId: string | null) => void): void {\r\n this.onShowChatListCallback = callback;\r\n }\r\n\r\n /**\r\n * Set callback for showing chat rename picker\r\n */\r\n setOnShowChatRenamePickerCallback(callback: (chats: LocalChatMeta[], currentChatId: string | null) => void): void {\r\n this.onShowChatRenamePickerCallback = callback;\r\n }\r\n\r\n /**\r\n * Set callback for background mode change\r\n */\r\n setOnBackgroundModeChange(callback: (backgroundMode: boolean) => void): void {\r\n this.onBackgroundModeChange = callback;\r\n }\r\n\r\n /**\r\n * Set callback for background task count change\r\n */\r\n setOnBackgroundTaskCountChange(callback: (count: number) => void): void {\r\n this.onBackgroundTaskCountChange = callback;\r\n // Subscribe to BackgroundTaskManager count changes\r\n BackgroundTaskManager.on('countChanged', (count: number) => {\r\n this.onBackgroundTaskCountChange?.(count);\r\n });\r\n }\r\n\r\n /**\r\n * Set callback for setting Auto mode (used after background task starts)\r\n */\r\n setOnSetAutoMode(callback: (enabled: boolean) => void): void {\r\n this.onSetAutoMode = callback;\r\n }\r\n\r\n /**\r\n * Set callback for showing background task picker\r\n */\r\n setOnShowBackgroundTaskPickerCallback(callback: (tasks: BackgroundTaskInfo[]) => void): void {\r\n this.onShowBackgroundTaskPickerCallback = callback;\r\n }\r\n\r\n /**\r\n * Set callback for showing background task cancel picker\r\n */\r\n setOnShowBackgroundTaskCancelPickerCallback(callback: (tasks: BackgroundTaskInfo[]) => void): void {\r\n this.onShowBackgroundTaskCancelPickerCallback = callback;\r\n }\r\n\r\n /**\r\n * Toggle background mode\r\n */\r\n toggleBackgroundMode(): void {\r\n this.backgroundMode = !this.backgroundMode;\r\n if (this.onBackgroundModeChange) {\r\n this.onBackgroundModeChange(this.backgroundMode);\r\n }\r\n }\r\n\r\n /**\r\n * Get current background mode state\r\n */\r\n getBackgroundMode(): boolean {\r\n return this.backgroundMode;\r\n }\r\n\r\n /**\r\n * Handle background task cancellation\r\n */\r\n handleBackgroundTaskCancel(taskId: string): void {\r\n const task = BackgroundTaskManager.getTask(taskId);\r\n const success = BackgroundTaskManager.cancelTask(taskId);\r\n\r\n if (success) {\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(`🛑 Cancelled background task: ${task?.command || taskId}`);\r\n }\r\n } else {\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(`❌ Failed to cancel task. It may have already completed.`);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Handle background task selection - show task output in focus mode\r\n */\r\n handleBackgroundTaskSelection(taskId: string): void {\r\n const task = BackgroundTaskManager.getTask(taskId);\r\n if (!task) {\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(`❌ Task not found: ${taskId}`);\r\n }\r\n return;\r\n }\r\n\r\n // Get the full output from the task\r\n const output = BackgroundTaskManager.getTaskOutput(taskId);\r\n\r\n // If we have the view callback, use it to show in focus mode (InteractiveShell)\r\n if (this.onBackgroundTaskViewCallback) {\r\n // Create event handlers for the App.tsx useEffect to subscribe to\r\n let onOutputHandler: ((chunk: string) => void) | undefined;\r\n let onCompleteHandler: ((exitCode: number) => void) | undefined;\r\n\r\n // Subscribe to future output updates for this task\r\n const outputListener = (taskIdEvent: string, chunk: string) => {\r\n if (taskIdEvent === taskId && onOutputHandler) {\r\n onOutputHandler(chunk);\r\n }\r\n };\r\n\r\n // Subscribe to task completion\r\n const completeListener = (taskIdEvent: string, exitCode: number) => {\r\n if (taskIdEvent === taskId && onCompleteHandler) {\r\n onCompleteHandler(exitCode);\r\n // Cleanup listeners\r\n BackgroundTaskManager.off('taskOutput', outputListener);\r\n BackgroundTaskManager.off('taskCompleted', completeListener);\r\n }\r\n };\r\n\r\n BackgroundTaskManager.on('taskOutput', outputListener);\r\n BackgroundTaskManager.on('taskCompleted', completeListener);\r\n\r\n // Invoke the callback to set up shellState in App.tsx\r\n this.onBackgroundTaskViewCallback({\r\n id: taskId,\r\n command: task.command,\r\n cwd: task.cwd,\r\n output,\r\n isRunning: task.isRunning,\r\n onOutput: (handler) => { onOutputHandler = handler; },\r\n onComplete: (handler) => { onCompleteHandler = handler; }\r\n });\r\n\r\n // If task is already complete, immediately trigger completion\r\n if (!task.isRunning && task.exitCode !== undefined) {\r\n setTimeout(() => {\r\n if (onCompleteHandler) {\r\n onCompleteHandler(task.exitCode!);\r\n }\r\n }, 100);\r\n }\r\n } else {\r\n // Fallback: show as static message\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(`📺 Viewing output for: ${task.command}\\n\\n${output || '(no output yet)'}`);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Set callback for showing a background task in focus mode\r\n */\r\n setOnBackgroundTaskViewCallback(callback: (task: { id: string; command: string; cwd: string; output: string; isRunning: boolean; onOutput: (handler: (chunk: string) => void) => void; onComplete: (handler: (exitCode: number) => void) => void }) => void): void {\r\n this.onBackgroundTaskViewCallback = callback;\r\n }\r\n\r\n /**\r\n * Handle chat rename operation\r\n */\r\n handleChatRename(chatId: string, newTitle: string): void {\r\n const chat = localChatStorage.loadChat(chatId);\r\n const oldTitle = chat?.title || 'Unknown chat';\r\n\r\n const success = localChatStorage.renameChat(chatId, newTitle);\r\n\r\n if (success) {\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(`✏️ Renamed chat: \"${oldTitle}\" → \"${newTitle}\"`);\r\n }\r\n } else {\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback('❌ Failed to rename the chat.');\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Handle chat delete picker selection\r\n */\r\n async handleChatDeleteSelection(chatId: string): Promise<void> {\r\n const chat = localChatStorage.loadChat(chatId);\r\n const chatTitle = chat?.title || 'Unknown chat';\r\n const isDeletingCurrentChat = this.currentChatId === chatId;\r\n\r\n // Get the backend conversation ID (UUID) if available for file deletion\r\n // Files are stored with the backend UUID, not the local chat ID\r\n const fileDeleteId = chat?.backendConversationId || chatId;\r\n\r\n const success = localChatStorage.deleteChat(chatId);\r\n\r\n if (success) {\r\n if (this.checkpointManager) {\r\n this.checkpointManager.deleteCheckpointsForChat(chatId);\r\n }\r\n\r\n // Clean up any files associated with this conversation (images in GCS)\r\n // Use backendConversationId (UUID) if available, as that's what files are stored under\r\n // This is a fire-and-forget operation - don't block on it\r\n if (apiClient.isAuthenticated()) {\r\n apiClient.deleteConversationFiles(fileDeleteId).catch(() => {\r\n // Silently ignore - files might not exist or backend error\r\n });\r\n }\r\n\r\n // If we deleted the current chat, start a new chat\r\n if (isDeletingCurrentChat) {\r\n this.startNewChat();\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(`🗑️ Deleted current chat: \"${chatTitle}\"\\n\\n🆕 Started a new chat session.`);\r\n }\r\n // Clear UI messages via restore callback with empty array\r\n if (this.onRestoreMessagesCallback) {\r\n this.onRestoreMessagesCallback([]);\r\n }\r\n } else {\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(`🗑️ Deleted chat: \"${chatTitle}\"`);\r\n }\r\n }\r\n } else {\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback('❌ Failed to delete the selected chat.');\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Handle background mode execution - runs command in background and returns immediately\r\n * Supports both local and remote shells (SSH, WSL, Docker)\r\n */\r\n private handleBackgroundModeExecution(command: string): void {\r\n if (!command.trim()) {\r\n return;\r\n }\r\n\r\n // Get current context to determine if we're in a remote shell\r\n const currentContext = this.contextManager.getCurrentContext();\r\n\r\n let taskId: string;\r\n let effectiveCwd: string;\r\n let remoteContextDisplay: string | undefined;\r\n\r\n if (currentContext.type === 'local') {\r\n // Local execution - use PTY\r\n effectiveCwd = this.cwd;\r\n taskId = BackgroundTaskManager.startTask(command, effectiveCwd);\r\n } else {\r\n // Remote execution - use appropriate remote PTY\r\n const metadata = currentContext.metadata;\r\n effectiveCwd = metadata?.workingDirectory || '~';\r\n\r\n // Build remote context display string\r\n if (currentContext.type === 'ssh' && metadata) {\r\n remoteContextDisplay = `${metadata.username || 'user'}@${metadata.hostname || 'remote'}`;\r\n } else if (currentContext.type === 'wsl' && metadata) {\r\n remoteContextDisplay = `wsl:${metadata.distroName || 'wsl'}`;\r\n } else if (currentContext.type === 'docker' && metadata) {\r\n remoteContextDisplay = `docker:${metadata.containerId?.substring(0, 12) || 'container'}`;\r\n }\r\n\r\n // Create the remote PTY process and register with BackgroundTaskManager\r\n if (currentContext.type === 'ssh') {\r\n const sshClient = currentContext.handler?.client;\r\n if (!sshClient) {\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback('❌ SSH client not available for background task');\r\n }\r\n return;\r\n }\r\n\r\n // Start remote task first to get callbacks\r\n const remoteTask = BackgroundTaskManager.startRemoteTask(\r\n command,\r\n effectiveCwd,\r\n remoteContextDisplay || 'ssh'\r\n );\r\n taskId = remoteTask.id;\r\n\r\n // Create SSH PTY with the callbacks from BackgroundTaskManager\r\n const sshPty = runSSHCommand(\r\n sshClient,\r\n command,\r\n effectiveCwd,\r\n remoteTask.onData,\r\n remoteTask.onExit\r\n );\r\n remoteTask.setRemotePty(sshPty);\r\n\r\n } else if (currentContext.type === 'wsl') {\r\n const distribution = metadata?.distroName || 'Ubuntu';\r\n\r\n // Start remote task first to get callbacks\r\n const remoteTask = BackgroundTaskManager.startRemoteTask(\r\n command,\r\n effectiveCwd,\r\n remoteContextDisplay || 'wsl'\r\n );\r\n taskId = remoteTask.id;\r\n\r\n // Create WSL PTY with the callbacks from BackgroundTaskManager\r\n const wslPty = runWSLCommand(\r\n distribution,\r\n command,\r\n effectiveCwd,\r\n remoteTask.onData,\r\n remoteTask.onExit\r\n );\r\n remoteTask.setRemotePty(wslPty);\r\n\r\n } else if (currentContext.type === 'docker') {\r\n const containerId = metadata?.containerId;\r\n if (!containerId) {\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback('❌ Docker container ID not available for background task');\r\n }\r\n return;\r\n }\r\n\r\n // Check if Docker is nested inside SSH\r\n const parentContext = this.contextManager.getParentContext();\r\n\r\n // Start remote task first to get callbacks\r\n const remoteTask = BackgroundTaskManager.startRemoteTask(\r\n command,\r\n effectiveCwd,\r\n remoteContextDisplay || 'docker'\r\n );\r\n taskId = remoteTask.id;\r\n\r\n if (parentContext && parentContext.type === 'ssh') {\r\n // Nested Docker inside SSH: route docker exec command through SSH\r\n const sshClient = parentContext.handler?.client;\r\n if (!sshClient) {\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback('❌ SSH client not available for nested Docker background task');\r\n }\r\n return;\r\n }\r\n\r\n // Build docker exec command to run via SSH\r\n const escapedCommand = command.replace(/\"/g, '\\\\\"');\r\n const dockerCommand = `docker exec -w \"${effectiveCwd}\" ${containerId} sh -c \"${escapedCommand}\"`;\r\n\r\n // Create SSH PTY to run the docker command\r\n const sshPty = runSSHCommand(\r\n sshClient,\r\n dockerCommand,\r\n parentContext.metadata.workingDirectory || '~',\r\n remoteTask.onData,\r\n remoteTask.onExit\r\n );\r\n remoteTask.setRemotePty(sshPty);\r\n } else {\r\n // Local Docker: use standard runDockerCommand\r\n const dockerPty = runDockerCommand(\r\n containerId,\r\n command,\r\n effectiveCwd,\r\n remoteTask.onData,\r\n remoteTask.onExit\r\n );\r\n remoteTask.setRemotePty(dockerPty);\r\n }\r\n\r\n } else {\r\n // Unknown remote type - fall back to local\r\n effectiveCwd = this.cwd;\r\n taskId = BackgroundTaskManager.startTask(command, effectiveCwd);\r\n }\r\n }\r\n\r\n // Notify user that task started\r\n const count = BackgroundTaskManager.getRunningCount();\r\n const locationInfo = remoteContextDisplay\r\n ? `\\n📍 Running on: ${remoteContextDisplay}`\r\n : '';\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(`🚀 Background task started: ${command}${locationInfo}\\n\\nTask ID: ${taskId}\\nUse /background-task list to see running tasks.`);\r\n }\r\n\r\n // Optionally record to history for AI context\r\n this.recordShellCommandToHistory(command, '[Running in background]', effectiveCwd);\r\n\r\n // Reset to Auto mode after starting task\r\n this.backgroundMode = false;\r\n if (this.onBackgroundModeChange) {\r\n this.onBackgroundModeChange(false);\r\n }\r\n // Enable Auto mode in InputBox\r\n if (this.onSetAutoMode) {\r\n this.onSetAutoMode(true);\r\n }\r\n }\r\n\r\n\r\n /**\r\n * Record a user shell command and its output to conversation history\r\n * This allows the AI to see what commands the user ran in command mode\r\n */\r\n private recordShellCommandToHistory(command: string, output: string, cwd: string, exitCode?: number): void {\r\n // Add as a user message so AI knows what the user did\r\n const shellCommandInfo = exitCode !== undefined && exitCode !== 0\r\n ? `[User ran shell command in ${cwd}]\\nCommand: ${command}\\nExit Code: ${exitCode}\\nOutput:\\n${output}`\r\n : `[User ran shell command in ${cwd}]\\nCommand: ${command}\\nOutput:\\n${output || '(no output)'}`;\r\n\r\n this.conversationHistory.push({\r\n role: 'user',\r\n content: shellCommandInfo\r\n });\r\n }\r\n\r\n /**\r\n * Save current conversation to local storage\r\n */\r\n private saveCurrentChat(options?: { allowEmpty?: boolean }): void {\r\n const allowEmpty = options?.allowEmpty ?? false;\r\n\r\n // Only save if there are messages (AI conversation or shell commands),\r\n // unless explicitly forced (revert-to-first-message edge case).\r\n if (this.conversationHistory.length === 0 && !allowEmpty) {\r\n return;\r\n }\r\n\r\n // Generate chat ID if not exists\r\n if (!this.currentChatId) {\r\n if (allowEmpty) {\r\n // Avoid creating brand-new empty chats.\r\n return;\r\n }\r\n this.currentChatId = localChatStorage.generateChatId();\r\n }\r\n\r\n // Convert to StoredMessage format (AI context)\r\n const storedMessages: StoredMessage[] = this.conversationHistory.map(msg => ({\r\n role: msg.role as StoredMessage['role'],\r\n content: msg.content as string,\r\n tool_calls: msg.tool_calls,\r\n tool_call_id: msg.tool_call_id,\r\n }));\r\n\r\n // Convert UI messages to serializable format\r\n const storedUIMessages: StoredUIMessage[] = this.uiMessageHistory.map(msg => ({\r\n id: msg.id,\r\n role: msg.role,\r\n content: msg.content,\r\n timestamp: msg.timestamp?.toISOString(),\r\n toolExecution: msg.toolExecution ? {\r\n toolName: msg.toolExecution.toolName,\r\n status: msg.toolExecution.status,\r\n result: msg.toolExecution.result,\r\n error: msg.toolExecution.error,\r\n arguments: msg.toolExecution.arguments,\r\n } : undefined,\r\n shouldStream: msg.shouldStream,\r\n isCommandMode: msg.isCommandMode,\r\n tool_call_id: msg.tool_call_id,\r\n tool_calls: msg.tool_calls,\r\n thinkingDuration: msg.thinkingDuration,\r\n taskCompletion: msg.taskCompletion,\r\n planAccepted: msg.planAccepted,\r\n connectionStatus: msg.connectionStatus, // For SSH/WSL/Docker connection status boxes\r\n }));\r\n\r\n // Capture remote context if in remote environment (SSH/WSL/Docker)\r\n // Use null to explicitly clear remote context when not in remote (so resuming won't reconnect)\r\n let remoteContext: StoredRemoteContext | null = null;\r\n let remoteContextStack: StoredRemoteContext[] | null = null;\r\n const currentContext = this.contextManager.getCurrentContext();\r\n\r\n // Build remoteContextStack for nested sessions\r\n if (currentContext.type !== 'local' && this.connectionCommandStack.length > 0) {\r\n remoteContextStack = [];\r\n\r\n // Build stack of contexts from the connectionCommandStack and cwdStack\r\n // cwdStack[0] = local CWD before first remote\r\n // cwdStack[1] = first remote CWD before second remote (if nested)\r\n // connectionCommandStack[0] = first connection command\r\n // connectionCommandStack[1] = second connection command (if nested)\r\n for (let i = 0; i < this.connectionCommandStack.length; i++) {\r\n const connCmd = this.connectionCommandStack[i];\r\n // Detect the type from the command\r\n let ctxType: 'ssh' | 'wsl' | 'docker' = 'ssh';\r\n if (connCmd.startsWith('docker ') || connCmd.startsWith('docker-compose ')) {\r\n ctxType = 'docker';\r\n } else if (connCmd.startsWith('wsl')) {\r\n ctxType = 'wsl';\r\n }\r\n\r\n // Get the CWD before this remote connection\r\n const cwdBefore = i < this.cwdStack.length ? this.cwdStack[i] : process.cwd();\r\n\r\n // For the last (current) context, use the actual metadata\r\n const isLastContext = i === this.connectionCommandStack.length - 1;\r\n const remoteCwd = isLastContext ? currentContext.metadata.workingDirectory : (this.cwdStack[i + 1] || '~');\r\n\r\n const storedCtx: StoredRemoteContext = {\r\n type: ctxType,\r\n connectionCommand: connCmd,\r\n remoteCwd: remoteCwd,\r\n localCwdBeforeRemote: cwdBefore,\r\n metadata: isLastContext ? {\r\n hostname: currentContext.metadata.hostname,\r\n username: currentContext.metadata.username,\r\n distroName: currentContext.metadata.distroName,\r\n containerId: currentContext.metadata.containerId,\r\n port: currentContext.metadata.port,\r\n } : {}\r\n };\r\n\r\n remoteContextStack.push(storedCtx);\r\n }\r\n\r\n // For backward compatibility, also set remoteContext to the last (current) context\r\n if (remoteContextStack.length > 0) {\r\n remoteContext = remoteContextStack[remoteContextStack.length - 1];\r\n }\r\n }\r\n\r\n // Determine the local CWD to save (use base of cwdStack if in remote, otherwise current cwd)\r\n const cwdToSave = currentContext.type !== 'local' && this.cwdStack.length > 0\r\n ? this.cwdStack[0]\r\n : this.cwd;\r\n\r\n const checkpointIndex: StoredCheckpointMeta[] | undefined = this.checkpointManager\r\n ? this.checkpointManager.list().map(cp => this.toStoredCheckpointMeta(cp))\r\n : undefined;\r\n\r\n try {\r\n localChatStorage.saveChat(\r\n this.currentChatId,\r\n storedMessages,\r\n storedUIMessages,\r\n cwdToSave,\r\n remoteContext,\r\n remoteContextStack,\r\n checkpointIndex\r\n );\r\n\r\n // Also store the backend conversation ID (UUID) for file deletion\r\n // This is the ID used for GCS file storage, not the local chat ID\r\n const backendId = conversationManager.getCurrentConversationId();\r\n if (backendId && backendId !== this.currentChatId) {\r\n localChatStorage.setBackendConversationId(this.currentChatId, backendId);\r\n }\r\n } catch (error) {\r\n // Silently ignore save errors - don't interrupt the user\r\n }\r\n }\r\n\r\n /**\r\n * Load a chat from local storage and restore it\r\n */\r\n loadChat(chatId: string, options?: { preserveCwd?: boolean }): boolean {\r\n const chat = localChatStorage.loadChat(chatId);\r\n if (!chat) {\r\n return false;\r\n }\r\n\r\n // Clear current history and load the saved chat\r\n this.conversationHistory = chat.messages.map(msg => ({\r\n role: msg.role,\r\n content: msg.content,\r\n tool_calls: msg.tool_calls,\r\n tool_call_id: msg.tool_call_id,\r\n }));\r\n\r\n // IMPORTANT: Also restore UI message history from saved chat\r\n // This ensures that when the chat is saved again (e.g., after revert), \r\n // the correct UI messages are preserved\r\n if (chat.uiMessages && chat.uiMessages.length > 0) {\r\n this.uiMessageHistory = chat.uiMessages.map(msg => ({\r\n id: msg.id,\r\n role: msg.role,\r\n content: msg.content,\r\n timestamp: msg.timestamp ? new Date(msg.timestamp) : undefined,\r\n toolExecution: msg.toolExecution,\r\n shouldStream: false,\r\n isCommandMode: msg.isCommandMode,\r\n tool_call_id: msg.tool_call_id,\r\n tool_calls: msg.tool_calls,\r\n thinkingDuration: msg.thinkingDuration,\r\n taskCompletion: msg.taskCompletion,\r\n planAccepted: msg.planAccepted,\r\n connectionStatus: msg.connectionStatus,\r\n }));\r\n }\r\n\r\n // Set the current chat ID to continue the conversation\r\n this.currentChatId = chatId;\r\n this.conversationStarted = true;\r\n\r\n // Restore checkpoint state for this chat immediately so /revert works\r\n // right after /chat resume, even before a new AI message is sent.\r\n this.ensureCheckpointManagerForChat(chatId, chat.checkpointIndex);\r\n\r\n // Restore CWD if saved (important for commands to run in correct directory)\r\n // Allow skipping when preserving an active remote session\r\n if (chat.cwd && !options?.preserveCwd) {\r\n this.cwd = chat.cwd;\r\n // Update context manager if available\r\n if (this.contextManager) {\r\n this.contextManager.updateWorkingDirectory(chat.cwd);\r\n }\r\n // Notify UI of CWD change\r\n if (this.onCwdChange) {\r\n this.onCwdChange(chat.cwd);\r\n }\r\n }\r\n\r\n // Reset context limit state when loading a chat\r\n // We'll recalculate it based on the loaded conversation\r\n this.setContextLimitReachedState(false);\r\n\r\n // Update token count to reflect loaded conversation\r\n // This will also check if the loaded chat is near the limit\r\n this.updateTokenCount().catch(err => {\r\n quickLog(`[${new Date().toISOString()}] [loadChatFromPicker] Failed to update token count: ${err}\\n`);\r\n });\r\n\r\n // Clean up any orphaned tool calls from previous interrupted sessions\r\n // This prevents \"improperly formed request\" errors when continuing conversations\r\n this.cleanupOrphanedToolCalls();\r\n quickLog(`[${new Date().toISOString()}] [loadChat] Cleaned up conversation history after load\\n`);\r\n\r\n return true;\r\n }\r\n\r\n /**\r\n * Handle chat picker selection\r\n * @param chatId The ID of the chat to load\r\n * @param skipLoadedMessage If true, skip showing the \"Loaded chat\" message (used during revert to avoid React setState race condition)\r\n * @param revertSuccessMessage If provided, this message will be appended to restoredMessages before calling onRestoreMessagesCallback (used during revert to include success message in the same React setState call, avoiding race conditions)\r\n * @param preserveRemoteSession If true, keep current SSH/WSL/Docker connection and skip reconnect logic (used during revert in a live remote session)\r\n */\r\n async handleChatPickerSelection(\r\n chatId: string,\r\n skipLoadedMessage: boolean = false,\r\n revertSuccessMessage?: string,\r\n preserveRemoteSession: boolean = false\r\n ): Promise<void> {\r\n const chat = localChatStorage.loadChat(chatId);\r\n\r\n if (!chat) {\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback('❌ Failed to load the selected chat.');\r\n }\r\n return;\r\n }\r\n\r\n // IMPORTANT: Clean up current remote session before loading new chat\r\n // This ensures that switching from a remote chat to a local chat properly resets the state\r\n const currentContext = this.contextManager.getCurrentContext();\r\n const shouldPreserveRemote = preserveRemoteSession && currentContext.type !== 'local';\r\n if (!shouldPreserveRemote && currentContext.type !== 'local') {\r\n // Disconnect from current remote session\r\n if (currentContext.handler) {\r\n try {\r\n await currentContext.handler.disconnect();\r\n } catch (error) {\r\n // Ignore disconnect errors - we're switching chats anyway\r\n }\r\n }\r\n\r\n // Pop context to return to local\r\n this.contextManager.popContext();\r\n\r\n // Clear remote context tracking\r\n this.cwdStack = [];\r\n this.connectionCommandStack = [];\r\n }\r\n\r\n // Load AI context\r\n this.loadChat(chatId, shouldPreserveRemote ? { preserveCwd: true } : undefined);\r\n\r\n // If we're preserving a live remote session (revert case), restore the current remote CWD\r\n if (shouldPreserveRemote) {\r\n this.cwd = currentContext.metadata.workingDirectory;\r\n this.contextManager.updateWorkingDirectory(this.cwd);\r\n if (this.onCwdChange) {\r\n this.onCwdChange(this.cwd);\r\n }\r\n }\r\n\r\n // Restore UI messages if available\r\n quickLog(`[${new Date().toISOString()}] [handleChatPickerSelection] chat.uiMessages exists: ${!!chat.uiMessages}, length: ${chat.uiMessages?.length ?? 0}\\n`);\r\n quickLog(`[${new Date().toISOString()}] [handleChatPickerSelection] onRestoreMessagesCallback exists: ${!!this.onRestoreMessagesCallback}\\n`);\r\n if (this.onRestoreMessagesCallback) {\r\n const sourceUiMessages = chat.uiMessages ?? [];\r\n // Convert StoredUIMessage back to Message format\r\n const restoredMessages: Message[] = sourceUiMessages.map(msg => ({\r\n id: msg.id,\r\n role: msg.role,\r\n content: msg.content,\r\n timestamp: msg.timestamp ? new Date(msg.timestamp) : undefined,\r\n toolExecution: msg.toolExecution,\r\n shouldStream: false, // Don't stream restored messages\r\n isCommandMode: msg.isCommandMode,\r\n tool_call_id: msg.tool_call_id,\r\n tool_calls: msg.tool_calls,\r\n thinkingDuration: msg.thinkingDuration,\r\n taskCompletion: msg.taskCompletion,\r\n planAccepted: msg.planAccepted,\r\n connectionStatus: msg.connectionStatus, // For SSH/WSL/Docker connection status boxes\r\n }));\r\n\r\n // If a revert success message was provided, append it to the restored messages\r\n // This ensures the message is part of the same React setState call, avoiding race conditions\r\n quickLog(`[${new Date().toISOString()}] [handleChatPickerSelection] revertSuccessMessage provided: ${!!revertSuccessMessage}\\n`);\r\n if (revertSuccessMessage) {\r\n const revertSystemMessage: Message = {\r\n id: `revert-success-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`,\r\n role: 'assistant',\r\n content: revertSuccessMessage,\r\n timestamp: new Date(),\r\n shouldStream: false,\r\n };\r\n restoredMessages.push(revertSystemMessage);\r\n quickLog(`[${new Date().toISOString()}] [handleChatPickerSelection] Appended revert success message, restoredMessages.length: ${restoredMessages.length}\\n`);\r\n }\r\n\r\n // Determine if this is a remote chat that needs reconnection\r\n const pendingContextStack = shouldPreserveRemote\r\n ? null\r\n : (chat.remoteContextStack ?? (chat.remoteContext ? [chat.remoteContext] : null));\r\n const hasRemoteContext = pendingContextStack && pendingContextStack.length > 0;\r\n\r\n // IMPORTANT: Append the \"Loaded chat\" or \"Reconnecting\" status message to restoredMessages\r\n // BEFORE calling onRestoreMessagesCallback. This prevents the message from being added\r\n // separately via onDirectMessageCallback between the two-phase restore (Phase 1: clear,\r\n // Phase 2: 50ms delayed restore), which would cause Ink's Static component to increment\r\n // its rendered count and skip the first restored message.\r\n if (!skipLoadedMessage && !revertSuccessMessage) {\r\n if (hasRemoteContext) {\r\n // For remote chats, include the reconnection notification\r\n const nestingInfo = pendingContextStack!.length > 1\r\n ? ` (${pendingContextStack!.length} levels: ${pendingContextStack!.map((c: any) => c.type).join(' > ')})`\r\n : '';\r\n const reconnectMessage: Message = {\r\n id: `reconnect-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`,\r\n role: 'assistant',\r\n content: `🔄 Reconnecting to session${nestingInfo}...`,\r\n timestamp: new Date(),\r\n shouldStream: false,\r\n };\r\n restoredMessages.push(reconnectMessage);\r\n quickLog(`[${new Date().toISOString()}] [handleChatPickerSelection] Appended reconnect message to restoredMessages\\n`);\r\n } else {\r\n // For local chats, include the \"Loaded chat\" confirmation\r\n const loadedMessage: Message = {\r\n id: `loaded-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`,\r\n role: 'assistant',\r\n content: `✅ Loaded chat: \"${chat.title}\"\\n\\nYou have ${chat.messageCount} messages in AI context. Continue your conversation!`,\r\n timestamp: new Date(),\r\n shouldStream: false,\r\n };\r\n restoredMessages.push(loadedMessage);\r\n quickLog(`[${new Date().toISOString()}] [handleChatPickerSelection] Appended loaded chat message to restoredMessages\\n`);\r\n }\r\n }\r\n\r\n quickLog(`[${new Date().toISOString()}] [handleChatPickerSelection] Calling onRestoreMessagesCallback with ${restoredMessages.length} messages\\n`);\r\n this.onRestoreMessagesCallback(restoredMessages);\r\n quickLog(`[${new Date().toISOString()}] [handleChatPickerSelection] onRestoreMessagesCallback completed\\n`);\r\n }\r\n\r\n // Attempt to restore remote context if chat was saved while in remote environment\r\n // Support both remoteContextStack (for nested sessions) and single remoteContext (backward compat)\r\n const contextStackToRestore = shouldPreserveRemote\r\n ? null\r\n : (chat.remoteContextStack ?? (chat.remoteContext ? [chat.remoteContext] : null));\r\n\r\n if (contextStackToRestore && contextStackToRestore.length > 0) {\r\n // Get the base local CWD from the first context's localCwdBeforeRemote\r\n const baseLocalCwd = contextStackToRestore[0].localCwdBeforeRemote;\r\n\r\n // Initialize stacks\r\n this.cwdStack = [baseLocalCwd];\r\n this.connectionCommandStack = [];\r\n\r\n // Note: The initial \"Reconnecting to session...\" notification is now included\r\n // in the restoredMessages array (if uiMessages were restored) to avoid Ink's Static\r\n // component count desync. Only show via onDirectMessageCallback if no UI restore happened.\r\n const nestingInfo = contextStackToRestore.length > 1\r\n ? ` (${contextStackToRestore.length} levels: ${contextStackToRestore.map(c => c.type).join(' > ')})`\r\n : '';\r\n if (this.onDirectMessageCallback && !this.onRestoreMessagesCallback) {\r\n this.onDirectMessageCallback(`🔄 Reconnecting to session${nestingInfo}...`);\r\n }\r\n\r\n this.isReconnecting = true;\r\n try {\r\n // Sequential reconnection through the stack\r\n let previousCwd = baseLocalCwd;\r\n for (let i = 0; i < contextStackToRestore.length; i++) {\r\n const remoteCtx = contextStackToRestore[i];\r\n const { type, connectionCommand, remoteCwd } = remoteCtx;\r\n const levelInfo = contextStackToRestore.length > 1 ? ` [${i + 1}/${contextStackToRestore.length}]` : '';\r\n\r\n // Show connecting status for this level\r\n // Only show via onDirectMessageCallback if no UI restore happened (same reason as reconnecting message above)\r\n if (this.onDirectMessageCallback && !this.onRestoreMessagesCallback) {\r\n this.onDirectMessageCallback(`🔄${levelInfo} Connecting to ${type.toUpperCase()}...`);\r\n }\r\n\r\n if (this.onConnectionStatusUpdate) {\r\n this.onConnectionStatusUpdate({\r\n type: type,\r\n status: 'connecting',\r\n connectionString: this.buildConnectionString(type, remoteCtx.metadata)\r\n });\r\n }\r\n\r\n try {\r\n // Detect and connect using the saved command\r\n const detection = this.commandDetector.detect(connectionCommand);\r\n if (!detection) {\r\n throw new Error(`Could not detect handler for: ${connectionCommand}`);\r\n }\r\n\r\n // Create a new instance of the handler to ensure each level of the connection\r\n // has its own state (client, stream, etc.), critical for nested sessions of the same type\r\n const handler = detection.handler.createNew();\r\n\r\n let context: SubshellContext;\r\n\r\n // Check if this is a nested connection (i > 0 means we're inside a remote session)\r\n if (i > 0) {\r\n const currentContext = this.contextManager.getCurrentContext();\r\n if (handler.connectFromRemote) {\r\n context = await handler.connectFromRemote(connectionCommand, previousCwd, currentContext);\r\n } else {\r\n throw new Error(`Nested connections not supported by ${type} handler`);\r\n }\r\n } else {\r\n // First level: connect from local\r\n context = await handler.connect(connectionCommand, previousCwd);\r\n }\r\n\r\n // Push to stacks\r\n this.connectionCommandStack.push(connectionCommand);\r\n if (i > 0) {\r\n this.cwdStack.push(previousCwd); // Save the previous remote CWD for nested exits\r\n }\r\n this.contextManager.pushContext(context);\r\n\r\n // Navigate to saved remote CWD\r\n if (remoteCwd && remoteCwd !== context.metadata.workingDirectory) {\r\n try {\r\n await this.contextManager.executeCommand(`cd \"${remoteCwd}\"`);\r\n } catch (cdError) {\r\n // Failed to cd to saved path - warn but continue\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(`⚠️ Could not restore ${type} directory: ${remoteCwd}`);\r\n }\r\n }\r\n }\r\n\r\n // Update previousCwd for next iteration\r\n previousCwd = this.contextManager.getCurrentContext().metadata.workingDirectory;\r\n\r\n // Show success for this level\r\n if (this.onConnectionStatusUpdate) {\r\n this.onConnectionStatusUpdate({\r\n type: type,\r\n status: 'connected',\r\n connectionString: this.buildConnectionString(type, remoteCtx.metadata)\r\n });\r\n }\r\n\r\n } catch (error: any) {\r\n // Connection failed at this level - fall back to local mode\r\n // If we partially connected, disconnect all and reset\r\n while (this.contextManager.getCurrentContext().type !== 'local') {\r\n const ctx = this.contextManager.getCurrentContext();\r\n if (ctx.handler) {\r\n try { await ctx.handler.disconnect(); } catch (e) { /* ignore */ }\r\n }\r\n this.contextManager.popContext();\r\n }\r\n\r\n this.cwdStack = [];\r\n this.connectionCommandStack = [];\r\n\r\n if (this.onConnectionStatusUpdate) {\r\n this.onConnectionStatusUpdate({\r\n type: type,\r\n status: 'error',\r\n connectionString: this.buildConnectionString(type, remoteCtx.metadata),\r\n error: error.message\r\n });\r\n }\r\n\r\n const failedAt = contextStackToRestore.length > 1 ? ` at level ${i + 1} (${type})` : '';\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(`⚠️ Loaded chat: \"${chat.title}\"\\n\\n❌ Could not reconnect${failedAt}: ${error.message}\\n\\n📁 Restored to local directory: ${baseLocalCwd}`);\r\n }\r\n return;\r\n }\r\n }\r\n\r\n // All levels connected successfully\r\n if (this.onDirectMessageCallback) {\r\n const successInfo = contextStackToRestore.length > 1\r\n ? `🔗 Reconnected through ${contextStackToRestore.length} levels`\r\n : '';\r\n if (successInfo) {\r\n this.onDirectMessageCallback(successInfo);\r\n }\r\n }\r\n return;\r\n } finally {\r\n this.isReconnecting = false;\r\n void this.maybeProcessSessionCommandQueue();\r\n }\r\n }\r\n\r\n // No remote context - show regular confirmation and restore CWD\r\n if (!shouldPreserveRemote && chat.cwd && !chat.remoteContext) {\r\n if (fs.existsSync(chat.cwd)) {\r\n this.cwd = chat.cwd;\r\n this.contextManager.updateWorkingDirectory(chat.cwd);\r\n if (this.onCwdChange) {\r\n this.onCwdChange(chat.cwd);\r\n }\r\n }\r\n }\r\n\r\n // Skip loaded message if called from revert (to avoid React setState race condition)\r\n // Also skip if we already included it in the restoredMessages array (when uiMessages were restored)\r\n const uiMessagesWereRestored = !!this.onRestoreMessagesCallback;\r\n if (this.onDirectMessageCallback && !skipLoadedMessage && !uiMessagesWereRestored) {\r\n const responseMessage = `✅ Loaded chat: \"${chat.title}\"\\n\\nYou have ${chat.messageCount} messages in AI context. Continue your conversation!`;\r\n this.onDirectMessageCallback(responseMessage);\r\n }\r\n }\r\n\r\n /**\r\n * Build a connection string for display based on remote context metadata\r\n */\r\n private buildConnectionString(type: 'ssh' | 'wsl' | 'docker', metadata?: StoredRemoteContext['metadata']): string {\r\n if (!metadata) return type;\r\n\r\n if (type === 'ssh' && metadata.hostname) {\r\n return `${metadata.username || 'user'}@${metadata.hostname}`;\r\n } else if (type === 'wsl' && metadata.distroName) {\r\n return metadata.distroName;\r\n } else if (type === 'docker' && metadata.containerId) {\r\n return metadata.containerId.substring(0, 12);\r\n }\r\n return type;\r\n }\r\n\r\n /**\r\n * Get the current chat ID\r\n */\r\n getCurrentChatId(): string | null {\r\n return this.currentChatId;\r\n }\r\n\r\n /**\r\n * Start a new chat session\r\n */\r\n startNewChat(): void {\r\n const currentContext = this.contextManager.getCurrentContext();\r\n this.conversationHistory = [];\r\n this.currentChatId = null;\r\n this.conversationStarted = false;\r\n this.uiMessageHistory = [];\r\n this.currentCheckpointId = undefined;\r\n\r\n if (this.checkpointManager) {\r\n this.checkpointManager.setCurrentChatId(null);\r\n }\r\n\r\n // Only clear stacks if we're in local context\r\n // If we're in a remote context (SSH/WSL/Docker), we want to PRESERVE the connection state\r\n // so the new chat continues in the same environment\r\n if (currentContext.type === 'local') {\r\n this.cwdStack = [];\r\n this.connectionCommandStack = [];\r\n }\r\n // Else: Preserve cwdStack and connectionCommandStack for remote sessions\r\n\r\n // Reset context limit state\r\n this.setContextLimitReachedState(false);\r\n\r\n // Update token count to reflect empty conversation\r\n this.updateTokenCount().catch(err => {\r\n quickLog(`[${new Date().toISOString()}] [startNewChat] Failed to update token count: ${err}\\n`);\r\n });\r\n }\r\n\r\n /**\r\n * Update UI message history (called from App.tsx via callback)\r\n * Also triggers auto-save to ensure the latest messages are persisted\r\n * \r\n * Note: Only saves when there's real user/AI content, not just slash command system messages.\r\n * This prevents empty chats from being created when user only runs /chat commands.\r\n */\r\n updateUIMessageHistory(messages: Message[]): void {\r\n this.uiMessageHistory = messages;\r\n\r\n // Auto-save when UI history changes, but only if there's real content\r\n // Real content is defined as: user or assistant messages (not just system slash command messages)\r\n // This ensures:\r\n // - /chat rename, /chat new, etc. don't create new chats on their own\r\n // - System messages from slash commands aren't saved permanently\r\n // - Only actual AI conversation content triggers saves\r\n\r\n const hasRealContent = this.conversationHistory.length > 0;\r\n\r\n // Only save if there's actual AI conversation history\r\n // The conversationHistory only contains real user/assistant/tool messages from AI interactions\r\n if (messages.length > 0 && hasRealContent) {\r\n this.saveCurrentChat();\r\n }\r\n }\r\n\r\n /**\r\n * Set callback for restoring UI messages\r\n */\r\n setOnRestoreMessagesCallback(callback: (messages: Message[]) => void): void {\r\n this.onRestoreMessagesCallback = callback;\r\n }\r\n\r\n /**\r\n * Set callback for setting input value (e.g., for revert)\r\n */\r\n setOnSetInput(callback: (value: string) => void): void {\r\n this.onSetInputCallback = callback;\r\n }\r\n\r\n\r\n\r\n /**\r\n * Get environment context for backend\r\n * Returns structured environment information to be sent to backend\r\n */\r\n private getEnvironmentContext(): { os: 'windows' | 'macos' | 'linux'; platform: string; shell: string; cwd: string; homeDir: string } {\r\n const platform = process.platform;\r\n const isWindows = platform === 'win32';\r\n const homeDir = process.env.HOME || process.env.USERPROFILE || '~';\r\n const shell = process.env.SHELL || process.env.ComSpec || (isWindows ? 'cmd' : 'bash');\r\n\r\n return {\r\n os: isWindows ? 'windows' : platform === 'darwin' ? 'macos' : 'linux',\r\n platform,\r\n shell,\r\n cwd: this.cwd,\r\n homeDir,\r\n };\r\n }\r\n\r\n /**\r\n * Get current mode\r\n * Returns the current mode (default, plan, or command)\r\n */\r\n private getMode(): 'default' | 'plan' | 'command' {\r\n if (this.commandMode) return 'command';\r\n if (this.planMode) return 'plan';\r\n return 'default';\r\n }\r\n\r\n private getPlanModeInstructions(): string {\r\n return `\\n\\n## PLAN MODE ACTIVE (MODE=plan)\r\n\r\nYou are currently in PLAN MODE. In this mode, you MUST:\r\n\r\n1. **DO NOT execute any implementation tools** (no write_to_file, edit_file, execute_command, etc.)\r\n2. **Call the \\`create_plan\\` tool** to present a structured plan to the user FIRST\r\n\r\n### How to Create a Plan:\r\n\r\nAnalyze the user's request, then call \\`create_plan\\` with:\r\n- A clear title describing what will be accomplished\r\n- A brief summary of the approach\r\n- An ordered list of specific, actionable tasks\r\n\r\nExample:\r\n\\`\\`\\`\r\ncreate_plan(\r\n title: \"Create Python CSV Filter Script\",\r\n summary: \"Build a Python script that reads a CSV file, filters rows based on criteria, and writes output\",\r\n tasks: [\r\n { description: \"Create sample input.csv with test data\", complexity: \"low\" },\r\n { description: \"Write csv_filter.py with read/filter/write logic\", complexity: \"medium\" },\r\n { description: \"Execute and verify the script works correctly\", complexity: \"low\" }\r\n ]\r\n)\r\n\\`\\`\\`\r\n\r\n### After Plan Approval:\r\n\r\nOnce the user approves the plan:\r\n1. Execute each task in order\r\n2. After completing each task, call \\`mark_task_complete(task_number: N)\\`\r\n3. After all tasks are done, call \\`task_complete\\` with a summary\r\n\r\n**CRITICAL: In plan mode, ALWAYS call create_plan FIRST before any other tools.**`;\r\n }\r\n\r\n /**\r\n * Toggle command mode on/off\r\n */\r\n toggleCommandMode(): void {\r\n if (this.commandMode) {\r\n // Exiting command mode - restore previous mode\r\n this.commandMode = false;\r\n this.planMode = this.previousMode === 'planning';\r\n\r\n // Notify UI\r\n if (this.onCommandModeChange) {\r\n this.onCommandModeChange(false);\r\n }\r\n if (this.onPlanModeChange) {\r\n this.onPlanModeChange(this.planMode);\r\n }\r\n } else {\r\n // Entering command mode - save current mode\r\n this.previousMode = this.planMode ? 'planning' : 'execution';\r\n this.commandMode = true;\r\n this.planMode = false;\r\n\r\n // Notify UI\r\n if (this.onCommandModeChange) {\r\n this.onCommandModeChange(true);\r\n }\r\n if (this.onPlanModeChange) {\r\n this.onPlanModeChange(false);\r\n }\r\n }\r\n }\r\n\r\n\r\n\r\n /**\r\n * Set the current interactive process\r\n * This is called by the execute_command tool when starting an interactive command\r\n */\r\n setCurrentInteractiveProcess(process: shellUtils.InteractiveProcess | undefined): void {\r\n this.currentInteractiveProcess = process;\r\n }\r\n\r\n /**\r\n * Handle command execution in command mode\r\n */\r\n private async handleCommandModeExecution(command: string): Promise<void> {\r\n if (!command.trim()) {\r\n return;\r\n }\r\n\r\n // Check if this is an interactive editor command (vim, nano, etc.)\r\n // These need full terminal control\r\n const currentContextForEditor = this.contextManager.getCurrentContext();\r\n\r\n if (isInteractiveEditorCommand(command)) {\r\n if (this.onInteractiveEditorMode) {\r\n if (currentContextForEditor.type === 'local') {\r\n // Local context: delegate to App for full terminal takeover\r\n this.onInteractiveEditorMode(true, command, this.cwd);\r\n } else {\r\n // Remote context (SSH, WSL, Docker) - pass remote context to App\r\n // Use the remote context's working directory, not the local Windows CWD\r\n const remoteCwd = currentContextForEditor.metadata?.workingDirectory || '~';\r\n const parentContext = this.contextManager.getParentContext() || undefined; // Convert null to undefined\r\n this.onInteractiveEditorMode(true, command, remoteCwd, currentContextForEditor, parentContext);\r\n }\r\n return;\r\n }\r\n }\r\n\r\n try {\r\n // Check for exit command in subshell\r\n if (command.trim() === 'exit') {\r\n const currentContext = this.contextManager.getCurrentContext();\r\n if (currentContext.type !== 'local') {\r\n // Disconnect from subshell\r\n if (currentContext.handler) {\r\n await currentContext.handler.disconnect();\r\n }\r\n\r\n // Pop context - this returns us to the parent context\r\n this.contextManager.popContext();\r\n\r\n // Check the NEW current context after popping\r\n const newContext = this.contextManager.getCurrentContext();\r\n\r\n // Pop the previous CWD from stack - this handles any nesting level\r\n const previousCwd = this.cwdStack.pop();\r\n if (previousCwd) {\r\n this.cwd = previousCwd;\r\n this.contextManager.updateWorkingDirectory(previousCwd);\r\n if (this.onCwdChange) {\r\n this.onCwdChange(previousCwd);\r\n }\r\n } else if (newContext.type !== 'local') {\r\n // Fallback: use parent context's CWD if no stack entry\r\n const parentCwd = newContext.metadata?.workingDirectory || '~';\r\n this.cwd = parentCwd;\r\n if (this.onCwdChange) {\r\n this.onCwdChange(parentCwd);\r\n }\r\n }\r\n\r\n if (newContext.type === 'local') {\r\n // Clear tracking when back to local\r\n this.connectionCommandStack.pop();\r\n }\r\n\r\n // Save chat - include remote context info if still in remote\r\n this.saveCurrentChat();\r\n\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback('✅ Exited subshell');\r\n }\r\n return;\r\n }\r\n }\r\n\r\n // WARPIFY MODE: SSH/WSL/Docker commands now run as NORMAL PTY commands\r\n // The old flow intercepted these commands and used the ssh2/etc libraries directly.\r\n // The new flow lets them run in focus mode, user enters password in terminal,\r\n // then presses Alt+E to \"warpify\" the session for AI context awareness.\r\n // The detection code is preserved below (commented) for reference.\r\n /*\r\n // Detect subshell commands (OLD FLOW - DISABLED FOR WARPIFY)\r\n const detection = this.commandDetector.detect(command);\r\n if (detection) {\r\n // ... old interception logic ...\r\n // This triggered password prompt BEFORE running the command\r\n }\r\n */\r\n\r\n\r\n // Special handling for cd command - change the actual working directory\r\n // Check for chained commands: cd path && cmd or cd path ; cmd\r\n const chainedCdMatch = command.match(/^cd\\s+([^;&]+?)\\s*(&&|;)\\s*(.+)$/);\r\n const simpleCdMatch = command.match(/^cd\\s+(.+)$/);\r\n\r\n // Determine if this starts with cd\r\n const cdMatch = chainedCdMatch || simpleCdMatch;\r\n if (cdMatch) {\r\n const currentContext = this.contextManager.getCurrentContext();\r\n const targetDir = (chainedCdMatch ? chainedCdMatch[1] : cdMatch[1]).trim();\r\n const hasChainedCommand = !!chainedCdMatch;\r\n const chainedCommand = chainedCdMatch ? chainedCdMatch[3] : null;\r\n\r\n if (currentContext.type === 'local') {\r\n // Local cd handling\r\n const newCwd = path.resolve(this.cwd, targetDir);\r\n\r\n if (!fs.existsSync(newCwd)) {\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback(`❌ Directory not found: ${targetDir}`);\r\n }\r\n return;\r\n }\r\n\r\n if (!fs.statSync(newCwd).isDirectory()) {\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback(`❌ Not a directory: ${targetDir}`);\r\n }\r\n return;\r\n }\r\n\r\n this.cwd = newCwd;\r\n this.contextManager.updateWorkingDirectory(newCwd);\r\n\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback(`Changed directory to: ${newCwd}`);\r\n }\r\n\r\n // If there's a chained command, execute it in the new directory\r\n if (hasChainedCommand && chainedCommand) {\r\n // Recursively handle the next command\r\n await this.handleCommandModeExecution(chainedCommand);\r\n }\r\n return;\r\n } else {\r\n // Subshell cd handling - execute just the cd command via handler\r\n const cdOnlyCommand = `cd ${targetDir}`;\r\n const result = await this.contextManager.executeCommand(cdOnlyCommand);\r\n\r\n if (result.exitCode === 0) {\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback(`Changed directory to: ${currentContext.metadata.workingDirectory}`);\r\n }\r\n\r\n // If there's a chained command, execute it in the new directory\r\n if (hasChainedCommand && chainedCommand) {\r\n // Recursively handle the next command\r\n await this.handleCommandModeExecution(chainedCommand);\r\n }\r\n } else {\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback(`❌ ${result.stderr || 'Failed to change directory'}`);\r\n }\r\n }\r\n return;\r\n }\r\n }\r\n\r\n // Get current context to determine correct CWD for notification\r\n const currentContext = this.contextManager.getCurrentContext();\r\n const effectiveCwd = currentContext.type !== 'local'\r\n ? currentContext.metadata?.workingDirectory || '~'\r\n : this.cwd;\r\n\r\n // Build remote context prefix for SSH/Docker/WSL (for path display in UI)\r\n let remoteContext: string | undefined;\r\n if (currentContext.type !== 'local') {\r\n const metadata = currentContext.metadata;\r\n if (currentContext.type === 'ssh' && metadata) {\r\n remoteContext = `${metadata.username || 'user'}@${metadata.hostname || 'remote'}`;\r\n } else if (currentContext.type === 'wsl' && metadata) {\r\n remoteContext = `wsl:${metadata.distroName || 'wsl'}`;\r\n } else if (currentContext.type === 'docker' && metadata) {\r\n remoteContext = `docker:${metadata.containerId?.substring(0, 12) || 'container'}`;\r\n }\r\n }\r\n\r\n // Notify UI that command is executing\r\n if (this.onToolExecutionUpdate) {\r\n this.onToolExecutionUpdate({\r\n toolName: 'execute_command',\r\n status: 'executing',\r\n arguments: { command, cwd: effectiveCwd, isPty: shellUtils.isPtyAvailable(), remoteContext, commandModeExecution: true }\r\n });\r\n }\r\n\r\n // Execute with streaming support for local commands\r\n if (currentContext.type === 'local') {\r\n // Use interactive execution to support stdin\r\n let output = '';\r\n\r\n await new Promise<void>((resolve) => {\r\n this.currentInteractiveProcess = shellUtils.executeCommandInteractive(\r\n command,\r\n this.cwd,\r\n (data: string) => {\r\n // PTY-style: single merged stream\r\n output += data;\r\n if (this.onToolStreamingOutput) {\r\n this.onToolStreamingOutput({ toolName: 'execute_command', chunk: data, type: 'stdout' });\r\n }\r\n },\r\n (exitCode: number) => {\r\n // Notify UI of completion\r\n if (this.onToolExecutionUpdate) {\r\n if (exitCode !== 0) {\r\n this.onToolExecutionUpdate({\r\n toolName: 'execute_command',\r\n status: 'error',\r\n result: output,\r\n error: `Exit Code: ${exitCode}`,\r\n arguments: { command, cwd: this.cwd, remoteContext, commandModeExecution: true }\r\n });\r\n } else {\r\n this.onToolExecutionUpdate({\r\n toolName: 'execute_command',\r\n status: 'completed',\r\n result: output || 'Command executed successfully',\r\n arguments: { command, cwd: this.cwd, remoteContext, commandModeExecution: true }\r\n });\r\n }\r\n }\r\n\r\n // Record shell command to AI conversation history\r\n this.recordShellCommandToHistory(command, output, this.cwd, exitCode);\r\n\r\n this.currentInteractiveProcess = undefined;\r\n resolve();\r\n }\r\n );\r\n });\r\n } else if (currentContext.type === 'wsl') {\r\n // WSL execution with PTY for proper TTY handling (sudo, etc.)\r\n const remoteCwd = currentContext.metadata?.workingDirectory || '~';\r\n const distribution = currentContext.metadata?.distroName || 'Ubuntu';\r\n\r\n let output = '';\r\n\r\n await new Promise<void>((resolve) => {\r\n const wslPty = runWSLCommand(\r\n distribution,\r\n command,\r\n remoteCwd,\r\n (data: string) => {\r\n // Stream output to UI\r\n output += data;\r\n if (this.onToolStreamingOutput) {\r\n this.onToolStreamingOutput({ toolName: 'execute_command', chunk: data, type: 'stdout' });\r\n }\r\n },\r\n (exitCode: number) => {\r\n // Notify UI of completion\r\n if (this.onToolExecutionUpdate) {\r\n if (exitCode !== 0) {\r\n this.onToolExecutionUpdate({\r\n toolName: 'execute_command',\r\n status: 'error',\r\n result: output,\r\n error: `Exit Code: ${exitCode}`,\r\n arguments: { command, cwd: remoteCwd, remoteContext, commandModeExecution: true }\r\n });\r\n } else {\r\n this.onToolExecutionUpdate({\r\n toolName: 'execute_command',\r\n status: 'completed',\r\n result: output || 'Command executed successfully',\r\n arguments: { command, cwd: remoteCwd, remoteContext, commandModeExecution: true }\r\n });\r\n }\r\n }\r\n\r\n // Record shell command to AI conversation history\r\n this.recordShellCommandToHistory(command, output, remoteCwd, exitCode);\r\n\r\n this.currentInteractiveProcess = undefined;\r\n resolve();\r\n }\r\n );\r\n\r\n // Set up interactive process for stdin\r\n this.currentInteractiveProcess = {\r\n process: null,\r\n write: (data: string) => wslPty.write(data),\r\n kill: () => wslPty.kill(),\r\n signal: (sig: NodeJS.Signals) => {\r\n if (sig === 'SIGINT') {\r\n wslPty.write('\\x03'); // Ctrl+C\r\n return;\r\n }\r\n // For termination signals (SIGTERM/SIGKILL), force-close the PTY so\r\n // command-mode queue processing is not left waiting on remote shells.\r\n if (sig === 'SIGTERM' || sig === 'SIGKILL') {\r\n wslPty.kill();\r\n }\r\n },\r\n resize: (cols: number, rows: number) => wslPty.resize(cols, rows),\r\n isPty: true\r\n };\r\n });\r\n } else if (currentContext.type === 'docker') {\r\n // Docker execution - check if nested inside SSH\r\n const parentContext = this.contextManager.getParentContext();\r\n const remoteCwd = currentContext.metadata?.workingDirectory || '~';\r\n const containerId = currentContext.metadata?.containerId || '';\r\n\r\n let output = '';\r\n\r\n if (parentContext && parentContext.type === 'ssh') {\r\n // Nested Docker inside SSH: route docker exec command through SSH\r\n const sshClient = parentContext.handler?.client;\r\n\r\n if (!sshClient) {\r\n throw new Error('SSH client not available for nested Docker session');\r\n }\r\n\r\n // Build docker exec command to run via SSH\r\n const escapedCommand = command.replace(/\"/g, '\\\\\"');\r\n const dockerCommand = `docker exec -w \"${remoteCwd}\" ${containerId} sh -c \"${escapedCommand}\"`;\r\n\r\n await new Promise<void>((resolve) => {\r\n const sshPty = runSSHCommand(\r\n sshClient,\r\n dockerCommand,\r\n parentContext.metadata.workingDirectory || '~',\r\n (data: string) => {\r\n // Stream output to UI\r\n output += data;\r\n if (this.onToolStreamingOutput) {\r\n this.onToolStreamingOutput({ toolName: 'execute_command', chunk: data, type: 'stdout' });\r\n }\r\n },\r\n (exitCode: number) => {\r\n // Notify UI of completion\r\n if (this.onToolExecutionUpdate) {\r\n if (exitCode !== 0) {\r\n this.onToolExecutionUpdate({\r\n toolName: 'execute_command',\r\n status: 'error',\r\n result: output,\r\n error: `Exit Code: ${exitCode}`,\r\n arguments: { command, cwd: remoteCwd, remoteContext, commandModeExecution: true }\r\n });\r\n } else {\r\n this.onToolExecutionUpdate({\r\n toolName: 'execute_command',\r\n status: 'completed',\r\n result: output || 'Command executed successfully',\r\n arguments: { command, cwd: remoteCwd, remoteContext, commandModeExecution: true }\r\n });\r\n }\r\n }\r\n\r\n // Record shell command to AI conversation history\r\n this.recordShellCommandToHistory(command, output, remoteCwd, exitCode);\r\n\r\n this.currentInteractiveProcess = undefined;\r\n resolve();\r\n }\r\n );\r\n\r\n // Set up interactive process for stdin\r\n this.currentInteractiveProcess = {\r\n process: null,\r\n write: (data: string) => sshPty.write(data),\r\n kill: () => sshPty.kill(),\r\n signal: (sig: NodeJS.Signals) => {\r\n if (sig === 'SIGINT') {\r\n sshPty.write('\\x03'); // Ctrl+C\r\n return;\r\n }\r\n if (sig === 'SIGTERM' || sig === 'SIGKILL') {\r\n sshPty.kill();\r\n }\r\n },\r\n resize: (cols: number, rows: number) => sshPty.resize(cols, rows),\r\n isPty: true\r\n };\r\n });\r\n } else {\r\n // Local Docker: use standard runDockerCommand\r\n await new Promise<void>((resolve) => {\r\n const dockerPty = runDockerCommand(\r\n containerId,\r\n command,\r\n remoteCwd,\r\n (data: string) => {\r\n // Stream output to UI\r\n output += data;\r\n if (this.onToolStreamingOutput) {\r\n this.onToolStreamingOutput({ toolName: 'execute_command', chunk: data, type: 'stdout' });\r\n }\r\n },\r\n (exitCode: number) => {\r\n // Notify UI of completion\r\n if (this.onToolExecutionUpdate) {\r\n if (exitCode !== 0) {\r\n this.onToolExecutionUpdate({\r\n toolName: 'execute_command',\r\n status: 'error',\r\n result: output,\r\n error: `Exit Code: ${exitCode}`,\r\n arguments: { command, cwd: remoteCwd, remoteContext, commandModeExecution: true }\r\n });\r\n } else {\r\n this.onToolExecutionUpdate({\r\n toolName: 'execute_command',\r\n status: 'completed',\r\n result: output || 'Command executed successfully',\r\n arguments: { command, cwd: remoteCwd, remoteContext, commandModeExecution: true }\r\n });\r\n }\r\n }\r\n\r\n // Record shell command to AI conversation history\r\n this.recordShellCommandToHistory(command, output, remoteCwd, exitCode);\r\n\r\n this.currentInteractiveProcess = undefined;\r\n resolve();\r\n }\r\n );\r\n\r\n // Set up interactive process for stdin\r\n this.currentInteractiveProcess = {\r\n process: null,\r\n write: (data: string) => dockerPty.write(data),\r\n kill: () => dockerPty.kill(),\r\n signal: (sig: NodeJS.Signals) => {\r\n if (sig === 'SIGINT') {\r\n dockerPty.write('\\x03'); // Ctrl+C\r\n return;\r\n }\r\n if (sig === 'SIGTERM' || sig === 'SIGKILL') {\r\n dockerPty.kill();\r\n }\r\n },\r\n resize: (cols: number, rows: number) => dockerPty.resize(cols, rows),\r\n isPty: true\r\n };\r\n });\r\n }\r\n } else if (currentContext.type === 'ssh') {\r\n // SSH execution with PTY for proper TTY handling\r\n const remoteCwd = currentContext.metadata?.workingDirectory || '~';\r\n const sshClient = currentContext.handler?.client;\r\n\r\n if (!sshClient) {\r\n throw new Error('SSH client not available');\r\n }\r\n\r\n let output = '';\r\n\r\n await new Promise<void>((resolve) => {\r\n const sshPty = runSSHCommand(\r\n sshClient,\r\n command,\r\n remoteCwd,\r\n (data: string) => {\r\n // Stream output to UI\r\n output += data;\r\n if (this.onToolStreamingOutput) {\r\n this.onToolStreamingOutput({ toolName: 'execute_command', chunk: data, type: 'stdout' });\r\n }\r\n },\r\n (exitCode: number) => {\r\n // Notify UI of completion\r\n if (this.onToolExecutionUpdate) {\r\n if (exitCode !== 0) {\r\n this.onToolExecutionUpdate({\r\n toolName: 'execute_command',\r\n status: 'error',\r\n result: output,\r\n error: `Exit Code: ${exitCode}`,\r\n arguments: { command, cwd: remoteCwd, remoteContext, commandModeExecution: true }\r\n });\r\n } else {\r\n this.onToolExecutionUpdate({\r\n toolName: 'execute_command',\r\n status: 'completed',\r\n result: output || 'Command executed successfully',\r\n arguments: { command, cwd: remoteCwd, remoteContext, commandModeExecution: true }\r\n });\r\n }\r\n }\r\n\r\n // Record shell command to AI conversation history\r\n this.recordShellCommandToHistory(command, output, remoteCwd, exitCode);\r\n\r\n this.currentInteractiveProcess = undefined;\r\n resolve();\r\n }\r\n );\r\n\r\n // Set up interactive process for stdin\r\n this.currentInteractiveProcess = {\r\n process: null,\r\n write: (data: string) => sshPty.write(data),\r\n kill: () => sshPty.kill(),\r\n signal: (sig: NodeJS.Signals) => {\r\n if (sig === 'SIGINT') {\r\n sshPty.write('\\x03'); // Ctrl+C\r\n return;\r\n }\r\n if (sig === 'SIGTERM' || sig === 'SIGKILL') {\r\n sshPty.kill();\r\n }\r\n },\r\n resize: (cols: number, rows: number) => sshPty.resize(cols, rows),\r\n isPty: true\r\n };\r\n });\r\n }\r\n\r\n // Save conversation after user shell command execution\r\n // This ensures shell commands are saved to chat history like AI messages\r\n this.saveCurrentChat();\r\n\r\n } catch (error: any) {\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback(`❌ Error: ${error.message}`);\r\n }\r\n }\r\n }\r\n}\r\n"],"mappings":"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","names":["message","result","compactionOutcome","silentStopPrompt","currentContext","cmd","process"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli-adapter.ts"],"sourcesContent":["import * as fs from 'fs';\r\nimport * as path from 'path';\r\nimport { fileURLToPath } from 'url';\r\nimport { dirname } from 'path';\r\nimport * as shellUtils from './utils/shell.js';\r\nimport { sanitizeForContext } from './utils/context-sanitizer.js';\r\n\r\nconst __filename = fileURLToPath(import.meta.url);\r\nconst __dirname = dirname(__filename);\r\nimport { ConfigManager } from './config/manager.js';\r\nimport { ToolRegistry } from './tools/registry.js';\r\nimport { viewFileTool, writeToFileTool, editFileTool, listDirTool, multiEditFileTool } from './tools/file-ops.js';\r\nimport { runCommandTool } from './tools/command.js';\r\nimport { grepSearchTool } from './tools/grep-search.js';\r\nimport { findFilesTool } from './tools/find-files.js';\r\nimport { getDiffTool } from './tools/get-diff.js';\r\nimport { inspectSymbolTool } from './tools/inspect-symbol.js';\r\nimport { createPlanTool, markTaskCompleteTool, getCurrentPlan, clearPlan, approvePlan, getPlanContextForPrompt, getPhaseContextForPrompt, getCurrentPhase, advanceToNextPhase, areAllTasksComplete, Plan, PlanStep } from './tools/plan-mode.js';\r\nimport { webSearchTool, fetchUrlTool } from './tools/web-search.js';\r\nimport { taskCompleteTool } from './tools/task-complete.js';\r\nimport { readBinaryFileTool } from './tools/read-binary-file.js';\r\nimport { createImageTool } from './tools/create-image.js';\r\nimport { backgroundCommandTool } from './tools/background-command.js';\r\nimport { subAgentTool } from './tools/sub-agent.js';\r\nimport { enterRemoteSessionTool } from './tools/enter-remote-session.js';\r\nimport { workflowTool } from './tools/workflow-tool.js';\r\nimport { fastContextTool } from './tools/fast-context.js';\r\nimport { addMcpTool } from './tools/add-mcp.js';\r\nimport { SubAgentManager } from './services/sub-agent-manager.js';\r\nimport { ShellInputAgent } from './services/shell-input-agent.js';\r\nimport { apiClient } from './services/api-client.js';\r\nimport { conversationManager } from './services/conversation-manager.js';\r\nimport { aiServiceClient, Message as AIMessage, StreamChunk } from './services/ai-service-client.js';\r\nimport { getSupportedModels, getModelDisplayName, isValidModel, getInvalidModelError, fetchModelsConfig, clearModelsCache, getModelConfigByIdAndName } from './config/models.js';\r\nimport { authenticateWithGoogle } from './services/auth-handler.js';\r\nimport { ContextManager } from './context/context-manager.js';\r\nimport { CommandDetector } from './context/command-detector.js';\r\nimport { SSHHandler } from './context/handlers/ssh-handler.js';\r\nimport { WSLHandler } from './context/handlers/wsl-handler.js';\r\nimport { DockerHandler } from './context/handlers/docker-handler.js';\r\nimport { SubshellContext } from './context/types.js';\r\nimport { AIContextInjector } from './services/ai-context-injector.js';\r\nimport { environmentContextInjector } from './services/environment-context-injector.js';\r\nimport { MCPConfigManager } from './config/mcp-config-manager.js';\r\nimport { MCPServerManager } from './mcp/mcp-server-manager.js';\r\nimport { MCPCommandHandler } from './mcp/mcp-command-handler.js';\r\nimport { isInteractiveEditorCommand, runWSLCommand, runDockerCommand, runSSHCommand } from './utils/editor-utils.js';\r\nimport { conversationLogger, quickLog } from './utils/conversation-logger.js';\r\nimport { localChatStorage, StoredMessage, LocalChatMeta, StoredUIMessage, StoredRemoteContext, StoredCheckpointMeta } from './services/local-chat-storage.js';\r\nimport { Message } from './types/index.js';\r\nimport { logError, logWarning } from './utils/logger.js';\r\nimport { BackgroundTaskManager, BackgroundTaskInfo } from './services/background-task-manager.js';\r\nimport { sessionQuotaManager } from './services/session-quota-manager.js';\r\nimport { ollamaService, OllamaService } from './services/ollama-service.js';\r\nimport { workflowStorage } from './services/workflow-storage.js';\r\nimport { Workflow, WorkflowStep, WorkflowExecutionState } from './types/workflow.js';\r\nimport { CheckpointManager, CheckpointMeta, RemoteFileHandler, RemoteSessionInfo } from './services/checkpoint-manager.js';\r\nimport { rulesStorage } from './services/rules-storage.js';\r\nimport { RulesEditorRequest, SaveRuleResult } from './types/rule.js';\r\nimport { resolveRuleReferences } from './utils/rule-reference-resolver.js';\r\n\r\ntype AutoCompactionCandidateKind = 'read_file_tool_result' | 'execute_command_tool_result' | 'user_shell_command_output';\r\n\r\ninterface AutoCompactionCandidate {\r\n index: number;\r\n kind: AutoCompactionCandidateKind;\r\n toolCallId?: string;\r\n}\r\n\r\ninterface AutoCompactionOutcome {\r\n triggered: boolean;\r\n historyChanged: boolean;\r\n compactedCount: number;\r\n beforeUsagePercent: number;\r\n afterUsagePercent: number;\r\n targetReached: boolean;\r\n}\r\n\r\nexport class CentaurusCLI {\r\n private configManager: ConfigManager;\r\n private toolRegistry: ToolRegistry;\r\n private conversationHistory: AIMessage[] = [];\r\n private cwd: string;\r\n private planMode: boolean = false;\r\n private pendingPlanRequest: string | null = null; // Stores original user request during planning phase\r\n private commandMode: boolean = false;\r\n private backgroundMode: boolean = false; // Background shell mode for running commands in background\r\n private shellIdCounter: number = 1;\r\n private previousMode: 'execution' | 'planning' = 'execution';\r\n private onResponseCallback?: (message: string) => void;\r\n private onDirectMessageCallback?: (message: string) => void; // For slash commands - adds directly to history\r\n private onFileChangeSummaryCallback?: (data: { filesChanged: number; insertions: number; deletions: number }) => void;\r\n private onResponseStreamCallback?: (chunk: string) => void;\r\n private onClearStreamedResponse?: () => void; // Clear streamed text when task_complete has summary\r\n private onThoughtStreamCallback?: (thought: string) => void;\r\n private onThoughtCompleteCallback?: (durationSeconds: number) => void;\r\n private onCommandModeChange?: (commandMode: boolean) => void;\r\n private onCwdChange?: (cwd: string) => void;\r\n private onModelChange?: (modelName: string, contextWindow: number) => void;\r\n private onShowPickerCallback?: (options: { message: string; choices: Array<{ label: string; value: string }>; type: 'model' }) => void;\r\n private onToolExecutionUpdate?: (update: { toolName: string; status: 'pending' | 'executing' | 'completed' | 'error'; result?: string; error?: string; arguments?: Record<string, any> }) => void;\r\n private onToolApprovalRequest?: (request: { message: string; risky: boolean; preview?: { type: 'code' | 'diff'; content: string; language?: string }; operationType?: 'write_file' | 'edit_file' | 'execute_command'; operationDetails?: Record<string, any> }) => Promise<boolean>;\r\n private onToolStreamingOutput?: (update: { toolName: string; chunk: string; type: 'stdout' | 'stderr' }) => void;\r\n private onPlanModeChange?: (planMode: boolean) => void;\r\n private onPlanApprovalRequest?: (plan: Plan) => Promise<boolean>;\r\n private onPlanCreated?: (plan: Plan) => void;\r\n private onTaskCompleted?: (task: PlanStep, taskNumber: number, totalTasks: number, completionNote?: string, taskDescription?: string) => void;\r\n private onPasswordRequest?: (message: string) => Promise<string>;\r\n private currentInteractiveProcess?: shellUtils.InteractiveProcess;\r\n private conversationStarted: boolean = false;\r\n private contextManager: ContextManager;\r\n private commandDetector: CommandDetector;\r\n private aiContextInjector: AIContextInjector;\r\n private onSubshellContextChange?: (context: SubshellContext, stack?: SubshellContext[]) => void;\r\n private currentAbortController?: AbortController;\r\n private requestIntentionallyAborted: boolean = false;\r\n private mcpCommandHandler?: MCPCommandHandler;\r\n private onInteractiveEditorMode?: (active: boolean, command?: string, cwd?: string, remoteContext?: SubshellContext, parentContext?: SubshellContext) => void;\r\n private onConnectionStatusUpdate?: (status: { type: 'ssh' | 'wsl' | 'docker'; status: 'connecting' | 'connected' | 'error' | 'disconnected'; connectionString?: string; error?: string }) => void;\r\n private currentChatId: string | null = null;\r\n private onShowChatPickerCallback?: (chats: LocalChatMeta[], currentChatId: string | null) => void;\r\n private onShowChatDeletePickerCallback?: (chats: LocalChatMeta[], currentChatId: string | null) => void;\r\n private onShowChatListCallback?: (chats: LocalChatMeta[], currentChatId: string | null) => void;\r\n private onShowChatRenamePickerCallback?: (chats: LocalChatMeta[], currentChatId: string | null) => void;\r\n private onRestoreMessagesCallback?: (messages: Message[]) => void;\r\n private uiMessageHistory: Message[] = []; // Mirror of App.tsx's messageHistory for saving\r\n private cwdStack: string[] = []; // Stack of CWDs for nested sessions (pushed when entering, popped when exiting)\r\n private connectionCommandStack: string[] = []; // Stack of commands used to connect (for nested sessions like SSH>SSH, SSH>Docker)\r\n private onBackgroundModeChange?: (backgroundMode: boolean) => void;\r\n private onBackgroundTaskCountChange?: (count: number) => void;\r\n private onSetAutoMode?: (enabled: boolean) => void;\r\n private onShowBackgroundTaskPickerCallback?: (tasks: { id: string; command: string; cwd: string; durationMs: number; isRunning: boolean; outputPreview: string }[]) => void;\r\n private onShowBackgroundTaskCancelPickerCallback?: (tasks: { id: string; command: string; cwd: string; durationMs: number; isRunning: boolean; outputPreview: string }[]) => void;\r\n private onBackgroundTaskViewCallback?: (task: { id: string; command: string; cwd: string; output: string; isRunning: boolean; onOutput: (handler: (chunk: string) => void) => void; onComplete: (handler: (exitCode: number) => void) => void }) => void;\r\n private onTokenCountUpdate?: (tokens: number) => void; // Report actual AI context token count to UI\r\n private currentTokenCount: number = 0; // Track current token count for context limit checking\r\n private tokenCountUpdateVersion: number = 0; // Guards against stale async token count updates\r\n private contextLimitReached: boolean = false; // Track if context limit has been reached\r\n private onContextLimitReached?: (reached: boolean) => void; // Notify UI about context limit state\r\n private onSessionQuotaUpdate?: (remaining: number, canSend: boolean, timeRemaining: string) => void;\r\n // MCP screen callbacks\r\n private onShowMCPAddScreen?: () => void;\r\n private onShowMCPRemoveScreen?: (servers: { name: string; command: string; args?: string[]; enabled?: boolean }[]) => void;\r\n private onShowMCPEnableScreen?: (servers: { name: string; command: string; args?: string[]; enabled?: boolean }[]) => void;\r\n private onShowMCPDisableScreen?: (servers: { name: string; command: string; args?: string[]; enabled?: boolean }[]) => void;\r\n private onShowMCPListScreen?: (servers: Array<{ name: string; enabled: boolean; status: 'connected' | 'disconnected' | 'error' | 'connecting'; tools: Array<{ name: string }> }>) => void;\r\n private onSubAgentCountChange?: (count: number) => void; // Callback for sub-agent count changes\r\n private onPromptAnswered?: (shellId: string) => void; // Callback when AI answers a shell prompt\r\n private onShowWorkflowCreatorCallback?: (initialSteps?: Array<{ type: 'command' | 'instruction'; content: string }>) => void; // Callback to show workflow creator screen with optional initial steps\r\n private onWorkflowSaveCallback?: (workflow: Workflow) => void; // Callback when workflow is saved\r\n private onShowRulesEditorCallback?: (request: RulesEditorRequest) => void; // Callback to show the rules editor\r\n private onAiAutoSuggestChange?: (enabled: boolean) => void; // Callback for AI auto-suggest setting changes\r\n private onRevertToCheckpointCallback?: (checkpointIndex: number, prompt: string) => void; // Callback for revert UI update\r\n\r\n private static readonly AUTO_COMPACTION_TRIGGER_PERCENT = 80;\r\n private static readonly AUTO_COMPACTION_TARGET_PERCENT = 40;\r\n private static readonly AUTO_COMPACTION_TOOL_NAME = 'auto_context_compaction';\r\n private static readonly READ_FILE_COMPACTION_MESSAGE = '[This tool result has been compacted to reduce context usage.]';\r\n private static readonly TERMINAL_COMPACTION_NOTICE = '[This terminal output was compacted to show the last 20 lines to reduce context usage.]';\r\n private static readonly TERMINAL_LINES_TO_KEEP = 20;\r\n private static readonly USER_SHELL_PREFIX = '[User ran shell command in ';\r\n private static readonly USER_SHELL_OUTPUT_MARKER = '\\nOutput:\\n';\r\n private static readonly COMPACTION_NOTICE_FOR_AGENT =\r\n '[SYSTEM NOTE: Older read-file and terminal outputs were compacted to reduce context usage. Continue working with the compacted history.]';\r\n\r\n // Checkpoint manager for revert functionality\r\n private checkpointManager?: CheckpointManager;\r\n private currentCheckpointId?: string; // Track current checkpoint being created\r\n\r\n // Workflow learning mode state\r\n private workflowLearningActive: boolean = false;\r\n private learnedWorkflowSteps: Array<{ type: 'command' | 'instruction'; content: string }> = [];\r\n\r\n // Callback to set input value (e.g., for revert)\r\n private onSetInputCallback: ((value: string) => void) | null = null;\r\n\r\n // Interrupt Queue state tracking\r\n private interruptQueue: string[] = [];\r\n private onInterruptQueueUpdateCallback?: (queue: string[]) => void;\r\n private onQueuedMessageDispatchedCallback?: (message: string) => void;\r\n // Session shell command queue (separate from AI interrupt queue)\r\n private sessionCommandQueue: string[] = [];\r\n private isProcessingSessionCommandQueue: boolean = false;\r\n private onCommandQueueUpdateCallback?: (queue: string[]) => void;\r\n private onQueuedCommandDispatchedCallback?: (command: string) => void;\r\n\r\n private static readonly INTERRUPT_SYSTEM_NOTE_PREFIX =\r\n '[SYSTEM NOTE: The user interrupted you with the following message. Please address it, adjust your actions, and proceed with the task.]';\r\n\r\n // Track if we are currently reconnecting to remote sessions (prevent race conditions)\r\n private isReconnecting: boolean = false;\r\n // Track session transitions (warpify/nesting) to prevent queue races across context changes.\r\n private isSessionTransitioning: boolean = false;\r\n\r\n constructor() {\r\n this.configManager = new ConfigManager();\r\n this.toolRegistry = new ToolRegistry();\r\n this.cwd = process.cwd();\r\n\r\n // Initialize Context Manager and Command Detector\r\n this.contextManager = new ContextManager(this.cwd, process.platform);\r\n this.commandDetector = new CommandDetector();\r\n this.aiContextInjector = new AIContextInjector();\r\n\r\n // Register context change callback to update cwd\r\n // Register context change callback to update cwd\r\n this.contextManager.onContextChange((context, stack) => {\r\n this.cwd = context.metadata.workingDirectory;\r\n if (this.onCwdChange) {\r\n this.onCwdChange(this.cwd);\r\n }\r\n if (this.onSubshellContextChange) {\r\n this.onSubshellContextChange(context, stack);\r\n }\r\n // A context transition can unblock queued command processing.\r\n void this.maybeProcessSessionCommandQueue();\r\n });\r\n\r\n // Initialize MCP\r\n this.initializeMCP();\r\n\r\n // Initialize ShellInputAgent with tool registry and wire shell input callback\r\n ShellInputAgent.initialize(this.toolRegistry);\r\n ShellInputAgent.setOnShellInput((shellId: string, input: string) => {\r\n this.writeToShellStdin(input);\r\n });\r\n }\r\n\r\n setOnResponseCallback(callback: (message: string) => void): void {\r\n this.onResponseCallback = callback;\r\n }\r\n\r\n setOnDirectMessageCallback(callback: (message: string) => void): void {\r\n this.onDirectMessageCallback = callback;\r\n }\r\n\r\n setOnFileChangeSummaryCallback(callback: (data: { filesChanged: number; insertions: number; deletions: number }) => void): void {\r\n this.onFileChangeSummaryCallback = callback;\r\n }\r\n\r\n setOnResponseStreamCallback(callback: (chunk: string) => void): void {\r\n this.onResponseStreamCallback = callback;\r\n }\r\n\r\n setOnClearStreamedResponse(callback: () => void): void {\r\n this.onClearStreamedResponse = callback;\r\n }\r\n\r\n setOnThoughtStreamCallback(callback: (thought: string) => void): void {\r\n this.onThoughtStreamCallback = callback;\r\n }\r\n\r\n setOnThoughtCompleteCallback(callback: (durationSeconds: number) => void): void {\r\n this.onThoughtCompleteCallback = callback;\r\n }\r\n\r\n setOnShowPickerCallback(callback: (options: { message: string; choices: Array<{ label: string; value: string }>; type: 'model' }) => void): void {\r\n this.onShowPickerCallback = callback;\r\n }\r\n\r\n setOnToolExecutionUpdate(callback: (update: { toolName: string; status: 'pending' | 'executing' | 'completed' | 'error'; result?: string; error?: string; arguments?: Record<string, any> }) => void): void {\r\n this.onToolExecutionUpdate = callback;\r\n }\r\n\r\n setOnToolApprovalRequest(callback: (request: { message: string; risky: boolean; preview?: { type: 'code' | 'diff'; content: string; language?: string; fullDiff?: string }; operationType?: 'write_file' | 'edit_file' | 'execute_command'; operationDetails?: Record<string, any> }) => Promise<boolean>): void {\r\n this.onToolApprovalRequest = callback;\r\n }\r\n\r\n setOnToolStreamingOutput(callback: (update: { toolName: string; chunk: string; type: 'stdout' | 'stderr' }) => void): void {\r\n this.onToolStreamingOutput = callback;\r\n }\r\n\r\n setOnPlanModeChange(callback: (planMode: boolean) => void): void {\r\n this.onPlanModeChange = callback;\r\n }\r\n\r\n setOnPlanApprovalRequest(callback: (plan: Plan) => Promise<boolean>): void {\r\n this.onPlanApprovalRequest = callback;\r\n }\r\n\r\n setOnPlanCreated(callback: (plan: Plan) => void): void {\r\n this.onPlanCreated = callback;\r\n }\r\n\r\n setOnTaskCompleted(callback: (task: PlanStep, taskNumber: number, totalTasks: number, completionNote?: string, taskDescription?: string) => void): void {\r\n this.onTaskCompleted = callback;\r\n }\r\n\r\n setOnCommandModeChange(callback: (commandMode: boolean) => void): void {\r\n this.onCommandModeChange = callback;\r\n }\r\n\r\n setOnCwdChange(callback: (cwd: string) => void): void {\r\n this.onCwdChange = callback;\r\n }\r\n\r\n setOnModelChange(callback: (modelName: string, contextWindow: number) => void): void {\r\n this.onModelChange = callback;\r\n }\r\n\r\n setOnSubshellContextChange(callback: (context: SubshellContext, stack?: SubshellContext[]) => void): void {\r\n this.onSubshellContextChange = callback;\r\n }\r\n\r\n setOnPasswordRequest(callback: (message: string) => Promise<string>): void {\r\n this.onPasswordRequest = callback;\r\n // Update SSH handler if already initialized\r\n if (this.sshHandler) {\r\n this.sshHandler.setPasswordRequestCallback(callback);\r\n }\r\n }\r\n\r\n setOnInteractiveEditorMode(callback: (active: boolean, command?: string, cwd?: string, remoteContext?: SubshellContext, parentContext?: SubshellContext) => void): void {\r\n this.onInteractiveEditorMode = callback;\r\n }\r\n\r\n setOnConnectionStatusUpdate(callback: (status: { type: 'ssh' | 'wsl' | 'docker'; status: 'connecting' | 'connected' | 'error'; connectionString?: string; error?: string }) => void): void {\r\n this.onConnectionStatusUpdate = callback;\r\n }\r\n\r\n setOnTokenCountUpdate(callback: (tokens: number) => void): void {\r\n this.onTokenCountUpdate = callback;\r\n }\r\n\r\n setOnContextLimitReached(callback: (reached: boolean) => void): void {\r\n this.onContextLimitReached = callback;\r\n }\r\n\r\n setOnSubAgentCountChange(callback: (count: number) => void): void {\r\n this.onSubAgentCountChange = callback;\r\n }\r\n\r\n setOnPromptAnswered(callback: (shellId: string) => void): void {\r\n this.onPromptAnswered = callback;\r\n // Wire this callback to ShellInputAgent\r\n ShellInputAgent.setOnPromptAnswered(callback);\r\n }\r\n\r\n setOnShowWorkflowCreator(callback: (initialSteps?: Array<{ type: 'command' | 'instruction'; content: string }>) => void): void {\r\n this.onShowWorkflowCreatorCallback = callback;\r\n }\r\n\r\n setOnWorkflowSave(callback: (workflow: Workflow) => void): void {\r\n this.onWorkflowSaveCallback = callback;\r\n }\r\n\r\n setOnShowRulesEditor(callback: (request: RulesEditorRequest) => void): void {\r\n this.onShowRulesEditorCallback = callback;\r\n }\r\n\r\n setOnInterruptQueueUpdate(callback: (queue: string[]) => void): void {\r\n this.onInterruptQueueUpdateCallback = callback;\r\n }\r\n\r\n setOnQueuedMessageDispatched(callback: (message: string) => void): void {\r\n this.onQueuedMessageDispatchedCallback = callback;\r\n }\r\n\r\n setOnCommandQueueUpdate(callback: (queue: string[]) => void): void {\r\n this.onCommandQueueUpdateCallback = callback;\r\n }\r\n\r\n setOnQueuedCommandDispatched(callback: (command: string) => void): void {\r\n this.onQueuedCommandDispatchedCallback = callback;\r\n }\r\n\r\n setOnAiAutoSuggestChange(callback: (enabled: boolean) => void): void {\r\n this.onAiAutoSuggestChange = callback;\r\n }\r\n\r\n setOnRevertToCheckpoint(callback: (checkpointIndex: number, prompt: string) => void): void {\r\n this.onRevertToCheckpointCallback = callback;\r\n }\r\n\r\n /**\r\n * Get checkpoints for autocomplete dropdown\r\n */\r\n getCheckpointsForAutocomplete(): Array<{ id: string; prompt: string; timestamp: Date }> {\r\n if (!this.currentChatId) return [];\r\n this.ensureCheckpointManagerForChat(this.currentChatId);\r\n if (!this.checkpointManager) return [];\r\n return this.checkpointManager.list().map(cp => ({\r\n id: cp.id,\r\n prompt: this.normalizePromptForInputBar(cp.prompt),\r\n timestamp: new Date(cp.createdAt)\r\n }));\r\n }\r\n\r\n private ensureCheckpointManagerForChat(chatId: string, fallbackCheckpoints?: StoredCheckpointMeta[]): void {\r\n if (!this.checkpointManager) {\r\n this.checkpointManager = new CheckpointManager();\r\n }\r\n\r\n this.checkpointManager.setCurrentChatId(chatId);\r\n\r\n if (fallbackCheckpoints && fallbackCheckpoints.length > 0) {\r\n this.checkpointManager.hydrateFromChatHistory(fallbackCheckpoints as unknown as CheckpointMeta[]);\r\n }\r\n }\r\n\r\n private toStoredCheckpointMeta(checkpoint: CheckpointMeta): StoredCheckpointMeta {\r\n return {\r\n id: checkpoint.id,\r\n prompt: checkpoint.prompt,\r\n createdAt: checkpoint.createdAt,\r\n createdAtMs: checkpoint.createdAtMs,\r\n cwd: checkpoint.cwd,\r\n contextType: checkpoint.contextType,\r\n remoteSessionInfo: checkpoint.remoteSessionInfo,\r\n conversationIndex: checkpoint.conversationIndex,\r\n uiMessageIndex: checkpoint.uiMessageIndex,\r\n uiMessageId: checkpoint.uiMessageId,\r\n snapshotDir: checkpoint.snapshotDir,\r\n manifestPath: checkpoint.manifestPath,\r\n changes: checkpoint.changes,\r\n commands: [...checkpoint.commands],\r\n toolCalls: checkpoint.toolCalls.map(toolCall => ({\r\n id: toolCall.id,\r\n name: toolCall.name,\r\n arguments: toolCall.arguments,\r\n timestamp: toolCall.timestamp,\r\n })),\r\n status: checkpoint.status,\r\n };\r\n }\r\n\r\n\r\n /**\r\n * Save a workflow from the workflow creator UI\r\n */\r\n saveWorkflow(name: string, steps: Array<{ type: 'command' | 'instruction'; content: string }>, description?: string): void {\r\n const workflow = workflowStorage.createWorkflow(name, steps, description);\r\n workflowStorage.save(workflow);\r\n if (this.onWorkflowSaveCallback) {\r\n this.onWorkflowSaveCallback(workflow);\r\n }\r\n }\r\n\r\n saveRule(name: string, content: string, previousName?: string): SaveRuleResult {\r\n return rulesStorage.save(name, content, previousName);\r\n }\r\n\r\n /**\r\n * Toggle workflow learning mode.\r\n * First call: Start learning and return a message to display.\r\n * Second call: Stop learning and show workflow creator with learned steps.\r\n * @returns Message to display to user, or null if showing workflow creator\r\n */\r\n toggleWorkflowLearning(): string | null {\r\n if (this.workflowLearningActive) {\r\n // Stop learning mode\r\n this.workflowLearningActive = false;\r\n const steps = [...this.learnedWorkflowSteps];\r\n this.learnedWorkflowSteps = []; // Clear for next learning session\r\n\r\n if (steps.length === 0) {\r\n return '⚠️ No steps were recorded. Learning mode cancelled.';\r\n }\r\n\r\n // Show workflow creator with the learned steps\r\n if (this.onShowWorkflowCreatorCallback) {\r\n this.onShowWorkflowCreatorCallback(steps);\r\n return null; // UI will handle the screen change\r\n } else {\r\n return '❌ Workflow creator not available. Please update the CLI.';\r\n }\r\n } else {\r\n // Start learning mode\r\n this.workflowLearningActive = true;\r\n this.learnedWorkflowSteps = [];\r\n return `📚 **Learning mode started!**\r\n\r\nCommands and prompts from here will be recorded to create your workflow.\r\n\r\n**Instructions:**\r\n• Run commands normally - they'll be saved as command steps\r\n• Type AI prompts - they'll be saved as instruction steps\r\n• Run \\`/workflow new learn-workflow\\` again when you're done\r\n\r\nRecording will begin with your next input.`;\r\n }\r\n }\r\n\r\n /**\r\n * Check if workflow learning is currently active\r\n */\r\n isWorkflowLearningActive(): boolean {\r\n return this.workflowLearningActive;\r\n }\r\n\r\n /**\r\n * Record a step during workflow learning mode\r\n * @param type Type of step (command or instruction)\r\n * @param content The command or instruction content\r\n */\r\n recordWorkflowStep(type: 'command' | 'instruction', content: string): void {\r\n if (this.workflowLearningActive && content.trim()) {\r\n this.learnedWorkflowSteps.push({ type, content: content.trim() });\r\n quickLog(`[${new Date().toISOString()}] [WorkflowLearning] Recorded ${type}: ${content.trim()}\\n`);\r\n }\r\n }\r\n\r\n /**\r\n * Cancel workflow learning mode with a message\r\n * Used when conflicting commands are run during learning (e.g., /exit, /clear, /workflow run)\r\n * @param reason The reason for cancellation\r\n * @returns Message to display to user, or empty string if not in learning mode\r\n */\r\n cancelWorkflowLearning(reason: string): string {\r\n if (!this.workflowLearningActive) {\r\n return '';\r\n }\r\n\r\n this.workflowLearningActive = false;\r\n const stepsCount = this.learnedWorkflowSteps.length;\r\n this.learnedWorkflowSteps = [];\r\n\r\n quickLog(`[${new Date().toISOString()}] [WorkflowLearning] Cancelled: ${reason} (${stepsCount} steps discarded)\\n`);\r\n\r\n return `⚠️ **Learning mode cancelled**: ${reason}\\n\\n` +\r\n `${stepsCount > 0 ? `${stepsCount} recorded step(s) were discarded.` : 'No steps were recorded.'}\\n` +\r\n `Run \\`/workflow new learn-workflow\\` to start again.`;\r\n }\r\n\r\n /**\r\n * Build a prompt for the AI to execute a workflow\r\n * This formats the workflow steps into a clear instruction set\r\n */\r\n private buildWorkflowPrompt(workflow: Workflow): string {\r\n const stepsText = workflow.steps.map((step, index) => {\r\n const stepNum = index + 1;\r\n if (step.type === 'command') {\r\n return `Step ${stepNum}: [RUN COMMAND] ${step.content}`;\r\n } else {\r\n return `Step ${stepNum}: [INSTRUCTION] ${step.content}`;\r\n }\r\n }).join('\\n');\r\n\r\n return `Execute the following workflow \"${workflow.name}\" step by step:\r\n\r\n${stepsText}\r\n\r\nIMPORTANT: \r\n- For [RUN COMMAND] steps, execute the exact command shown using the execute_command tool.\r\n- For [INSTRUCTION] steps, follow the instruction using appropriate tools.\r\n- Execute each step in order, waiting for completion before moving to the next.\r\n- If any step fails, stop and report the error.\r\n- After all steps complete successfully, summarize what was done.\r\n\r\nBegin executing now, starting with Step 1.`;\r\n }\r\n\r\n /**\r\n * Warpify a detected SSH/WSL/Docker session.\r\n * This establishes a proper ssh2/handler connection (will prompt for password)\r\n * so commands can be executed via the handler, not just PTY passthrough.\r\n * \r\n * @param command - The original command (e.g., \"ssh rohan@localhost\")\r\n * @param type - The detected session type\r\n * @param connectionString - The connection string (e.g., \"rohan@localhost\")\r\n * @returns Promise<boolean> - true if warpify succeeded\r\n */\r\n async warpifySession(command: string, type: 'ssh' | 'wsl' | 'docker', connectionString?: string): Promise<boolean> {\r\n this.isSessionTransitioning = true;\r\n try {\r\n // Show connecting status\r\n if (this.onConnectionStatusUpdate) {\r\n this.onConnectionStatusUpdate({\r\n type: type,\r\n status: 'connecting',\r\n connectionString: connectionString\r\n });\r\n }\r\n\r\n // Detect and connect using the command\r\n const detection = this.commandDetector.detect(command);\r\n if (!detection) {\r\n throw new Error(`Could not detect handler for command: ${command}`);\r\n }\r\n\r\n // Check if we are already in a remote session\r\n const currentContext = this.contextManager.getCurrentContext();\r\n // Setup variables for the new context\r\n let context: SubshellContext;\r\n\r\n if (currentContext.type !== 'local') {\r\n // Nested session: connect from remote\r\n if (detection.handler.connectFromRemote) {\r\n quickLog(`[${new Date().toISOString()}] [Warpify] nesting connection: ${currentContext.type} -> ${type}\\n`);\r\n context = await detection.handler.connectFromRemote(command, this.cwd, currentContext);\r\n } else {\r\n throw new Error(`Nested connections are not supported by the ${type} handler yet.`);\r\n }\r\n } else {\r\n // Local session: connect from local\r\n const initialCwd = type === 'ssh' ? undefined : this.cwd;\r\n const newHandler = detection.handler.createNew();\r\n context = await newHandler.connect(command, initialCwd);\r\n }\r\n\r\n // Connection succeeded! Now we can safely mutate the stacks.\r\n // Save current CWD to stack BEFORE entering the new session state (local or nested)\r\n this.cwdStack.push(this.cwd);\r\n this.connectionCommandStack.push(command);\r\n this.contextManager.pushContext(context);\r\n\r\n // Explicitly sync this.cwd with the new context's CWD to ensure consistency immediately\r\n if (context.metadata.workingDirectory) {\r\n this.cwd = context.metadata.workingDirectory;\r\n }\r\n\r\n // Set up disconnect callback for remote connections\r\n if (context.handler && context.handler.setDisconnectCallback) {\r\n context.handler.setDisconnectCallback((error?: string) => {\r\n this.handleRemoteDisconnect(connectionString || '', type, error);\r\n });\r\n }\r\n\r\n // Show success\r\n if (this.onConnectionStatusUpdate) {\r\n this.onConnectionStatusUpdate({\r\n type: type,\r\n status: 'connected',\r\n connectionString: connectionString\r\n });\r\n }\r\n\r\n // Update CWD to remote working directory\r\n if (this.onCwdChange && context.metadata.workingDirectory) {\r\n this.onCwdChange(context.metadata.workingDirectory);\r\n }\r\n\r\n quickLog(`[${new Date().toISOString()}] [Warpify] Successfully established ${type} connection via ssh2/handler\\n`);\r\n return true;\r\n\r\n } catch (error: any) {\r\n // Connection failed\r\n if (this.onConnectionStatusUpdate) {\r\n this.onConnectionStatusUpdate({\r\n type: type,\r\n status: 'error',\r\n connectionString: connectionString,\r\n error: error.message\r\n });\r\n }\r\n quickLog(`[${new Date().toISOString()}] [Warpify] Failed to establish connection: ${error.message}\\n`);\r\n return false;\r\n } finally {\r\n this.isSessionTransitioning = false;\r\n void this.maybeProcessSessionCommandQueue();\r\n }\r\n }\r\n\r\n /**\r\n * Exit the current remote session and return to parent environment.\r\n * Called by the AI agent's enter_remote_session tool with action=\"exit\".\r\n * @returns Promise<boolean> - true if there was a session to exit\r\n */\r\n async exitRemoteSession(): Promise<boolean> {\r\n const currentContext = this.contextManager.getCurrentContext();\r\n\r\n // Check if we're in a remote session\r\n if (currentContext.type === 'local') {\r\n return false; // No remote session to exit\r\n }\r\n\r\n // Get connection info for logging\r\n const sessionType = currentContext.type;\r\n const metadata = currentContext.metadata as any;\r\n const connectionString = metadata.connectionString ||\r\n metadata.host ||\r\n metadata.containerId ||\r\n metadata.distro ||\r\n metadata.workingDirectory ||\r\n 'unknown';\r\n\r\n quickLog(`[${new Date().toISOString()}] [ExitRemoteSession] Exiting ${sessionType} session: ${connectionString}\\n`);\r\n\r\n // Close the handler if it has a disconnect method\r\n if (currentContext.handler && typeof currentContext.handler.disconnect === 'function') {\r\n try {\r\n await currentContext.handler.disconnect();\r\n } catch (error: any) {\r\n quickLog(`[${new Date().toISOString()}] [ExitRemoteSession] Error during handler disconnect: ${error.message}\\n`);\r\n }\r\n }\r\n\r\n // Pop the context\r\n this.contextManager.popContext();\r\n\r\n // Restore CWD from stack\r\n const previousCwd = this.cwdStack.pop();\r\n const newContext = this.contextManager.getCurrentContext();\r\n\r\n if (previousCwd) {\r\n this.cwd = previousCwd;\r\n } else if (newContext.type !== 'local') {\r\n this.cwd = newContext.metadata.workingDirectory;\r\n }\r\n this.contextManager.updateWorkingDirectory(this.cwd);\r\n\r\n if (newContext.type === 'local') {\r\n this.connectionCommandStack.pop();\r\n }\r\n\r\n // Notify CWD change\r\n if (this.onCwdChange) {\r\n this.onCwdChange(this.cwd);\r\n }\r\n\r\n // Save chat state\r\n this.saveCurrentChat();\r\n\r\n // Update UI connection status\r\n if (this.onConnectionStatusUpdate) {\r\n if (newContext.type === 'local') {\r\n this.onConnectionStatusUpdate({\r\n type: sessionType as 'ssh' | 'wsl' | 'docker',\r\n status: 'disconnected',\r\n connectionString: connectionString\r\n });\r\n } else {\r\n // Still in a nested session\r\n const newMetadata = newContext.metadata as any;\r\n this.onConnectionStatusUpdate({\r\n type: newContext.type as 'ssh' | 'wsl' | 'docker',\r\n status: 'connected',\r\n connectionString: newMetadata.connectionString || newMetadata.host || newMetadata.workingDirectory\r\n });\r\n }\r\n }\r\n\r\n return true;\r\n }\r\n\r\n /**\r\n * Handle disconnection from a remote session\r\n */\r\n private handleRemoteDisconnect(connectionString: string, type: string, error?: string): void {\r\n quickLog(`[${new Date().toISOString()}] [Warpify] Disconnected from ${type} session: ${connectionString} (${error || 'clean exit'})\\n`);\r\n\r\n // Pop the context (this was the session that just ended)\r\n const endedContext = this.contextManager.popContext();\r\n\r\n // Now look at the NEW current context (the parent)\r\n const currentContext = this.contextManager.getCurrentContext();\r\n\r\n // Restore CWD from stack - this handles any nesting level\r\n const previousCwd = this.cwdStack.pop();\r\n if (previousCwd) {\r\n this.cwd = previousCwd;\r\n } else if (currentContext.type !== 'local') {\r\n // Fallback: use parent context's CWD if no stack entry\r\n this.cwd = currentContext.metadata.workingDirectory;\r\n }\r\n this.contextManager.updateWorkingDirectory(this.cwd);\r\n\r\n if (currentContext.type === 'local') {\r\n this.connectionCommandStack.pop();\r\n }\r\n\r\n // Notify CWD change\r\n if (this.onCwdChange) {\r\n this.onCwdChange(this.cwd);\r\n }\r\n\r\n // Save chat state\r\n this.saveCurrentChat();\r\n\r\n // Update UI connection status\r\n if (this.onConnectionStatusUpdate) {\r\n if (currentContext.type === 'local') {\r\n this.onConnectionStatusUpdate({\r\n type: type as any,\r\n status: 'disconnected',\r\n connectionString\r\n });\r\n } else {\r\n // Update status to reflect the parent session\r\n let parentConnString = '';\r\n if (currentContext.type === 'ssh') {\r\n parentConnString = `${currentContext.metadata.username}@${currentContext.metadata.hostname}`;\r\n } else if (currentContext.type === 'wsl') {\r\n parentConnString = currentContext.metadata.distroName || '';\r\n } else if (currentContext.type === 'docker') {\r\n parentConnString = currentContext.metadata.containerId || '';\r\n }\r\n\r\n this.onConnectionStatusUpdate({\r\n type: currentContext.type as any,\r\n status: 'connected',\r\n connectionString: parentConnString\r\n });\r\n }\r\n }\r\n\r\n // If there's an active tool execution (shell running), mark it as error\r\n if (this.onToolExecutionUpdate && this.currentInteractiveProcess) {\r\n this.onToolExecutionUpdate({\r\n toolName: 'execute_command',\r\n status: 'error',\r\n error: `Disconnected from ${type}: ${error || 'Connection lost'}`\r\n });\r\n this.currentInteractiveProcess = undefined;\r\n }\r\n }\r\n\r\n private normalizePromptForInputBar(prompt: string): string {\r\n if (!prompt) return '';\r\n\r\n if (prompt.startsWith(CentaurusCLI.INTERRUPT_SYSTEM_NOTE_PREFIX)) {\r\n return prompt\r\n .slice(CentaurusCLI.INTERRUPT_SYSTEM_NOTE_PREFIX.length)\r\n .trimStart();\r\n }\r\n\r\n return prompt;\r\n }\r\n\r\n /**\r\n * Calculate and update token count based on current conversation history\r\n * This ensures UI is always in sync with the actual AI context\r\n * Uses backend's accurate token counting API (Vertex AI countTokens)\r\n */\r\n private async updateTokenCount(): Promise<void> {\r\n const updateVersion = ++this.tokenCountUpdateVersion;\r\n\r\n try {\r\n // Get current model\r\n const currentModel = this.configManager.get('modelName') || 'gemini-2.5-flash';\r\n\r\n // Prepare messages for token counting\r\n // Backend will automatically include system prompt when counting\r\n // We just send the conversation history\r\n const messagesForCounting = [...this.conversationHistory];\r\n\r\n // Call backend API for accurate token counting\r\n const tokenCount = await this.countTokensForMessages(currentModel, messagesForCounting);\r\n\r\n if (updateVersion !== this.tokenCountUpdateVersion) {\r\n quickLog(`[${new Date().toISOString()}] [updateTokenCount] Ignored stale token count update\\n`);\r\n return;\r\n }\r\n\r\n this.applyTokenCount(tokenCount);\r\n\r\n const logMsg = `\\n============================================================\\n` +\r\n `[TOKEN COUNT CHECK] Model: ${currentModel}\\n` +\r\n `[TOKEN COUNT CHECK] Messages: ${messagesForCounting.length}\\n` +\r\n `[TOKEN COUNT CHECK] Count: ${tokenCount} tokens\\n` +\r\n `============================================================\\n`;\r\n\r\n quickLog(`[${new Date().toISOString()}] [updateTokenCount] ${logMsg}`);\r\n } catch (error) {\r\n if (updateVersion !== this.tokenCountUpdateVersion) {\r\n return;\r\n }\r\n\r\n const currentModel = this.configManager.get('modelName') || 'gemini-2.5-flash';\r\n const estimatedTokens = this.estimateTokenCount(this.conversationHistory);\r\n this.applyTokenCount(estimatedTokens);\r\n quickLog(`[${new Date().toISOString()}] [updateTokenCount] Fallback estimate: ${estimatedTokens} tokens for ${currentModel} (API error: ${error})\\n`);\r\n }\r\n }\r\n\r\n /**\r\n * Get current token count for context limit checking\r\n */\r\n private getCurrentTokenCount(): number {\r\n return this.currentTokenCount;\r\n }\r\n\r\n private applyTokenCount(tokens: number): void {\r\n this.currentTokenCount = tokens;\r\n if (this.onTokenCountUpdate) {\r\n this.onTokenCountUpdate(tokens);\r\n }\r\n }\r\n\r\n private setContextLimitReachedState(reached: boolean): void {\r\n if (this.contextLimitReached === reached) {\r\n return;\r\n }\r\n\r\n this.contextLimitReached = reached;\r\n if (this.onContextLimitReached) {\r\n this.onContextLimitReached(reached);\r\n }\r\n }\r\n\r\n private estimateTokenCount(messages: AIMessage[]): number {\r\n const SYSTEM_PROMPT_ESTIMATE = 14000; // Backend injects ~14K char system prompt\r\n let totalCharacters = 0;\r\n\r\n for (const msg of messages) {\r\n if (typeof msg.content === 'string') {\r\n totalCharacters += msg.content.length;\r\n }\r\n\r\n if (msg.thinking) {\r\n totalCharacters += msg.thinking.length;\r\n }\r\n\r\n if (msg.tool_calls) {\r\n for (const tc of msg.tool_calls) {\r\n totalCharacters += tc.name.length;\r\n if (tc.arguments) {\r\n totalCharacters += JSON.stringify(tc.arguments).length;\r\n }\r\n }\r\n }\r\n\r\n if (msg.role === 'tool' && msg.tool_call_id) {\r\n totalCharacters += msg.tool_call_id.length;\r\n }\r\n }\r\n\r\n const systemPromptChars = messages.length > 0 ? SYSTEM_PROMPT_ESTIMATE : 0;\r\n return Math.ceil((totalCharacters + systemPromptChars) / 4);\r\n }\r\n\r\n private async countTokensForMessages(model: string, messages: AIMessage[]): Promise<number> {\r\n try {\r\n return await apiClient.countTokens(model, messages);\r\n } catch (error) {\r\n const estimated = this.estimateTokenCount(messages);\r\n quickLog(`[${new Date().toISOString()}] [countTokensForMessages] Falling back to estimate (${estimated}) due to API error: ${error}\\n`);\r\n return estimated;\r\n }\r\n }\r\n\r\n setOnSessionQuotaUpdate(callback: (remaining: number, canSend: boolean, timeRemaining: string) => void): void {\r\n this.onSessionQuotaUpdate = callback;\r\n }\r\n\r\n // MCP screen callback setters\r\n setOnMCPAddScreenSetup(callback: () => void): void {\r\n this.onShowMCPAddScreen = callback;\r\n }\r\n\r\n setOnMCPRemoveScreenSetup(callback: (servers: { name: string; command: string; args?: string[]; enabled?: boolean }[]) => void): void {\r\n this.onShowMCPRemoveScreen = callback;\r\n }\r\n\r\n setOnMCPEnableScreenSetup(callback: (servers: { name: string; command: string; args?: string[]; enabled?: boolean }[]) => void): void {\r\n this.onShowMCPEnableScreen = callback;\r\n }\r\n\r\n setOnMCPDisableScreenSetup(callback: (servers: { name: string; command: string; args?: string[]; enabled?: boolean }[]) => void): void {\r\n this.onShowMCPDisableScreen = callback;\r\n }\r\n\r\n setOnMCPListScreenSetup(callback: (servers: Array<{ name: string; enabled: boolean; status: 'connected' | 'disconnected' | 'error' | 'connecting'; tools: Array<{ name: string }> }>) => void): void {\r\n this.onShowMCPListScreen = callback;\r\n }\r\n\r\n // MCP server operation methods (called from UI)\r\n mcpAddServer(config: any): { success: boolean; error?: string } {\r\n if (this.mcpCommandHandler) {\r\n return this.mcpCommandHandler.addServer(config);\r\n }\r\n return { success: false, error: 'MCP not initialized' };\r\n }\r\n\r\n mcpRemoveServer(name: string): void {\r\n if (this.mcpCommandHandler) {\r\n this.mcpCommandHandler.removeServer(name);\r\n }\r\n }\r\n\r\n mcpEnableServer(name: string): void {\r\n if (this.mcpCommandHandler) {\r\n this.mcpCommandHandler.enableServer(name);\r\n }\r\n }\r\n\r\n mcpDisableServer(name: string): void {\r\n if (this.mcpCommandHandler) {\r\n this.mcpCommandHandler.disableServer(name);\r\n }\r\n }\r\n\r\n mcpValidateConfig(jsonString: string): { valid: boolean; config?: any; error?: string } {\r\n if (this.mcpCommandHandler) {\r\n return this.mcpCommandHandler.validateServerConfig(jsonString);\r\n }\r\n return { valid: false, error: 'MCP not initialized' };\r\n }\r\n\r\n /**\r\n * Programmatically add an MCP server, connect to it, and register its tools.\r\n * Used by the add_mcp AI tool for automatic MCP provisioning.\r\n */\r\n async mcpAddAndConnect(config: any, timeoutMs?: number): Promise<{\r\n success: boolean;\r\n serverName: string;\r\n tools: string[];\r\n error?: string;\r\n logs?: string;\r\n }> {\r\n if (this.mcpCommandHandler) {\r\n return this.mcpCommandHandler.addAndConnect(config, timeoutMs);\r\n }\r\n return { success: false, serverName: config?.name || 'unknown', tools: [], error: 'MCP not initialized' };\r\n }\r\n\r\n /**\r\n * Get the names of currently connected MCP servers.\r\n * Used by AIContextInjector to inform the AI about available MCP integrations.\r\n */\r\n getConnectedMCPServerNames(): string[] {\r\n if (this.mcpCommandHandler) {\r\n return this.mcpCommandHandler.getConnectedServerNames();\r\n }\r\n return [];\r\n }\r\n\r\n /**\r\n * Notify UI about session quota status\r\n */\r\n private notifySessionQuotaStatus(): void {\r\n if (this.onSessionQuotaUpdate) {\r\n const remaining = sessionQuotaManager.getRemainingMessages();\r\n const canSend = sessionQuotaManager.canSendMessage();\r\n const timeRemaining = sessionQuotaManager.getFormattedTimeRemaining();\r\n this.onSessionQuotaUpdate(remaining, canSend, timeRemaining);\r\n }\r\n }\r\n\r\n private async initializeMCP(): Promise<void> {\r\n try {\r\n const mcpConfigManager = new MCPConfigManager();\r\n const mcpServerManager = new MCPServerManager();\r\n\r\n this.mcpCommandHandler = new MCPCommandHandler(\r\n mcpConfigManager,\r\n mcpServerManager,\r\n this.toolRegistry\r\n );\r\n\r\n // Wire MCP screen callbacks\r\n this.mcpCommandHandler.setOnShowMCPAddScreen(() => {\r\n if (this.onShowMCPAddScreen) {\r\n this.onShowMCPAddScreen();\r\n }\r\n });\r\n this.mcpCommandHandler.setOnShowMCPRemoveScreen((servers) => {\r\n if (this.onShowMCPRemoveScreen) {\r\n this.onShowMCPRemoveScreen(servers);\r\n }\r\n });\r\n this.mcpCommandHandler.setOnShowMCPEnableScreen((servers) => {\r\n if (this.onShowMCPEnableScreen) {\r\n this.onShowMCPEnableScreen(servers);\r\n }\r\n });\r\n this.mcpCommandHandler.setOnShowMCPDisableScreen((servers) => {\r\n if (this.onShowMCPDisableScreen) {\r\n this.onShowMCPDisableScreen(servers);\r\n }\r\n });\r\n this.mcpCommandHandler.setOnShowMCPListScreen((servers) => {\r\n if (this.onShowMCPListScreen) {\r\n this.onShowMCPListScreen(servers);\r\n }\r\n });\r\n\r\n // Initialize MCP servers and tools\r\n await this.mcpCommandHandler.initializeMCP();\r\n } catch (error: any) {\r\n logWarning(`Failed to initialize MCP: ${error?.message || error}`);\r\n }\r\n }\r\n\r\n writeToShellStdin(input: string): void {\r\n if (this.currentInteractiveProcess) {\r\n try {\r\n this.currentInteractiveProcess.write(input);\r\n } catch (error: any) {\r\n // Silently ignore - process might have already exited\r\n }\r\n }\r\n }\r\n\r\n sendSignalToShell(signal: NodeJS.Signals): void {\r\n if (this.currentInteractiveProcess) {\r\n try {\r\n this.currentInteractiveProcess.signal(signal);\r\n } catch (error: any) {\r\n // Silently ignore - process might have already exited\r\n }\r\n }\r\n }\r\n\r\n killCurrentProcess(): void {\r\n if (this.currentInteractiveProcess && this.currentInteractiveProcess.process.pid) {\r\n try {\r\n const { killProcessTree } = require('./utils/shell.js');\r\n killProcessTree(this.currentInteractiveProcess.process.pid, 'SIGKILL');\r\n } catch (error: any) {\r\n // Ignore\r\n }\r\n }\r\n }\r\n\r\n getPlanMode(): boolean {\r\n return this.planMode;\r\n }\r\n\r\n getCommandMode(): boolean {\r\n return this.commandMode;\r\n }\r\n\r\n /**\r\n * Get current conversation history for shell input agent context\r\n * Returns a copy to prevent modification\r\n */\r\n getConversationHistory(): AIMessage[] {\r\n return [...this.conversationHistory];\r\n }\r\n\r\n getCurrentWorkingDirectory(): string {\r\n return this.cwd;\r\n }\r\n\r\n getCurrentSubshellContext(): SubshellContext {\r\n return this.contextManager.getCurrentContext();\r\n }\r\n\r\n getCurrentInteractiveProcess(): shellUtils.InteractiveProcess | undefined {\r\n return this.currentInteractiveProcess;\r\n }\r\n\r\n /**\r\n * Get the current conversation ID for file uploads\r\n */\r\n getCurrentConversationId(): string | null {\r\n return conversationManager.getCurrentConversationId();\r\n }\r\n\r\n async handlePickerSelection(selection: string, pickerType: 'model' | 'local-model'): Promise<void> {\r\n try {\r\n if (pickerType === 'local-model') {\r\n // Local Ollama model selection\r\n // Selection is the model name (e.g., \"llama3:latest\")\r\n const modelName = selection;\r\n\r\n // Store the local model configuration\r\n this.configManager.set('model', modelName);\r\n this.configManager.set('modelName', modelName);\r\n this.configManager.set('isLocalModel', true);\r\n\r\n // Notify UI of model name change\r\n // Local models don't have a fixed context window, use a reasonable default\r\n if (this.onModelChange) {\r\n this.onModelChange(modelName, 128000); // Most local models have 128k context\r\n }\r\n\r\n const responseMessage = `✅ Switched to local Ollama model: ${modelName}`;\r\n\r\n // Send response back to UI\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback(responseMessage);\r\n }\r\n } else {\r\n // Cloud model selection (existing behavior)\r\n // Selection is the index of the model in models array from backend\r\n // Clear cache to ensure we get fresh uid fields from the backend\r\n clearModelsCache();\r\n const modelsConfig = await fetchModelsConfig();\r\n const modelIndex = parseInt(selection, 10);\r\n\r\n if (isNaN(modelIndex) || modelIndex < 0 || modelIndex >= modelsConfig.models.length) {\r\n throw new Error('Invalid model selection');\r\n }\r\n\r\n const selectedModel = modelsConfig.models[modelIndex];\r\n\r\n // Store only the model ID, uid, and name (not the full config with thinkingConfig)\r\n // This prevents caching issues when we update model configs\r\n this.configManager.set('model', selectedModel.id);\r\n this.configManager.set('modelUid', selectedModel.uid);\r\n this.configManager.set('modelName', selectedModel.name);\r\n this.configManager.set('isLocalModel', false);\r\n\r\n // Notify UI of model name change and contextWindow\r\n if (this.onModelChange) {\r\n this.onModelChange(selectedModel.name, selectedModel.contextWindow);\r\n }\r\n\r\n const responseMessage = `✅ Switched to cloud model: ${selectedModel.name}`;\r\n\r\n // Send response back to UI\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback(responseMessage);\r\n }\r\n }\r\n } catch (error: any) {\r\n // Send error message to UI\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback(`❌ ${error.message}`);\r\n }\r\n }\r\n }\r\n\r\n private sshHandler?: SSHHandler;\r\n\r\n /**\r\n * Notify UI about tool execution status\r\n */\r\n private notifyToolStatus(\r\n toolName: string,\r\n status: 'executing' | 'completed' | 'error',\r\n args?: Record<string, any>,\r\n result?: string,\r\n error?: string\r\n ): void {\r\n // Skip UI status updates for enter_remote_session - the password prompt\r\n // and \"Established Wormhole\" message already provide sufficient feedback\r\n if (toolName === 'enter_remote_session') {\r\n return;\r\n }\r\n\r\n if (this.onToolExecutionUpdate) {\r\n // Get current context for remote prefix\r\n const currentContext = this.contextManager.getCurrentContext();\r\n\r\n // Build remote context prefix for SSH/Docker/WSL\r\n let remoteContext: string | undefined;\r\n if (currentContext.type !== 'local') {\r\n const metadata = currentContext.metadata;\r\n if (currentContext.type === 'ssh' && metadata) {\r\n // SSH: user@hostname\r\n const username = metadata.username || 'user';\r\n const hostname = metadata.hostname || 'remote';\r\n remoteContext = `${username}@${hostname}`;\r\n } else if (currentContext.type === 'wsl' && metadata) {\r\n // WSL: distroName or just wsl\r\n remoteContext = `wsl:${metadata.distroName || 'wsl'}`;\r\n } else if (currentContext.type === 'docker' && metadata) {\r\n // Docker: container id (first 12 chars)\r\n remoteContext = `docker:${metadata.containerId?.substring(0, 12) || 'container'}`;\r\n }\r\n }\r\n\r\n // Add cwd and remoteContext to arguments for execute_command tool\r\n let toolArgs = args;\r\n if (toolName === 'execute_command' && args) {\r\n toolArgs = { ...args, cwd: this.cwd, remoteContext };\r\n } else if (remoteContext && args) {\r\n // For other tools, also add remoteContext if in remote environment\r\n toolArgs = { ...args, remoteContext };\r\n } else if (remoteContext) {\r\n toolArgs = { remoteContext };\r\n }\r\n\r\n this.onToolExecutionUpdate({\r\n toolName,\r\n status,\r\n result,\r\n error,\r\n arguments: toolArgs\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Fire-and-forget update of the live file change breadcrumb.\r\n * Called after each file-modifying tool so the UI updates in real time.\r\n */\r\n private updateFileChangeSummary(): void {\r\n if (!this.onFileChangeSummaryCallback || !this.checkpointManager) return;\r\n\r\n const ctx = this.contextManager.getCurrentContext();\r\n let remoteHandler: RemoteFileHandler | undefined;\r\n if (ctx.type !== 'local' && ctx.handler &&\r\n typeof ctx.handler.readFile === 'function' &&\r\n typeof ctx.handler.writeFile === 'function' &&\r\n typeof ctx.handler.executeCommand === 'function' &&\r\n typeof ctx.handler.isConnected === 'function') {\r\n remoteHandler = ctx.handler as RemoteFileHandler;\r\n }\r\n\r\n this.checkpointManager.getAggregatedSessionChanges(remoteHandler)\r\n .then(sessionChanges => {\r\n if (!sessionChanges || !this.onFileChangeSummaryCallback) return;\r\n const { changes, stats } = sessionChanges;\r\n const fileCount = changes.added.length + changes.modified.length + changes.deleted.length;\r\n if (fileCount > 0) {\r\n const totalInsertions = stats.reduce((sum, s) => sum + s.insertions, 0);\r\n const totalDeletions = stats.reduce((sum, s) => sum + s.deletions, 0);\r\n this.onFileChangeSummaryCallback({ filesChanged: fileCount, insertions: totalInsertions, deletions: totalDeletions });\r\n }\r\n })\r\n .catch(() => {}); // Silently ignore errors\r\n }\r\n\r\n /**\r\n * Truncate large results for AI consumption\r\n */\r\n private truncateResult(result: any, maxLength: number = 500000): any {\r\n const resultStr = typeof result === 'string' ? result : JSON.stringify(result);\r\n if (resultStr.length > maxLength) {\r\n const truncated = resultStr.substring(0, maxLength);\r\n const linesRemoved = (resultStr.match(/\\n/g) || []).length - (truncated.match(/\\n/g) || []).length;\r\n return truncated + `\\n\\n[... truncated ${resultStr.length - maxLength} characters, ~${linesRemoved} lines removed for brevity ...]`;\r\n }\r\n return result;\r\n }\r\n\r\n private calculateUsagePercent(tokenCount: number, maxTokens: number): number {\r\n if (!Number.isFinite(maxTokens) || maxTokens <= 0) {\r\n return 0;\r\n }\r\n return (tokenCount / maxTokens) * 100;\r\n }\r\n\r\n private isUserShellCommandMessage(content: string): boolean {\r\n if (!content) {\r\n return false;\r\n }\r\n\r\n return content.startsWith(CentaurusCLI.USER_SHELL_PREFIX) &&\r\n content.includes(CentaurusCLI.USER_SHELL_OUTPUT_MARKER);\r\n }\r\n\r\n private getAutoCompactionCandidates(): AutoCompactionCandidate[] {\r\n const candidates: AutoCompactionCandidate[] = [];\r\n const toolNameByCallId = new Map<string, string>();\r\n\r\n for (let i = 0; i < this.conversationHistory.length; i++) {\r\n const message = this.conversationHistory[i];\r\n\r\n if (message.role === 'assistant' && Array.isArray(message.tool_calls)) {\r\n for (const toolCall of message.tool_calls) {\r\n if (toolCall?.id && toolCall?.name) {\r\n toolNameByCallId.set(toolCall.id, toolCall.name);\r\n }\r\n }\r\n continue;\r\n }\r\n\r\n if (message.role === 'tool' && message.tool_call_id) {\r\n const toolName = toolNameByCallId.get(message.tool_call_id);\r\n if (toolName === 'execute_command') {\r\n candidates.push({\r\n index: i,\r\n kind: 'execute_command_tool_result',\r\n toolCallId: message.tool_call_id\r\n });\r\n } else if (toolName === 'view_file' || toolName === 'read_file') {\r\n candidates.push({\r\n index: i,\r\n kind: 'read_file_tool_result',\r\n toolCallId: message.tool_call_id\r\n });\r\n }\r\n continue;\r\n }\r\n\r\n if (message.role === 'user' && this.isUserShellCommandMessage(message.content)) {\r\n candidates.push({\r\n index: i,\r\n kind: 'user_shell_command_output'\r\n });\r\n }\r\n }\r\n\r\n return candidates;\r\n }\r\n\r\n private compactTerminalOutputToLastLines(content: string): { content: string; changed: boolean } {\r\n if (!content || content.includes(CentaurusCLI.TERMINAL_COMPACTION_NOTICE)) {\r\n return { content, changed: false };\r\n }\r\n\r\n const normalized = content.replace(/\\r\\n/g, '\\n');\r\n const lines = normalized.split('\\n');\r\n\r\n if (lines.length > 0 && lines[lines.length - 1] === '') {\r\n lines.pop();\r\n }\r\n\r\n const tail = lines.slice(-CentaurusCLI.TERMINAL_LINES_TO_KEEP).join('\\n');\r\n const compactedBody = tail || '(no output)';\r\n const compactedContent = `${compactedBody}\\n\\n${CentaurusCLI.TERMINAL_COMPACTION_NOTICE}`;\r\n\r\n return {\r\n content: compactedContent,\r\n changed: compactedContent !== content\r\n };\r\n }\r\n\r\n private compactUserShellCommandMessage(content: string): { content: string; changed: boolean } {\r\n if (!this.isUserShellCommandMessage(content) || content.includes(CentaurusCLI.TERMINAL_COMPACTION_NOTICE)) {\r\n return { content, changed: false };\r\n }\r\n\r\n const markerIndex = content.indexOf(CentaurusCLI.USER_SHELL_OUTPUT_MARKER);\r\n if (markerIndex < 0) {\r\n return { content, changed: false };\r\n }\r\n\r\n const markerEnd = markerIndex + CentaurusCLI.USER_SHELL_OUTPUT_MARKER.length;\r\n const header = content.slice(0, markerEnd);\r\n const output = content.slice(markerEnd);\r\n const compactedOutput = this.compactTerminalOutputToLastLines(output);\r\n\r\n if (!compactedOutput.changed) {\r\n return { content, changed: false };\r\n }\r\n\r\n const compactedContent = `${header}${compactedOutput.content}`;\r\n return {\r\n content: compactedContent,\r\n changed: compactedContent !== content\r\n };\r\n }\r\n\r\n private applyAutoCompactionCandidate(candidate: AutoCompactionCandidate): boolean {\r\n const message = this.conversationHistory[candidate.index];\r\n if (!message || typeof message.content !== 'string') {\r\n return false;\r\n }\r\n\r\n if (candidate.kind === 'read_file_tool_result') {\r\n if (message.content.trim() === CentaurusCLI.READ_FILE_COMPACTION_MESSAGE) {\r\n return false;\r\n }\r\n message.content = CentaurusCLI.READ_FILE_COMPACTION_MESSAGE;\r\n return true;\r\n }\r\n\r\n if (candidate.kind === 'execute_command_tool_result') {\r\n const compacted = this.compactTerminalOutputToLastLines(message.content);\r\n if (!compacted.changed) {\r\n return false;\r\n }\r\n message.content = compacted.content;\r\n return true;\r\n }\r\n\r\n if (candidate.kind === 'user_shell_command_output') {\r\n const compacted = this.compactUserShellCommandMessage(message.content);\r\n if (!compacted.changed) {\r\n return false;\r\n }\r\n message.content = compacted.content;\r\n return true;\r\n }\r\n\r\n return false;\r\n }\r\n\r\n private appendAutoCompactionNoticeForAgent(): boolean {\r\n const lastMessage = this.conversationHistory[this.conversationHistory.length - 1];\r\n if (lastMessage?.role === 'user' && lastMessage.content === CentaurusCLI.COMPACTION_NOTICE_FOR_AGENT) {\r\n return false;\r\n }\r\n\r\n this.conversationHistory.push({\r\n role: 'user',\r\n content: CentaurusCLI.COMPACTION_NOTICE_FOR_AGENT\r\n });\r\n return true;\r\n }\r\n\r\n private async maybeAutoCompactContext(model: string, maxTokens: number): Promise<AutoCompactionOutcome> {\r\n let beforeUsagePercent = 0;\r\n let afterUsagePercent = 0;\r\n let compactedCount = 0;\r\n let historyChanged = false;\r\n\r\n try {\r\n let tokenCount = await this.countTokensForMessages(model, this.conversationHistory);\r\n this.applyTokenCount(tokenCount);\r\n\r\n beforeUsagePercent = this.calculateUsagePercent(tokenCount, maxTokens);\r\n afterUsagePercent = beforeUsagePercent;\r\n\r\n if (beforeUsagePercent < CentaurusCLI.AUTO_COMPACTION_TRIGGER_PERCENT) {\r\n return {\r\n triggered: false,\r\n historyChanged: false,\r\n compactedCount: 0,\r\n beforeUsagePercent,\r\n afterUsagePercent,\r\n targetReached: afterUsagePercent < CentaurusCLI.AUTO_COMPACTION_TARGET_PERCENT\r\n };\r\n }\r\n\r\n this.notifyToolStatus(\r\n CentaurusCLI.AUTO_COMPACTION_TOOL_NAME,\r\n 'executing',\r\n {\r\n usagePercent: Number(beforeUsagePercent.toFixed(2)),\r\n triggerPercent: CentaurusCLI.AUTO_COMPACTION_TRIGGER_PERCENT,\r\n targetPercent: CentaurusCLI.AUTO_COMPACTION_TARGET_PERCENT\r\n }\r\n );\r\n\r\n const candidates = this.getAutoCompactionCandidates();\r\n for (const candidate of candidates) {\r\n const changed = this.applyAutoCompactionCandidate(candidate);\r\n if (!changed) {\r\n continue;\r\n }\r\n\r\n compactedCount += 1;\r\n historyChanged = true;\r\n\r\n tokenCount = await this.countTokensForMessages(model, this.conversationHistory);\r\n this.applyTokenCount(tokenCount);\r\n afterUsagePercent = this.calculateUsagePercent(tokenCount, maxTokens);\r\n\r\n if (afterUsagePercent < CentaurusCLI.AUTO_COMPACTION_TARGET_PERCENT) {\r\n break;\r\n }\r\n }\r\n\r\n if (compactedCount > 0) {\r\n const noticeAdded = this.appendAutoCompactionNoticeForAgent();\r\n historyChanged = historyChanged || noticeAdded;\r\n\r\n tokenCount = await this.countTokensForMessages(model, this.conversationHistory);\r\n this.applyTokenCount(tokenCount);\r\n afterUsagePercent = this.calculateUsagePercent(tokenCount, maxTokens);\r\n }\r\n\r\n const targetReached = afterUsagePercent < CentaurusCLI.AUTO_COMPACTION_TARGET_PERCENT;\r\n const completionSummary = compactedCount > 0\r\n ? `Auto compacted context: compacted ${compactedCount} item(s). Usage ${beforeUsagePercent.toFixed(1)}% -> ${afterUsagePercent.toFixed(1)}%.`\r\n : `Auto compacted context: no eligible read-file or terminal outputs left to compact. Usage ${afterUsagePercent.toFixed(1)}%.`;\r\n\r\n this.notifyToolStatus(\r\n CentaurusCLI.AUTO_COMPACTION_TOOL_NAME,\r\n 'completed',\r\n {\r\n compactedCount,\r\n beforeUsagePercent: Number(beforeUsagePercent.toFixed(2)),\r\n afterUsagePercent: Number(afterUsagePercent.toFixed(2)),\r\n targetPercent: CentaurusCLI.AUTO_COMPACTION_TARGET_PERCENT,\r\n targetReached\r\n },\r\n completionSummary\r\n );\r\n\r\n quickLog(\r\n `[${new Date().toISOString()}] [AutoCompaction] Triggered at ${beforeUsagePercent.toFixed(1)}%, compacted ${compactedCount} item(s), final usage ${afterUsagePercent.toFixed(1)}%\\n`\r\n );\r\n\r\n return {\r\n triggered: true,\r\n historyChanged,\r\n compactedCount,\r\n beforeUsagePercent,\r\n afterUsagePercent,\r\n targetReached\r\n };\r\n } catch (error) {\r\n const errorMessage = error instanceof Error ? error.message : String(error);\r\n this.notifyToolStatus(\r\n CentaurusCLI.AUTO_COMPACTION_TOOL_NAME,\r\n 'error',\r\n undefined,\r\n undefined,\r\n `Auto compaction failed: ${errorMessage}`\r\n );\r\n quickLog(`[${new Date().toISOString()}] [AutoCompaction] Failed: ${errorMessage}\\n`);\r\n\r\n return {\r\n triggered: true,\r\n historyChanged,\r\n compactedCount,\r\n beforeUsagePercent,\r\n afterUsagePercent,\r\n targetReached: afterUsagePercent < CentaurusCLI.AUTO_COMPACTION_TARGET_PERCENT\r\n };\r\n }\r\n }\r\n\r\n async initialize(): Promise<void> {\r\n // Register tools\r\n this.toolRegistry.register(viewFileTool);\r\n this.toolRegistry.register(writeToFileTool);\r\n this.toolRegistry.register(editFileTool);\r\n this.toolRegistry.register(multiEditFileTool);\r\n this.toolRegistry.register(listDirTool);\r\n this.toolRegistry.register(runCommandTool);\r\n this.toolRegistry.register(grepSearchTool);\r\n this.toolRegistry.register(findFilesTool);\r\n this.toolRegistry.register(getDiffTool);\r\n this.toolRegistry.register(inspectSymbolTool);\r\n this.toolRegistry.register(createPlanTool);\r\n this.toolRegistry.register(markTaskCompleteTool);\r\n this.toolRegistry.register(webSearchTool);\r\n this.toolRegistry.register(fetchUrlTool);\r\n this.toolRegistry.register(taskCompleteTool);\r\n this.toolRegistry.register(readBinaryFileTool);\r\n this.toolRegistry.register(createImageTool);\r\n this.toolRegistry.register(backgroundCommandTool);\r\n this.toolRegistry.register(subAgentTool);\r\n this.toolRegistry.register(enterRemoteSessionTool);\r\n this.toolRegistry.register(workflowTool);\r\n this.toolRegistry.register(fastContextTool);\r\n this.toolRegistry.register(addMcpTool);\r\n\r\n // Initialize SubAgentManager with tool registry\r\n SubAgentManager.initialize(this.toolRegistry);\r\n SubAgentManager.setOnSubAgentCountChange((count) => {\r\n if (this.onSubAgentCountChange) {\r\n this.onSubAgentCountChange(count);\r\n }\r\n });\r\n\r\n // Load configuration\r\n const config = this.configManager.load();\r\n\r\n // Enable backend sync if authenticated\r\n if (apiClient.isAuthenticated()) {\r\n this.configManager.enableBackendSync();\r\n }\r\n\r\n // Initialize subshell handlers\r\n this.sshHandler = new SSHHandler();\r\n this.contextManager.registerHandler('ssh', this.sshHandler);\r\n this.commandDetector.registerHandler(this.sshHandler);\r\n\r\n const wslHandler = new WSLHandler();\r\n this.contextManager.registerHandler('wsl', wslHandler);\r\n this.commandDetector.registerHandler(wslHandler);\r\n\r\n const dockerHandler = new DockerHandler();\r\n this.contextManager.registerHandler('docker', dockerHandler);\r\n this.commandDetector.registerHandler(dockerHandler);\r\n\r\n // Fetch rate limits configuration from backend (async, non-blocking)\r\n // Uses cached values if backend is unreachable\r\n sessionQuotaManager.fetchConfigFromBackend().catch(() => {\r\n // Silently fall back to cached/default config\r\n });\r\n\r\n // Note: No need to initialize AI provider - using backend proxy via aiServiceClient\r\n }\r\n\r\n /**\r\n * Get migration message if configuration was migrated\r\n * Returns null if no migration occurred\r\n */\r\n getMigrationMessage(): string | null {\r\n if (this.configManager.wasMigrationPerformed()) {\r\n // Reset flag so message only shows once\r\n this.configManager.resetMigrationFlag();\r\n\r\n return `\r\n╔════════════════════════════════════════════════════════════════════════════╗\r\n║ CONFIGURATION UPDATE NOTICE ║\r\n╚════════════════════════════════════════════════════════════════════════════╝\r\n\r\nYour Centaurus CLI configuration has been automatically updated to the new format.\r\n\r\n📋 What Changed:\r\n • API keys are now managed centrally by the backend service\r\n • Local API key storage has been removed from your configuration\r\n • All AI requests now go through the backend for improved security\r\n\r\n🔐 Authentication Required:\r\n • You must be signed in to use AI features\r\n • Your authentication is already active for this session\r\n\r\n✅ Your Preferences Preserved:\r\n • Model selection: ${this.configManager.get('model') || 'gemini-2.5-flash'}\r\n • Other settings remain unchanged\r\n\r\nℹ️ This is a one-time migration. Your configuration is now up to date.\r\n\r\nPress Enter to continue...\r\n`;\r\n }\r\n\r\n return null;\r\n }\r\n\r\n /**\r\n * Check if user is authenticated\r\n */\r\n isAuthenticated(): boolean {\r\n return apiClient.isAuthenticated();\r\n }\r\n\r\n /**\r\n * Start a new conversation in the backend\r\n * @deprecated Backend conversation creation is no longer needed since messages are stored locally\r\n * This function is kept as a no-op for compatibility\r\n */\r\n private async ensureConversationStarted(): Promise<void> {\r\n // No-op: Backend conversation creation has been disabled\r\n // Conversations are now managed locally via local-chat-storage.ts\r\n return;\r\n }\r\n\r\n /**\r\n * Save a message to the backend\r\n * @deprecated Messages are now stored locally only via saveCurrentChat()\r\n * This function is kept as a no-op for compatibility\r\n */\r\n private async saveMessageToBackend(role: 'user' | 'assistant' | 'system', content: string): Promise<void> {\r\n // No-op: Messages are stored locally only\r\n // Backend message storage has been disabled to avoid \"failed to fetch\" errors\r\n // All conversation history is persisted via local-chat-storage.ts\r\n return;\r\n }\r\n\r\n getModel(): string {\r\n const config = this.configManager.load();\r\n // Return the stored model name, or fall back to model ID\r\n return config.modelName || config.model || 'gemini-2.5-flash';\r\n }\r\n\r\n /**\r\n * Cancel the current AI request\r\n */\r\n cancelCurrentRequest(): void {\r\n if (this.currentAbortController) {\r\n this.requestIntentionallyAborted = true;\r\n this.currentAbortController.abort();\r\n this.currentAbortController = undefined;\r\n }\r\n }\r\n\r\n /**\r\n * Embed file descriptions into conversation history messages.\r\n * When the backend generates a new file description (non-Gemini path),\r\n * it sends a file_descriptions event. We find messages in conversation history\r\n * that contain the matching [FILE: name (gs://...)] marker and append a\r\n * [FILE_DESCRIPTION_CACHE: gs://...] block next to it. On subsequent turns,\r\n * the backend will parse these blocks and skip re-describing the file.\r\n */\r\n private embedFileDescriptionsInHistory(\r\n descriptions: Record<string, { description: string; filename: string; mimeType: string }>\r\n ): void {\r\n const gcsUris = Object.keys(descriptions);\r\n if (gcsUris.length === 0) return;\r\n\r\n try {\r\n quickLog(`[${new Date().toISOString()}] [CLI] Embedding ${gcsUris.length} file description(s) into chat history\\n`);\r\n } catch (e) { }\r\n\r\n for (const gcsUri of gcsUris) {\r\n const { description } = descriptions[gcsUri];\r\n const cacheBlock = `\\n[FILE_DESCRIPTION_CACHE: ${gcsUri}]${description}[/FILE_DESCRIPTION_CACHE]`;\r\n\r\n // Find the message in history that contains this GCS URI marker\r\n for (let i = 0; i < this.conversationHistory.length; i++) {\r\n const msg = this.conversationHistory[i];\r\n if (typeof msg.content !== 'string') continue;\r\n\r\n // Check if this message has the file marker for this gcsUri and doesn't already have a cache block\r\n if (msg.content.includes(gcsUri) && !msg.content.includes(`[FILE_DESCRIPTION_CACHE: ${gcsUri}]`)) {\r\n this.conversationHistory[i] = {\r\n ...msg,\r\n content: msg.content + cacheBlock,\r\n };\r\n try {\r\n quickLog(`[${new Date().toISOString()}] [CLI] Embedded description for ${gcsUri} in message index ${i}\\n`);\r\n } catch (e) { }\r\n break; // Each URI should only appear in one message\r\n }\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Clean up orphaned tool_calls from conversation history.\r\n * This validates the ENTIRE history and removes any assistant messages \r\n * where tool_calls don't have matching tool result messages.\r\n * \r\n * Vertex AI / Claude APIs require that every assistant message with tool_calls\r\n * has matching tool result messages immediately following it.\r\n */\r\n private cleanupOrphanedToolCalls(): void {\r\n if (this.conversationHistory.length === 0) return;\r\n\r\n let cleanedAny = false;\r\n let iterations = 0;\r\n const maxIterations = 20; // Safety limit to prevent infinite loops\r\n\r\n // Keep cleaning until no more orphans are found\r\n // (removing one orphan may expose another)\r\n while (iterations < maxIterations) {\r\n iterations++;\r\n let foundOrphan = false;\r\n\r\n // Scan through history to find ALL assistant messages with tool_calls\r\n for (let i = 0; i < this.conversationHistory.length; i++) {\r\n const msg = this.conversationHistory[i];\r\n\r\n if (msg.role !== 'assistant' || !msg.tool_calls || msg.tool_calls.length === 0) {\r\n continue;\r\n }\r\n\r\n // Collect all tool_call IDs from this assistant message\r\n const expectedToolCallIds = new Set(msg.tool_calls.map((tc: any) => tc.id));\r\n\r\n // Check if ALL tool_calls have matching tool result messages after this message\r\n // Tool results must come AFTER the assistant message, before the next user/assistant message\r\n let j = i + 1;\r\n while (j < this.conversationHistory.length) {\r\n const nextMsg = this.conversationHistory[j];\r\n\r\n // If we hit a user or assistant message, stop looking for tool results\r\n if (nextMsg.role === 'user' || nextMsg.role === 'assistant') {\r\n break;\r\n }\r\n\r\n // If it's a tool result, check if it matches one of our expected IDs\r\n if (nextMsg.role === 'tool' && nextMsg.tool_call_id) {\r\n expectedToolCallIds.delete(nextMsg.tool_call_id);\r\n }\r\n\r\n j++;\r\n }\r\n\r\n // If there are still unmatched tool_calls, this is an orphan\r\n if (expectedToolCallIds.size > 0) {\r\n try {\r\n quickLog(`[${new Date().toISOString()}] [CLI] Found orphaned tool_calls at index ${i}: ${Array.from(expectedToolCallIds).join(', ')}\\n`);\r\n } catch (e) { }\r\n\r\n // Remove this assistant message and all tool results up to (but not including) the next user/assistant message\r\n const removeCount = j - i;\r\n this.conversationHistory.splice(i, removeCount);\r\n\r\n foundOrphan = true;\r\n cleanedAny = true;\r\n break; // Restart scan from beginning since indices changed\r\n }\r\n }\r\n\r\n if (!foundOrphan) {\r\n break; // No more orphans found, we're done\r\n }\r\n }\r\n\r\n if (cleanedAny) {\r\n try {\r\n quickLog(`[${new Date().toISOString()}] [CLI] Completed history cleanup after ${iterations} iteration(s), ${this.conversationHistory.length} messages remaining\\n`);\r\n } catch (e) { }\r\n }\r\n }\r\n\r\n /**\r\n * Strip thinking blocks from ALL assistant messages in the conversation history.\r\n * \r\n * This is called at the START of a new user request to clear thinking from\r\n * previous tasks. During the current agent loop, thinking is preserved for\r\n * all turns to help the AI maintain reasoning context.\r\n * \r\n * Pattern: \r\n * - New user request starts → strip ALL thinking from history\r\n * - During agent loop (multi-turn tool execution) → keep ALL thinking\r\n * - This allows AI to remember reasoning for current task but not old tasks\r\n */\r\n private stripThinkingFromHistory(): void {\r\n for (let i = 0; i < this.conversationHistory.length; i++) {\r\n const msg = this.conversationHistory[i];\r\n if (msg.role === 'assistant' && msg.thinking) {\r\n // Remove thinking from the message in place\r\n delete msg.thinking;\r\n }\r\n }\r\n }\r\n\r\n private notifyCommandQueueUpdate(): void {\r\n if (this.onCommandQueueUpdateCallback) {\r\n this.onCommandQueueUpdateCallback([...this.sessionCommandQueue]);\r\n }\r\n }\r\n\r\n private canProcessSessionCommandQueue(): boolean {\r\n return !this.currentAbortController\r\n && this.interruptQueue.length === 0\r\n && !this.isReconnecting\r\n && !this.isSessionTransitioning;\r\n }\r\n\r\n private enqueueSessionCommand(command: string): void {\r\n const trimmedCommand = command.trim();\r\n if (!trimmedCommand) {\r\n return;\r\n }\r\n\r\n this.sessionCommandQueue.push(trimmedCommand);\r\n this.notifyCommandQueueUpdate();\r\n void this.maybeProcessSessionCommandQueue();\r\n }\r\n\r\n private async maybeProcessSessionCommandQueue(): Promise<void> {\r\n if (this.isProcessingSessionCommandQueue || !this.canProcessSessionCommandQueue()) {\r\n return;\r\n }\r\n\r\n const nextCommand = this.sessionCommandQueue.shift();\r\n if (!nextCommand) {\r\n return;\r\n }\r\n\r\n this.notifyCommandQueueUpdate();\r\n this.isProcessingSessionCommandQueue = true;\r\n\r\n try {\r\n if (this.onQueuedCommandDispatchedCallback) {\r\n this.onQueuedCommandDispatchedCallback(nextCommand);\r\n }\r\n\r\n await this.handleCommandModeExecution(nextCommand);\r\n } finally {\r\n this.isProcessingSessionCommandQueue = false;\r\n\r\n if (this.canProcessSessionCommandQueue() && this.sessionCommandQueue.length > 0) {\r\n setTimeout(() => {\r\n void this.maybeProcessSessionCommandQueue();\r\n }, 0);\r\n }\r\n }\r\n }\r\n\r\n async handleMessage(message: string, options: { isolatedWorkflow?: boolean; interruptCleanText?: string } = {}): Promise<void> {\r\n // Prevent messages from being processed while remote reconnection is ongoing\r\n if (this.isReconnecting) {\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback('⏳ Please wait until the chat is fully reconnected before sending a message.');\r\n }\r\n return;\r\n }\r\n\r\n // Handle command mode - execute commands directly\r\n if (this.commandMode) {\r\n // Record command step if workflow learning mode is active\r\n if (this.workflowLearningActive) {\r\n this.recordWorkflowStep('command', message);\r\n }\r\n this.enqueueSessionCommand(message);\r\n return;\r\n }\r\n\r\n // Handle background mode - execute commands in background\r\n if (this.backgroundMode) {\r\n // Record command step if workflow learning mode is active\r\n if (this.workflowLearningActive) {\r\n this.recordWorkflowStep('command', message);\r\n }\r\n this.handleBackgroundModeExecution(message);\r\n return;\r\n }\r\n\r\n // Handle slash commands\r\n if (message.startsWith('/')) {\r\n await this.handleSlashCommand(message);\r\n return;\r\n }\r\n\r\n const {\r\n augmentedPrompt,\r\n referencedRules,\r\n missingRuleNames\r\n } = resolveRuleReferences(message);\r\n const resolvedInterruptCleanText = options.interruptCleanText\r\n ? resolveRuleReferences(options.interruptCleanText).augmentedPrompt\r\n : undefined;\r\n\r\n if (referencedRules.length > 0) {\r\n quickLog(\r\n `[${new Date().toISOString()}] [Rules] Injected ${referencedRules.length} rule(s): ${referencedRules.map(rule => rule.name).join(', ')}\\n`\r\n );\r\n }\r\n\r\n if (missingRuleNames.length > 0) {\r\n quickLog(\r\n `[${new Date().toISOString()}] [Rules] Missing referenced rules: ${missingRuleNames.join(', ')}\\n`\r\n );\r\n }\r\n\r\n const resolvedMessage = augmentedPrompt.replace(/@([^\\s@]+)/g, (match, relPath) => {\r\n // @rules: references are already handled by resolveRuleReferences above\r\n if (relPath.startsWith('rules:')) return match;\r\n // Resolve relative file/directory paths to absolute paths for the AI\r\n const absPath = path.resolve(this.cwd, relPath);\r\n if (fs.existsSync(absPath)) return absPath;\r\n return match;\r\n });\r\n\r\n // Record step if workflow learning mode is active\r\n // AI prompts are recorded as instruction steps\r\n if (this.workflowLearningActive) {\r\n this.recordWorkflowStep('instruction', message);\r\n }\r\n\r\n // Check authentication\r\n if (!apiClient.isAuthenticated()) {\r\n throw new Error('Authentication required. Please sign in to use AI features.');\r\n }\r\n\r\n // Check session quota before making any AI request\r\n if (!sessionQuotaManager.canSendMessage()) {\r\n const timeRemaining = sessionQuotaManager.getFormattedTimeRemaining();\r\n const message = `\\n⚠️ Session quota reached. You have used all ${sessionQuotaManager.getCurrentConfig().maxMessagesPerSession} messages for this session.\\n\\nYour quota will reset in ${timeRemaining}.\\n\\nYou can still use:\\n • Slash commands (e.g., /help, /session-limits, /exit)\\n • Terminal commands (in Command mode)\\n\\nUse /session-limits to check your quota status.`;\r\n\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(message);\r\n }\r\n\r\n // Notify UI about quota status\r\n this.notifySessionQuotaStatus();\r\n return;\r\n }\r\n\r\n // Check context window limit before accepting new messages\r\n // Get current model's context window\r\n const currentModel = this.configManager.get('modelName') || 'gemini-2.5-flash';\r\n const { getModelContextWindowSync } = await import('./config/models.js');\r\n const maxTokens = getModelContextWindowSync(currentModel);\r\n\r\n // Calculate current token usage percentage\r\n // We need to estimate tokens for the new message too\r\n const newMessageChars = resolvedMessage.length;\r\n const estimatedNewMessageTokens = Math.ceil(newMessageChars / 4);\r\n\r\n // Get current token count from state (updated by updateTokenCount)\r\n const currentTokens = this.getCurrentTokenCount();\r\n const projectedTokens = currentTokens + estimatedNewMessageTokens;\r\n const usagePercent = (projectedTokens / maxTokens) * 100;\r\n\r\n // Always allow sending messages. If usage is high, auto-compaction runs first.\r\n this.setContextLimitReachedState(false);\r\n if (usagePercent >= CentaurusCLI.AUTO_COMPACTION_TRIGGER_PERCENT) {\r\n quickLog(\r\n `[${new Date().toISOString()}] [handleMessage] High context before user send (${usagePercent.toFixed(1)}%, ${projectedTokens}/${maxTokens}). Running auto-compaction.\\n`\r\n );\r\n\r\n const compactionOutcome = await this.maybeAutoCompactContext(currentModel, maxTokens);\r\n const refreshedTokens = this.getCurrentTokenCount();\r\n const refreshedProjectedTokens = refreshedTokens + estimatedNewMessageTokens;\r\n const refreshedUsagePercent = this.calculateUsagePercent(refreshedProjectedTokens, maxTokens);\r\n\r\n quickLog(\r\n `[${new Date().toISOString()}] [handleMessage] Pre-send compaction: triggered=${compactionOutcome.triggered}, compacted=${compactionOutcome.compactedCount}, projected usage now ${refreshedUsagePercent.toFixed(1)}% (${refreshedProjectedTokens}/${maxTokens})\\n`\r\n );\r\n }\r\n\r\n // Cancel any active request when a new message comes in\r\n // This enables \"interrupt and replace\" - new message takes priority\r\n if (this.currentAbortController) {\r\n // INSTEAD of aborting, enqueue the message as an interrupt.\r\n const queuedMessage = options.interruptCleanText || message;\r\n this.interruptQueue.push(queuedMessage);\r\n\r\n if (this.onInterruptQueueUpdateCallback) {\r\n this.onInterruptQueueUpdateCallback(this.interruptQueue);\r\n }\r\n\r\n quickLog(`[${new Date().toISOString()}] [handleMessage] Added message to interrupt queue. Queue length: ${this.interruptQueue.length}\\n`);\r\n return;\r\n }\r\n\r\n\r\n // Store original request if in planning mode (for execution phase after approval)\r\n if (this.planMode && !this.pendingPlanRequest) {\r\n this.pendingPlanRequest = resolvedMessage;\r\n }\r\n\r\n // Notify UI that this message is now actively being processed.\r\n // Only fire for DIRECT (non-queued) messages here.\r\n // For queued interrupts, the finally block fires this BEFORE calling handleMessage,\r\n // so we must not fire it again here — that would cause the user bubble to appear twice.\r\n if (!options.interruptCleanText && this.onQueuedMessageDispatchedCallback) {\r\n this.onQueuedMessageDispatchedCallback(message);\r\n }\r\n\r\n // Build the user message content - inject plan mode instructions if active\r\n let userMessageContent = resolvedMessage;\r\n\r\n // When plan mode is active, explicitly inject planning instructions into the user message\r\n // This ensures the AI creates a plan even when toggled mid-conversation\r\n if (this.planMode && !getCurrentPlan()?.isActive) {\r\n const planModePrefix = `[PLAN MODE ACTIVE - You MUST call create_plan first before any implementation tools]\r\n\r\nUser Request: ${resolvedMessage}\r\n\r\nCRITICAL INSTRUCTIONS:\r\n1. You are in PLANNING MODE - DO NOT implement anything directly\r\n2. First explore the codebase using view_file, list_dir, grep_search to understand the context\r\n3. Then call create_plan with a detailed plan including:\r\n - designSummary: Your understanding of the codebase\r\n - tasks: Hierarchical tasks with subtasks\r\n4. Wait for user approval before implementing\r\n\r\nDO NOT use write_to_file, edit_file, or execute_command until the plan is approved.`;\r\n userMessageContent = planModePrefix;\r\n }\r\n\r\n // NEW USER REQUEST: Strip thinking from previous tasks\r\n // This clears thinking from the previous agent loop but thinking will be\r\n // preserved for all turns within the current agent loop\r\n this.stripThinkingFromHistory();\r\n\r\n // Add user message to history, using clean text if it's an interrupt\r\n const messageToStore = resolvedInterruptCleanText || userMessageContent;\r\n this.conversationHistory.push({\r\n role: 'user',\r\n content: messageToStore,\r\n });\r\n\r\n // Capture the abort controller for THIS request locally so we are entirely immune to race conditions\r\n if (!this.currentAbortController) {\r\n this.currentAbortController = new AbortController();\r\n }\r\n const myAbortController = this.currentAbortController;\r\n\r\n // Calculate start index for AI context (0 for normal, current index for isolated workflow)\r\n const contextStartIndex = options.isolatedWorkflow ? this.conversationHistory.length - 1 : 0;\r\n\r\n // Helper to get messages for AI context respecting isolation\r\n const getMessagesForContext = () => {\r\n const msgs = options.isolatedWorkflow ? this.conversationHistory.slice(contextStartIndex) : [...this.conversationHistory];\r\n\r\n // If this is an interrupt, we must ensure the AI sees the wrapped message for this specific turn\r\n if (options.interruptCleanText && msgs.length > 0) {\r\n // Swap the last user message's content with the wrapped message just for the AI request\r\n const lastMsg = msgs[msgs.length - 1];\r\n if (lastMsg.role === 'user') {\r\n // We map over to not mutate the original history array object\r\n msgs[msgs.length - 1] = { ...lastMsg, content: userMessageContent };\r\n }\r\n }\r\n return msgs;\r\n };\r\n\r\n // Messages are stored locally only - no backend persistence needed\r\n // Local storage is handled by saveCurrentChat() which saves to ~/.centaurus/chats/\r\n\r\n // Start logging session and log user message\r\n conversationLogger.startSession();\r\n conversationLogger.logUserMessage(message);\r\n\r\n // Initialize and start checkpoint for revert functionality\r\n if (!this.checkpointManager) {\r\n this.checkpointManager = new CheckpointManager();\r\n }\r\n\r\n // Ensure we have a chat ID for checkpoints (even for the first message)\r\n if (!this.currentChatId) {\r\n this.currentChatId = localChatStorage.generateChatId();\r\n }\r\n\r\n if (this.currentChatId) {\r\n this.ensureCheckpointManagerForChat(this.currentChatId);\r\n this.currentCheckpointId = undefined;\r\n // Start checkpoint snapshot for this user message\r\n const currentContext = this.contextManager.getCurrentContext();\r\n\r\n // Build remote session info and handler for non-local contexts\r\n let remoteSessionInfo: RemoteSessionInfo | undefined;\r\n let remoteHandler: RemoteFileHandler | undefined;\r\n if (currentContext.type !== 'local' && currentContext.handler) {\r\n const metadata = currentContext.metadata;\r\n remoteSessionInfo = {\r\n hostname: metadata.hostname,\r\n username: metadata.username,\r\n sessionId: currentContext.sessionId,\r\n connectionString: metadata.hostname\r\n ? `${metadata.username || 'user'}@${metadata.hostname}`\r\n : metadata.distroName || metadata.containerId || undefined,\r\n };\r\n // Use the handler if it implements the RemoteFileHandler interface\r\n if (typeof currentContext.handler.readFile === 'function' &&\r\n typeof currentContext.handler.writeFile === 'function' &&\r\n typeof currentContext.handler.executeCommand === 'function' &&\r\n typeof currentContext.handler.isConnected === 'function') {\r\n remoteHandler = currentContext.handler as RemoteFileHandler;\r\n }\r\n }\r\n\r\n try {\r\n const checkpointPrompt = options.interruptCleanText || message;\r\n const checkpointStartPromise = this.checkpointManager.startCheckpoint({\r\n prompt: checkpointPrompt,\r\n cwd: this.cwd,\r\n contextType: currentContext.type as 'local' | 'ssh' | 'wsl' | 'docker',\r\n conversationIndex: this.conversationHistory.length - 1,\r\n uiMessageIndex: this.uiMessageHistory.length,\r\n remoteSessionInfo,\r\n handler: remoteHandler,\r\n });\r\n let checkpoint: CheckpointMeta | null = null;\r\n\r\n if (currentContext.type !== 'local') {\r\n const remoteCheckpointTimeoutMs = 2000;\r\n const timeoutMarker = '__checkpoint_timeout__' as const;\r\n let timeoutHandle: NodeJS.Timeout | null = null;\r\n\r\n const timeoutPromise: Promise<typeof timeoutMarker> = new Promise((resolve) => {\r\n timeoutHandle = setTimeout(() => resolve(timeoutMarker), remoteCheckpointTimeoutMs);\r\n });\r\n\r\n const checkpointOrTimeout = await Promise.race([\r\n checkpointStartPromise,\r\n timeoutPromise,\r\n ]);\r\n\r\n if (timeoutHandle) {\r\n clearTimeout(timeoutHandle);\r\n }\r\n\r\n if (checkpointOrTimeout === timeoutMarker) {\r\n quickLog(\r\n `[${new Date().toISOString()}] [Checkpoint] Remote checkpoint start exceeded ${remoteCheckpointTimeoutMs}ms (${currentContext.type}); continuing without blocking AI turn\\n`\r\n );\r\n\r\n void checkpointStartPromise\r\n .then((lateCheckpoint) => {\r\n if (!lateCheckpoint || !this.checkpointManager) {\r\n return;\r\n }\r\n\r\n // Keep the late checkpoint — it's still valid for diff/revert\r\n // The AI turn was already started without blocking, but this checkpoint\r\n // data will be available for any subsequent get_diff or /revert operations\r\n this.currentCheckpointId = lateCheckpoint.id;\r\n quickLog(\r\n `[${new Date().toISOString()}] [Checkpoint] Late checkpoint ${lateCheckpoint.id} resolved after timeout — keeping for diff/revert\\n`\r\n );\r\n })\r\n .catch((lateError: any) => {\r\n quickLog(\r\n `[${new Date().toISOString()}] [Checkpoint] Late checkpoint creation failed after timeout: ${lateError?.message || lateError}\\n`\r\n );\r\n });\r\n } else {\r\n checkpoint = checkpointOrTimeout;\r\n }\r\n } else {\r\n checkpoint = await checkpointStartPromise;\r\n }\r\n\r\n if (checkpoint) {\r\n this.currentCheckpointId = checkpoint.id;\r\n quickLog(`[${new Date().toISOString()}] [Checkpoint] Started checkpoint ${checkpoint.id} (${currentContext.type}) for: \"${message.slice(0, 50)}...\"\\n`);\r\n }\r\n } catch (error: any) {\r\n quickLog(`[${new Date().toISOString()}] [Checkpoint] Failed to start checkpoint: ${error.message}\\n`);\r\n }\r\n }\r\n\r\n try {\r\n const tools = this.toolRegistry.getSchemas();\r\n const context = {\r\n cwd: this.cwd,\r\n contextManager: this.contextManager,\r\n cliAdapter: this, // Pass CLI adapter reference for interactive process management\r\n checkpointManager: this.checkpointManager, // For session-aware diff tool\r\n currentCheckpointId: this.currentCheckpointId, // Active checkpoint for this request\r\n currentChatId: this.currentChatId || undefined, // Current chat session ID (for session-wide diffs)\r\n requireApproval: async (message: string, risky: boolean, preview?: { type: 'code' | 'diff'; content: string; language?: string; fullDiff?: string }, operationType?: 'write_file' | 'edit_file' | 'execute_command', operationDetails?: Record<string, any>) => {\r\n // Special bypass for shell input to running processes:\r\n // If the AI is sending input to an existing shell (via shell_input), we bypass the separate approval step.\r\n // The user already implicitly approved the interaction by running the command in agent control mode.\r\n if (operationType === 'execute_command' && operationDetails?.shell_input) {\r\n return true;\r\n }\r\n\r\n if (this.onToolApprovalRequest) {\r\n return await this.onToolApprovalRequest({ message, risky, preview, operationType, operationDetails });\r\n }\r\n return true; // Auto-approve if no callback registered\r\n },\r\n onStreamingOutput: (chunk: string, type: 'stdout' | 'stderr', toolName?: string) => {\r\n // Forward streaming output to UI callback\r\n if (this.onToolStreamingOutput) {\r\n this.onToolStreamingOutput({ toolName: toolName || 'execute_command', chunk, type });\r\n }\r\n },\r\n };\r\n\r\n // Get selected model ID from config, then look up full config from models-config.json\r\n const config = this.configManager.load();\r\n const selectedModelId = config.model || 'gemini-2.5-flash';\r\n\r\n // Look up the full model config (including thinkingConfig) from backend\r\n // This ensures we always use the latest config\r\n const selectedModelConfig = await getModelConfigByIdAndName(selectedModelId, config.modelName || '');\r\n const selectedModel = selectedModelId;\r\n const selectedModelThinkingConfig = selectedModelConfig?.thinkingConfig;\r\n const modelContextWindow = selectedModelConfig?.contextWindow || maxTokens;\r\n\r\n // Build messages array WITHOUT system prompt - backend will inject it\r\n // The backend uses cli-system-prompt.md for CLI clients\r\n // We pass environmentContext and mode separately so backend can inject them\r\n\r\n // SAFETY: Clean up any orphaned tool calls before making AI request\r\n // This prevents \"improperly formed request\" errors from corrupted history\r\n this.cleanupOrphanedToolCalls();\r\n\r\n let messages: AIMessage[] = getMessagesForContext();\r\n\r\n // Inject subshell context if in a subshell environment\r\n const currentContext = this.contextManager.getCurrentContext();\r\n messages = this.aiContextInjector.injectSubshellContext(messages, currentContext);\r\n\r\n // Inject MCP auto-provisioning context so the AI knows it can dynamically add MCP servers\r\n const connectedMcpServers = this.getConnectedMCPServerNames();\r\n messages = this.aiContextInjector.injectMCPContext(messages, connectedMcpServers);\r\n\r\n // Build environment context to send to backend\r\n // Initial context - will be updated in loop\r\n let environmentContext = this.getEnvironmentContext();\r\n const mode = this.getMode();\r\n\r\n let finalAssistantMessage = '';\r\n const MAX_TURNS = 500; // Allow up to 500 turns for complex tasks\r\n let turnCount = 0;\r\n let thoughtStartTime: number | null = null; // Track when thinking started\r\n let thoughtContent = ''; // Accumulate thought content during streaming\r\n let currentTurnThinking = ''; // Persist thinking for the current turn to attach to assistant message\r\n let currentTurnThinkingSignature = ''; // Persist thinking signature for Claude extended thinking\r\n\r\n // ANTI-LOOP: Track duplicate tool calls to detect infinite loops\r\n const MAX_DUPLICATE_CALLS = 2; // Max times same operation allowed on same target\r\n const fileWriteTracker: Map<string, number> = new Map(); // Track writes per file\r\n const recentToolCalls: Array<{ name: string; targetFile: string; turn: number }> = [];\r\n\r\n // ANTI-LOOP: Track ALL duplicate tool calls (not just file ops)\r\n const toolCallTracker: Map<string, number> = new Map(); // Hash -> count\r\n const MAX_IDENTICAL_TOOL_CALLS = 3; // Max times exact same tool call allowed\r\n\r\n // Create AbortController for this request (if not already created during interruption handling)\r\n if (!this.currentAbortController) {\r\n this.currentAbortController = new AbortController();\r\n }\r\n\r\n // Clean up any orphaned tool_calls from a previous aborted request\r\n // This prevents 400 Bad Request errors when sending to the backend\r\n this.cleanupOrphanedToolCalls();\r\n\r\n // Multi-turn tool execution loop\r\n while (turnCount < MAX_TURNS) {\r\n turnCount++;\r\n\r\n // Track session quota - each AI call in the agent loop counts as 1 message\r\n sessionQuotaManager.incrementMessageCount();\r\n this.notifySessionQuotaStatus();\r\n\r\n // Check if session quota is now exhausted after incrementing\r\n if (!sessionQuotaManager.canSendMessage() && turnCount > 1) {\r\n // Quota exhausted mid-loop, stop and inform user\r\n const timeRemaining = sessionQuotaManager.getFormattedTimeRemaining();\r\n const quotaMessage = `\\n\\n⚠️ **Session quota reached** during agent execution.\\n\\nYou have used all ${sessionQuotaManager.getCurrentConfig().maxMessagesPerSession} messages for this session.\\nQuota will reset in ${timeRemaining}.\\n\\nYour current task may be incomplete. You can resume when your quota resets.\\n\\nUse /session-limits to check your quota status.`;\r\n\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback(quotaMessage);\r\n }\r\n\r\n logWarning('Agent loop stopped due to session quota exhaustion');\r\n return;\r\n }\r\n\r\n // Refresh environment context to capture any CWD changes from previous turns\r\n // This is sent to backend which will inject it into the system prompt\r\n environmentContext = this.getEnvironmentContext();\r\n\r\n let assistantMessage = '';\r\n let toolCalls: any[] = [];\r\n\r\n // REAL-TIME TOOL EXECUTION: Track execution state and results during streaming\r\n const inStreamToolResults: any[] = []; // Results from tools executed during streaming\r\n const inStreamHandledIds = new Set<string>(); // IDs of tools already executed in-stream\r\n let toolsExecutedInStream = false; // Flag to indicate tools were executed during stream\r\n let pendingTextBuffer = ''; // Buffer for text while tool is executing\r\n let isToolExecuting = false; // Flag to pause text streaming during tool execution\r\n\r\n // DEBUG: Log message history state before AI call\r\n const messageStats = {\r\n totalMessages: messages.length,\r\n totalCharacters: messages.reduce((sum, m) => {\r\n let len = typeof m.content === 'string' ? m.content.length : 0;\r\n // Include thinking content (internal reasoning)\r\n if (m.thinking) {\r\n len += m.thinking.length;\r\n }\r\n // Include tool calls (name + arguments)\r\n if (m.tool_calls) {\r\n m.tool_calls.forEach(tc => {\r\n len += tc.name.length;\r\n // Arguments are JSON stringified in the payload\r\n if (tc.arguments) {\r\n len += JSON.stringify(tc.arguments).length;\r\n }\r\n });\r\n }\r\n // Include tool_call_id for tool messages\r\n if (m.role === 'tool' && m.tool_call_id) {\r\n len += m.tool_call_id.length;\r\n }\r\n return sum + len;\r\n }, 0),\r\n byRole: {\r\n system: messages.filter(m => m.role === 'system').length,\r\n user: messages.filter(m => m.role === 'user').length,\r\n assistant: messages.filter(m => m.role === 'assistant').length,\r\n tool: messages.filter(m => m.role === 'tool').length\r\n },\r\n assistantWithToolCalls: messages.filter(m => m.role === 'assistant' && m.tool_calls && m.tool_calls.length > 0).length\r\n };\r\n try {\r\n quickLog(`[${new Date().toISOString()}] [CLI] === TURN ${turnCount} AI CALL ===\\n`);\r\n quickLog(`[${new Date().toISOString()}] [CLI] Message history: ${messageStats.totalMessages} messages, ${messageStats.totalCharacters} chars\\n`);\r\n quickLog(`[${new Date().toISOString()}] [CLI] By role: system=${messageStats.byRole.system}, user=${messageStats.byRole.user}, assistant=${messageStats.byRole.assistant}, tool=${messageStats.byRole.tool}\\n`);\r\n quickLog(`[${new Date().toISOString()}] [CLI] Assistant messages with tool_calls: ${messageStats.assistantWithToolCalls}\\n`);\r\n } catch (e) { }\r\n\r\n // Update token count using accurate API\r\n // This will use backend's Vertex AI countTokens for precision\r\n this.updateTokenCount().catch(err => {\r\n quickLog(`[${new Date().toISOString()}] [CLI] Failed to update token count: ${err}\\n`);\r\n });\r\n\r\n // Stream AI response from backend\r\n // Backend will inject system prompt automatically with environment context\r\n for await (const chunk of aiServiceClient.streamChat(selectedModel, messages, tools, environmentContext, mode, selectedModelThinkingConfig, myAbortController.signal, config.modelName || '', selectedModelConfig?.uid || config.modelUid || '')) {\r\n // Handle file_descriptions chunks — embed descriptions into conversation history\r\n // so they are sent back on subsequent turns and the backend can skip re-describing.\r\n if (chunk.type === 'file_descriptions') {\r\n this.embedFileDescriptionsInHistory(chunk.descriptions);\r\n continue;\r\n }\r\n\r\n // Handle error chunks\r\n if (chunk.type === 'error') {\r\n // Check if this is an abort situation (user cancelled or sent new message)\r\n if (chunk.code === 'TIMEOUT' && (myAbortController as any).isIntentionalAbort) {\r\n if (!(myAbortController as any).isReplacement) {\r\n // Clean up orphaned tool_calls from conversation history only if NOT a replacement.\r\n // If it's a replacement, the newer handleMessage already cleaned up before starting.\r\n this.cleanupOrphanedToolCalls();\r\n }\r\n // Gracefully exit - request was intentionally cancelled\r\n return;\r\n }\r\n conversationLogger.logError('AI Stream', new Error(chunk.message));\r\n throw new Error(chunk.message);\r\n }\r\n\r\n // Handle thought chunks (internal reasoning)\r\n if (chunk.type === 'thought') {\r\n conversationLogger.logThoughtChunk(chunk.content);\r\n\r\n if (!thoughtStartTime) {\r\n thoughtStartTime = Date.now();\r\n }\r\n thoughtContent += chunk.content;\r\n\r\n // Send thought to UI callback if available\r\n if (this.onThoughtStreamCallback) {\r\n this.onThoughtStreamCallback(chunk.content);\r\n }\r\n continue;\r\n }\r\n\r\n // Handle thinking_signature chunks (Claude extended thinking)\r\n if (chunk.type === 'thinking_signature') {\r\n // Store the signature for this turn - it must be passed back with thinking content\r\n currentTurnThinkingSignature = chunk.signature;\r\n continue;\r\n }\r\n\r\n // Handle text chunks\r\n if (chunk.type === 'text') {\r\n // If we were thinking and now got text, finalize the thought\r\n if (thoughtStartTime) {\r\n const thinkingDuration = Math.round((Date.now() - thoughtStartTime) / 1000);\r\n conversationLogger.logThoughtComplete(thinkingDuration);\r\n if (this.onThoughtCompleteCallback) {\r\n this.onThoughtCompleteCallback(thinkingDuration);\r\n }\r\n // Capture thinking for this turn before clearing\r\n currentTurnThinking = thoughtContent;\r\n thoughtStartTime = null;\r\n thoughtContent = '';\r\n }\r\n\r\n // Filter out <TASK_COMPLETE> markers (AI shouldn't output these)\r\n let filteredContent = chunk.content;\r\n filteredContent = filteredContent.replace(/<\\/?TASK_COMPLETE>/gi, '');\r\n filteredContent = filteredContent.trim();\r\n\r\n if (filteredContent) {\r\n assistantMessage += filteredContent;\r\n conversationLogger.logAITextChunk(filteredContent);\r\n\r\n // REAL-TIME TOOL EXECUTION: If a tool is executing, accumulate text\r\n // This text will be flushed after the tool completes\r\n if (isToolExecuting) {\r\n pendingTextBuffer += filteredContent;\r\n } else {\r\n // Normal streaming - send chunk to UI in real-time\r\n if (this.onResponseStreamCallback) {\r\n this.onResponseStreamCallback(filteredContent);\r\n }\r\n }\r\n }\r\n }\r\n\r\n // Handle tool call chunks\r\n if (chunk.type === 'tool_call') {\r\n const toolCall = chunk.toolCall;\r\n\r\n // Kiro/Claude compatibility: Parse string arguments early so they are objects throughout the pipeline\r\n // This ensures logging, UI updates, and tool execution all see the parsed object\r\n if (toolCall.arguments && typeof toolCall.arguments === 'string') {\r\n try {\r\n toolCall.arguments = JSON.parse(toolCall.arguments);\r\n } catch (e) {\r\n // Ignore parsing error, will be handled by downstream logic\r\n }\r\n }\r\n\r\n // Debug: Log every tool_call chunk received\r\n try {\r\n quickLog(`[${new Date().toISOString()}] [CLI] *** TOOL_CALL CHUNK RECEIVED (REAL-TIME): ${toolCall?.name || 'unknown'}\\n`);\r\n } catch (e) { }\r\n\r\n conversationLogger.logToolCall(toolCall?.name || 'unknown', toolCall?.id || 'unknown', toolCall?.arguments || {});\r\n\r\n // If we were thinking and now got a tool call, finalize the thought\r\n if (thoughtStartTime) {\r\n const thinkingDuration = Math.round((Date.now() - thoughtStartTime) / 1000);\r\n conversationLogger.logThoughtComplete(thinkingDuration);\r\n if (this.onThoughtCompleteCallback) {\r\n this.onThoughtCompleteCallback(thinkingDuration);\r\n }\r\n // Capture thinking for this turn before clearing\r\n currentTurnThinking = thoughtContent;\r\n thoughtStartTime = null;\r\n thoughtContent = '';\r\n }\r\n toolCalls.push(chunk.toolCall);\r\n\r\n // SPECIAL TOOLS: Skip in-stream execution for tools that need post-stream handling\r\n // These tools have special logic (setting flags, clearing state, etc.) that must run post-stream\r\n const SPECIAL_TOOLS = ['task_complete', 'create_plan', 'mark_task_complete'];\r\n if (SPECIAL_TOOLS.includes(toolCall.name)) {\r\n // Just notify UI with pending status, execute in post-stream loop\r\n if (this.onToolExecutionUpdate) {\r\n this.onToolExecutionUpdate({\r\n toolName: toolCall.name,\r\n status: 'pending',\r\n arguments: toolCall.arguments\r\n });\r\n }\r\n continue; // Skip to next chunk, handle this tool in post-stream loop\r\n }\r\n\r\n // Mark that we're executing a tool (text will accumulate)\r\n isToolExecuting = true;\r\n toolsExecutedInStream = true;\r\n\r\n // REAL-TIME EXECUTION: Execute tool immediately during streaming\r\n // This reduces latency by not waiting for the entire stream to finish\r\n try {\r\n\r\n\r\n // Extract and display reason_text if present (skip for task_complete and shell_input)\r\n const reasonText = toolCall.arguments.reason_text;\r\n // Don't show reason text for shell inputs (hidden from history per user request)\r\n const isShellInput = toolCall.name === 'execute_command' && toolCall.arguments.shell_input;\r\n\r\n if (reasonText && !isShellInput && this.onResponseStreamCallback) {\r\n this.onResponseStreamCallback(reasonText + '\\n\\n');\r\n }\r\n\r\n // Show 'executing' status immediately\r\n this.notifyToolStatus(toolCall.name, 'executing', toolCall.arguments);\r\n\r\n // Log tool execution start\r\n conversationLogger.logToolExecutionStart(toolCall.name, toolCall.id);\r\n\r\n // Execute the tool (it will request approval if needed via requireApproval callback)\r\n // SPECIAL: Intercept sub_agent spawn to enforce approval\r\n if (toolCall.name === 'sub_agent' && toolCall.arguments?.action === 'spawn') {\r\n const approved = await context.requireApproval(\r\n `Spawn Sub-Agent`,\r\n true, // risky\r\n undefined,\r\n 'execute_command',\r\n { command: `spawn sub-agent` }\r\n );\r\n if (!approved) {\r\n throw new Error('User rejected sub-agent spawn request');\r\n }\r\n }\r\n\r\n const result = await this.toolRegistry.execute(toolCall.name, toolCall.arguments, context);\r\n\r\n if (result.success) {\r\n conversationLogger.logToolResult(toolCall.name, toolCall.id, result.result, true);\r\n\r\n // Notify UI: tool succeeded\r\n this.notifyToolStatus(toolCall.name, 'completed', toolCall.arguments, result.result);\r\n\r\n // Update live file change breadcrumb after file-modifying tools\r\n if (['write_to_file', 'edit_file', 'multi_edit_file'].includes(toolCall.name)) {\r\n this.updateFileChangeSummary();\r\n }\r\n\r\n // Parse and truncate result for AI\r\n let parsedResult = result.result;\r\n if (typeof result.result === 'string') {\r\n try {\r\n parsedResult = JSON.parse(result.result);\r\n } catch {\r\n parsedResult = result.result;\r\n }\r\n }\r\n\r\n // Sanitize context for AI consumption (strip ANSI, compact file writes)\r\n const sanitizedResult = sanitizeForContext(toolCall.name, parsedResult, toolCall.arguments);\r\n\r\n // Log the sanitized version for debugging purposes\r\n conversationLogger.logToolResult(`${toolCall.name} (SANITIZED_CONTEXT)`, toolCall.id, sanitizedResult, true);\r\n\r\n inStreamToolResults.push({\r\n tool_call_id: toolCall.id,\r\n name: toolCall.name,\r\n result: this.truncateResult(sanitizedResult),\r\n });\r\n } else {\r\n conversationLogger.logToolResult(toolCall.name, toolCall.id, null, false, result.error);\r\n\r\n // Notify UI: tool failed\r\n this.notifyToolStatus(toolCall.name, 'error', toolCall.arguments, undefined, result.error);\r\n\r\n inStreamToolResults.push({\r\n tool_call_id: toolCall.id,\r\n name: toolCall.name,\r\n result: `Error: ${result.error}`,\r\n });\r\n }\r\n\r\n inStreamHandledIds.add(toolCall.id);\r\n } catch (error: any) {\r\n conversationLogger.logError(`Tool execution: ${toolCall.name}`, error);\r\n this.notifyToolStatus(toolCall.name, 'error', toolCall.arguments, undefined, error.message);\r\n\r\n inStreamToolResults.push({\r\n tool_call_id: toolCall.id,\r\n name: toolCall.name,\r\n result: `Error: ${error.message}`,\r\n });\r\n inStreamHandledIds.add(toolCall.id);\r\n }\r\n\r\n // Tool execution complete - flush pending text\r\n isToolExecuting = false;\r\n if (pendingTextBuffer && this.onResponseStreamCallback) {\r\n this.onResponseStreamCallback(pendingTextBuffer);\r\n pendingTextBuffer = '';\r\n }\r\n\r\n // Debug: Log after execution\r\n try {\r\n quickLog(`[${new Date().toISOString()}] [CLI] *** TOOL EXECUTED IN-STREAM: ${toolCall?.name || 'unknown'}\\n`);\r\n } catch (e) { }\r\n }\r\n\r\n // Handle done chunk\r\n if (chunk.type === 'done') {\r\n // If we were thinking and stream ended, finalize the thought\r\n if (thoughtStartTime) {\r\n const thinkingDuration = Math.round((Date.now() - thoughtStartTime) / 1000);\r\n conversationLogger.logThoughtComplete(thinkingDuration);\r\n if (this.onThoughtCompleteCallback) {\r\n this.onThoughtCompleteCallback(thinkingDuration);\r\n }\r\n // Capture thinking for this turn before clearing\r\n currentTurnThinking = thoughtContent;\r\n thoughtStartTime = null;\r\n thoughtContent = '';\r\n }\r\n\r\n // Log AI text completion\r\n conversationLogger.logAITextComplete();\r\n conversationLogger.logStreamEnd('done');\r\n break;\r\n }\r\n } // End of stream loop\r\n\r\n // Log loop state after stream ends\r\n conversationLogger.logLoopState(turnCount, {\r\n toolCallCount: toolCalls.length,\r\n assistantMessageLength: assistantMessage.length,\r\n hasToolCalls: toolCalls.length > 0,\r\n willContinue: toolCalls.length > 0,\r\n });\r\n\r\n // If there are tool calls, execute them\r\n if (toolCalls.length > 0) {\r\n // CRITICAL: AI should ONLY communicate via reason_text and task_complete summary\r\n // Any text output alongside tool calls should be suppressed\r\n // EXCEPTION: If task_complete is in the tool calls, preserve assistantMessage \r\n // because task_complete can have an empty summary and rely on streamed text\r\n const hasTaskComplete = toolCalls.some(tc => tc.name === 'task_complete');\r\n if (assistantMessage && assistantMessage.trim() && !hasTaskComplete) {\r\n // Suppress text output - AI should only use reason_text\r\n assistantMessage = ''; // Clear ALL text output - AI should only use reason_text\r\n }\r\n\r\n // Tool call limit removed - let AI use as many tools as needed per turn\r\n\r\n const toolResults: any[] = [...inStreamToolResults]; // Start with in-stream results\r\n const handledToolCallIds = new Set<string>(); // Only for special tools (create_plan, mark_task_complete)\r\n let userCancelledOperation = false;\r\n let taskCompleted = false;\r\n let taskCompleteSummary = '';\r\n\r\n for (let i = 0; i < toolCalls.length; i++) {\r\n const toolCall = toolCalls[i];\r\n\r\n // REAL-TIME EXECUTION: Skip tools that were already executed in-stream\r\n if (inStreamHandledIds.has(toolCall.id)) {\r\n try {\r\n quickLog(`[${new Date().toISOString()}] [CLI] *** SKIPPING TOOL (already executed in-stream): ${toolCall.name}\\n`);\r\n } catch (e) { }\r\n continue;\r\n }\r\n\r\n // Debug: Log which tool we're about to execute\r\n try {\r\n quickLog(`[${new Date().toISOString()}] [CLI] *** ABOUT TO EXECUTE TOOL [${i + 1}/${toolCalls.length}]: ${toolCalls[i].name}\\n`);\r\n } catch (e) { }\r\n try {\r\n // Check if this is task_complete FIRST (before displaying anything)\r\n if (toolCall.name === 'task_complete') {\r\n // SUBAGENT BLOCKING: Check if any sub-agents are still running\r\n const runningSubAgents = SubAgentManager.getRunningSubAgents();\r\n if (runningSubAgents.length > 0) {\r\n // Block task_complete and provide feedback\r\n const agentIds = runningSubAgents.map(a => a.id).join(', ');\r\n toolResults.push({\r\n tool_call_id: toolCall.id,\r\n name: toolCall.name,\r\n result: `Cannot complete task: ${runningSubAgents.length} sub-agent(s) still running. IDs: ${agentIds}. Check their status periodically with sub_agent(action=\"status\", agent_id=\"...\") and wait for completion before calling task_complete.`,\r\n });\r\n handledToolCallIds.add(toolCall.id);\r\n continue; // Skip task_complete execution, keep loop running\r\n }\r\n\r\n taskCompleted = true;\r\n conversationLogger.logTaskComplete('');\r\n\r\n // task_complete no longer has a summary parameter\r\n // The AI streams all response text BEFORE calling task_complete()\r\n // So we just preserve whatever assistantMessage was already streamed\r\n\r\n // Execute the tool for proper result handling\r\n\r\n\r\n await this.toolRegistry.execute(toolCall.name, toolCall.arguments, context);\r\n\r\n // Clear the plan when task is complete\r\n clearPlan();\r\n\r\n // Stop processing remaining tools\r\n break;\r\n }\r\n\r\n if (toolCall.name === 'create_plan') {\r\n // Execute the tool to create the plan\r\n const execResult = await this.toolRegistry.execute(toolCall.name, toolCall.arguments, context);\r\n // Extract the actual result string (toolRegistry.execute returns { success, result })\r\n const result = execResult.success ? String(execResult.result) : `Error: ${execResult.error}`;\r\n\r\n // Parse the PLAN_CREATED response to get the plan\r\n if (typeof result === 'string' && result.startsWith('PLAN_CREATED:')) {\r\n const planJson = result.substring('PLAN_CREATED:'.length);\r\n try {\r\n const plan = JSON.parse(planJson) as Plan;\r\n\r\n // Notify UI that a plan was created\r\n if (this.onPlanCreated) {\r\n this.onPlanCreated(plan);\r\n }\r\n\r\n // If we have approval callback, ask for approval\r\n if (this.onPlanApprovalRequest) {\r\n const approved = await this.onPlanApprovalRequest(plan);\r\n\r\n if (approved) {\r\n // Approve and activate the plan\r\n approvePlan();\r\n\r\n // Suppress any text output\r\n assistantMessage = '';\r\n\r\n // Switch out of plan mode to execution mode\r\n this.planMode = false;\r\n if (this.onPlanModeChange) {\r\n this.onPlanModeChange(false);\r\n }\r\n\r\n // Add assistant message with plan tool call to history\r\n const planAssistantMsg: AIMessage = {\r\n role: 'assistant',\r\n content: '',\r\n tool_calls: [toolCall],\r\n };\r\n if (currentTurnThinking) {\r\n planAssistantMsg.thinking = currentTurnThinking;\r\n }\r\n if (currentTurnThinkingSignature) {\r\n planAssistantMsg.thinkingSignature = currentTurnThinkingSignature;\r\n }\r\n this.conversationHistory.push(planAssistantMsg);\r\n\r\n // Add plan approval response\r\n this.conversationHistory.push({\r\n role: 'tool',\r\n tool_call_id: toolCall.id,\r\n content: 'Plan approved by user. Now switching to execution mode.',\r\n });\r\n\r\n // Mark this tool call as handled so it's not duplicated\r\n handledToolCallIds.add(toolCall.id);\r\n\r\n // Add user message that includes ONLY current phase context for execution\r\n // This implements phased execution - AI only sees one task at a time\r\n const phaseContext = getPhaseContextForPrompt();\r\n const originalRequest = this.pendingPlanRequest || message;\r\n const executionPrompt = `${phaseContext}\\n\\nOriginal Request: ${originalRequest}\\n\\nComplete the current task. After finishing each subtask, call mark_task_complete with the subtask number (e.g., \"1.1\"). When all subtasks are done, the main task will automatically complete and you'll receive the next task.`;\r\n\r\n this.conversationHistory.push({\r\n role: 'user',\r\n content: executionPrompt,\r\n });\r\n\r\n // Clear pending plan request\r\n this.pendingPlanRequest = null;\r\n\r\n // Update messages array for this turn\r\n messages = getMessagesForContext();\r\n\r\n // Continue the loop - AI will now execute with plan context\r\n continue;\r\n } else {\r\n // User wants to edit - stop the loop, they'll provide feedback\r\n clearPlan();\r\n finalAssistantMessage = 'Plan editing requested. Please provide your feedback or modifications.';\r\n taskCompleted = true;\r\n break;\r\n }\r\n } else {\r\n // No approval callback - add the tool result to history and wait for user response\r\n // This ensures the AI doesn't get stuck in a silent loop\r\n const planAssistantMsg: AIMessage = {\r\n role: 'assistant',\r\n content: '',\r\n tool_calls: [toolCall],\r\n };\r\n if (currentTurnThinking) {\r\n planAssistantMsg.thinking = currentTurnThinking;\r\n }\r\n if (currentTurnThinkingSignature) {\r\n planAssistantMsg.thinkingSignature = currentTurnThinkingSignature;\r\n }\r\n this.conversationHistory.push(planAssistantMsg);\r\n\r\n this.conversationHistory.push({\r\n role: 'tool',\r\n tool_call_id: toolCall.id,\r\n content: `Plan created: \"${plan.title}\" with ${plan.steps.length} tasks. Waiting for user approval.`,\r\n });\r\n\r\n // Mark this tool call as handled so it's not duplicated\r\n handledToolCallIds.add(toolCall.id);\r\n\r\n // Update messages for next iteration\r\n messages = [...this.conversationHistory];\r\n\r\n // Stop and wait for user to approve\r\n finalAssistantMessage = `Plan created: ${plan.title}. Please approve to continue.`;\r\n taskCompleted = true;\r\n break;\r\n }\r\n } catch (parseError: any) {\r\n // Log error and add error result to history so AI knows\r\n logWarning(`Failed to parse plan: ${parseError?.message || parseError}`);\r\n\r\n // CRITICAL: Add tool result even on parse error to prevent silent loop\r\n const errorAssistantMsg: AIMessage = {\r\n role: 'assistant',\r\n content: '',\r\n tool_calls: [toolCall],\r\n };\r\n if (currentTurnThinking) {\r\n errorAssistantMsg.thinking = currentTurnThinking;\r\n }\r\n if (currentTurnThinkingSignature) {\r\n errorAssistantMsg.thinkingSignature = currentTurnThinkingSignature;\r\n }\r\n this.conversationHistory.push(errorAssistantMsg);\r\n\r\n this.conversationHistory.push({\r\n role: 'tool',\r\n tool_call_id: toolCall.id,\r\n content: `Error parsing plan: ${parseError}. Please try again with valid plan format.`,\r\n });\r\n\r\n // Mark this tool call as handled so it's not duplicated\r\n handledToolCallIds.add(toolCall.id);\r\n\r\n messages = getMessagesForContext();\r\n }\r\n } else {\r\n // Tool returned non-PLAN_CREATED result - add it to history\r\n const resultAssistantMsg: AIMessage = {\r\n role: 'assistant',\r\n content: '',\r\n tool_calls: [toolCall],\r\n };\r\n if (currentTurnThinking) {\r\n resultAssistantMsg.thinking = currentTurnThinking;\r\n }\r\n if (currentTurnThinkingSignature) {\r\n resultAssistantMsg.thinkingSignature = currentTurnThinkingSignature;\r\n }\r\n this.conversationHistory.push(resultAssistantMsg);\r\n\r\n this.conversationHistory.push({\r\n role: 'tool',\r\n tool_call_id: toolCall.id,\r\n content: result || 'create_plan executed but returned empty result.',\r\n });\r\n\r\n // Mark this tool call as handled so it's not duplicated\r\n handledToolCallIds.add(toolCall.id);\r\n\r\n messages = getMessagesForContext();\r\n }\r\n continue;\r\n }\r\n\r\n if (toolCall.name === 'mark_task_complete') {\r\n // Execute the tool\r\n const execResult = await this.toolRegistry.execute(toolCall.name, toolCall.arguments, context);\r\n // Extract the actual result string (toolRegistry.execute returns { success, result })\r\n const result = execResult.success ? String(execResult.result) : `Error: ${execResult.error}`;\r\n\r\n // Parse the TASK_COMPLETED response\r\n if (typeof result === 'string' && result.startsWith('TASK_COMPLETED:')) {\r\n const completionJson = result.substring('TASK_COMPLETED:'.length);\r\n try {\r\n const completion = JSON.parse(completionJson);\r\n const currentPlanData = getCurrentPlan();\r\n\r\n if (currentPlanData && this.onTaskCompleted) {\r\n // Handle both main task completion and subtask completion\r\n const taskNumParts = String(completion.taskNumber).split('.');\r\n const mainTaskNum = parseInt(taskNumParts[0], 10) - 1;\r\n const task = currentPlanData.steps[mainTaskNum];\r\n if (task) {\r\n this.onTaskCompleted(\r\n task,\r\n completion.taskNumber,\r\n completion.totalCount,\r\n completion.completionNote,\r\n completion.taskDescription // Pass the actual task/subtask description\r\n );\r\n }\r\n }\r\n\r\n // Notify UI about completed task/subtask\r\n const displayType = completion.type === 'subtask' ? 'Subtask' : 'Task';\r\n this.notifyToolStatus(toolCall.name, 'completed', toolCall.arguments,\r\n `${displayType} ${completion.taskNumber} of ${completion.totalCount} completed: ${completion.taskDescription}`);\r\n\r\n // Check if we need to advance to next phase (when main task is complete)\r\n if (completion.mainTaskComplete || completion.type === 'task') {\r\n // A main task is complete - check if there's a next phase\r\n const nextPhase = getCurrentPhase();\r\n\r\n if (nextPhase && !completion.allComplete) {\r\n // Inject next phase context for the AI\r\n const phaseContext = getPhaseContextForPrompt();\r\n\r\n // Add tool result for current task\r\n toolResults.push({\r\n tool_call_id: toolCall.id,\r\n name: toolCall.name,\r\n result: `Task completed. Moving to Task ${nextPhase.taskNumber}: ${nextPhase.task.description}`,\r\n });\r\n\r\n // Add the tool call and result to history\r\n const nextPhaseAssistantMsg: AIMessage = {\r\n role: 'assistant',\r\n content: '',\r\n tool_calls: [toolCall],\r\n };\r\n if (currentTurnThinking) {\r\n nextPhaseAssistantMsg.thinking = currentTurnThinking;\r\n }\r\n if (currentTurnThinkingSignature) {\r\n nextPhaseAssistantMsg.thinkingSignature = currentTurnThinkingSignature;\r\n }\r\n this.conversationHistory.push(nextPhaseAssistantMsg);\r\n this.conversationHistory.push({\r\n role: 'tool',\r\n tool_call_id: toolCall.id,\r\n content: `Task completed. Now starting Task ${nextPhase.taskNumber}.`,\r\n });\r\n\r\n // Add next phase instructions\r\n this.conversationHistory.push({\r\n role: 'user',\r\n content: `${phaseContext}\\n\\nContinue with the next task. Complete each subtask and call mark_task_complete for each one.`,\r\n });\r\n\r\n // Mark as handled\r\n handledToolCallIds.add(toolCall.id);\r\n\r\n // Update messages and continue\r\n messages = getMessagesForContext();\r\n continue;\r\n }\r\n }\r\n\r\n // Add to tool results for subtask completion or final completion\r\n toolResults.push({\r\n tool_call_id: toolCall.id,\r\n name: toolCall.name,\r\n result: completion.allComplete\r\n ? 'All tasks completed! Output your summary of what was accomplished, then call task_complete().'\r\n : completion.nextSubtask\r\n ? `Subtask ${completion.taskNumber} completed. Next subtask: ${completion.nextSubtask}`\r\n : completion.nextTask\r\n ? `Task ${completion.taskNumber} completed. Next: ${completion.nextTask}`\r\n : `Task ${completion.taskNumber} completed.`,\r\n });\r\n\r\n // If all tasks are complete, prompt AI to call task_complete\r\n if (completion.allComplete) {\r\n toolResults[toolResults.length - 1].result =\r\n 'All tasks in the plan are now completed! Output your summary of what was accomplished, then call task_complete().';\r\n }\r\n } catch (parseError: any) {\r\n logWarning(`Failed to parse task completion: ${parseError?.message || parseError}`);\r\n }\r\n }\r\n continue;\r\n }\r\n\r\n // ANTI-LOOP: Only detect EXACT identical tool calls (same name + same args)\r\n // This is a safety net - the backend fix (functionCall in messages) should prevent loops\r\n // We use a HIGH threshold to avoid blocking legitimate multi-step operations\r\n const toolArgsToTrack = { ...toolCall.arguments };\r\n delete toolArgsToTrack.reason_text; // Ignore reason_text - only matters for params\r\n const toolCallHash = `${toolCall.name}:${JSON.stringify(toolArgsToTrack)}`;\r\n const toolCallCount = (toolCallTracker.get(toolCallHash) || 0) + 1;\r\n toolCallTracker.set(toolCallHash, toolCallCount);\r\n\r\n // Only stop after 5 IDENTICAL calls (same tool + same exact args)\r\n if (toolCallCount > 5) {\r\n // Log the loop detection\r\n conversationLogger.logNarrationDetection('duplicate_tool_loop', {\r\n toolName: toolCall.name,\r\n callCount: toolCallCount,\r\n maxAllowed: 5\r\n });\r\n\r\n // Force task completion with a helpful message\r\n const loopMessage = `⚠️ **Loop Detected**: The AI called \\`${toolCall.name}\\` with identical parameters ${toolCallCount} times.\\n\\n` +\r\n `The system has stopped to prevent an infinite loop.\\n\\n` +\r\n `**Tip**: Try rephrasing your request or ask about a specific aspect of the task.`;\r\n\r\n if (this.onResponseStreamCallback) {\r\n this.onResponseStreamCallback(loopMessage);\r\n }\r\n\r\n // Set as completed and break\r\n finalAssistantMessage = loopMessage;\r\n taskCompleted = true;\r\n break;\r\n }\r\n\r\n // NOTE: File-specific loop detection REMOVED\r\n // The backend now includes functionCall parts in assistant messages,\r\n // so the AI should properly remember its previous actions and not repeat them.\r\n // Extract and display reason_text if present (but skip for task_complete)\r\n const reasonText = toolCall.arguments.reason_text;\r\n if (reasonText && this.onResponseStreamCallback) {\r\n this.onResponseStreamCallback(reasonText + '\\n\\n');\r\n }\r\n\r\n // Determine the effective CWD for this command (use remote context CWD if applicable)\r\n const currentCtx = this.contextManager.getCurrentContext();\r\n const effectiveCwd = currentCtx.type !== 'local'\r\n ? currentCtx.metadata?.workingDirectory || '~'\r\n : this.cwd;\r\n\r\n // Build remote context prefix for SSH/Docker/WSL (for path display in UI)\r\n let remoteContext: string | undefined;\r\n if (currentCtx.type !== 'local') {\r\n const metadata = currentCtx.metadata;\r\n if (currentCtx.type === 'ssh' && metadata) {\r\n remoteContext = `${metadata.username || 'user'}@${metadata.hostname || 'remote'}`;\r\n } else if (currentCtx.type === 'wsl' && metadata) {\r\n remoteContext = `wsl:${metadata.distroName || 'wsl'}`;\r\n } else if (currentCtx.type === 'docker' && metadata) {\r\n remoteContext = `docker:${metadata.containerId?.substring(0, 12) || 'container'}`;\r\n }\r\n }\r\n\r\n // Notify UI: tool executing\r\n if (this.onToolExecutionUpdate) {\r\n let toolArgs = { ...toolCall.arguments, remoteContext };\r\n\r\n // Special handling for execute_command\r\n if (toolCall.name === 'execute_command') {\r\n // Add effective CWD\r\n toolArgs.cwd = effectiveCwd;\r\n }\r\n\r\n this.onToolExecutionUpdate({\r\n toolName: toolCall.name,\r\n status: 'executing',\r\n arguments: toolArgs\r\n });\r\n }\r\n\r\n // Log tool execution start\r\n conversationLogger.logToolExecutionStart(toolCall.name, toolCall.id);\r\n\r\n // Execute the tool (it will request approval if needed)\r\n // SPECIAL: Intercept sub_agent spawn to enforce approval\r\n if (toolCall.name === 'sub_agent' && toolCall.arguments?.action === 'spawn') {\r\n const approved = await context.requireApproval(\r\n `Spawn Sub-Agent`,\r\n true, // risky\r\n undefined,\r\n 'execute_command',\r\n { command: `spawn sub-agent` }\r\n );\r\n if (!approved) {\r\n // User rejected - log result as error and skip execution\r\n conversationLogger.logToolResult(toolCall.name, toolCall.id, null, false, 'User rejected');\r\n\r\n // Notify UI: tool failed\r\n if (this.onToolExecutionUpdate) {\r\n this.onToolExecutionUpdate({\r\n toolName: toolCall.name,\r\n status: 'error',\r\n error: 'User rejected',\r\n arguments: toolCall.arguments\r\n });\r\n }\r\n\r\n toolResults.push({\r\n tool_call_id: toolCall.id,\r\n name: toolCall.name,\r\n result: 'User rejected sub-agent spawn request',\r\n error: 'User rejected'\r\n });\r\n continue;\r\n }\r\n }\r\n const result = await this.toolRegistry.execute(toolCall.name, toolCall.arguments, context);\r\n\r\n if (result.success) {\r\n // Log successful tool result\r\n conversationLogger.logToolResult(toolCall.name, toolCall.id, result.result, true);\r\n\r\n // Notify UI: tool succeeded (send full result to UI)\r\n if (this.onToolExecutionUpdate) {\r\n // Add cwd to arguments for execute_command tool, and remoteContext for all tools\r\n const toolArgs = toolCall.name === 'execute_command'\r\n ? { ...toolCall.arguments, cwd: effectiveCwd, remoteContext }\r\n : { ...toolCall.arguments, remoteContext };\r\n\r\n this.onToolExecutionUpdate({\r\n toolName: toolCall.name,\r\n status: 'completed',\r\n result: result.result,\r\n arguments: toolArgs\r\n });\r\n }\r\n\r\n // Parse result if it's a string (avoid double-stringification)\r\n let parsedResult = result.result;\r\n if (typeof result.result === 'string') {\r\n try {\r\n parsedResult = JSON.parse(result.result);\r\n } catch {\r\n // Keep as string if not valid JSON\r\n parsedResult = result.result;\r\n }\r\n }\r\n\r\n // Sanitize context for AI consumption (strip ANSI, compact file writes)\r\n const sanitizedResult = sanitizeForContext(toolCall.name, parsedResult, toolCall.arguments);\r\n\r\n // Log the sanitized version for debugging purposes\r\n conversationLogger.logToolResult(`${toolCall.name} (SANITIZED_CONTEXT)`, toolCall.id, sanitizedResult, true);\r\n\r\n // Truncate result before sending to AI (to avoid exceeding message size limits)\r\n const truncatedResult = this.truncateResult(sanitizedResult);\r\n\r\n toolResults.push({\r\n tool_call_id: toolCall.id,\r\n name: toolCall.name,\r\n result: truncatedResult,\r\n });\r\n } else {\r\n // Log failed tool result\r\n conversationLogger.logToolResult(toolCall.name, toolCall.id, null, false, result.error);\r\n\r\n // Check if operation was cancelled by user\r\n if (result.error && result.error.includes('Operation cancelled by user')) {\r\n userCancelledOperation = true;\r\n }\r\n\r\n // Notify UI: tool failed\r\n if (this.onToolExecutionUpdate) {\r\n // Add cwd to arguments for execute_command tool, and remoteContext for all tools\r\n const toolArgs = toolCall.name === 'execute_command'\r\n ? { ...toolCall.arguments, cwd: effectiveCwd, remoteContext }\r\n : { ...toolCall.arguments, remoteContext };\r\n\r\n this.onToolExecutionUpdate({\r\n toolName: toolCall.name,\r\n status: 'error',\r\n error: result.error,\r\n arguments: toolArgs\r\n });\r\n }\r\n\r\n toolResults.push({\r\n tool_call_id: toolCall.id,\r\n name: toolCall.name,\r\n result: `Error: ${result.error}`,\r\n });\r\n\r\n // If user cancelled, stop processing remaining tools\r\n if (userCancelledOperation) {\r\n break;\r\n }\r\n }\r\n } catch (error: any) {\r\n // Log tool execution error\r\n conversationLogger.logError(`Tool execution: ${toolCall.name}`, error);\r\n\r\n // Check if operation was cancelled by user\r\n if (error.message && error.message.includes('Operation cancelled by user')) {\r\n userCancelledOperation = true;\r\n }\r\n\r\n // Build remote context for error notification\r\n const catchCtx = this.contextManager.getCurrentContext();\r\n let catchRemoteContext: string | undefined;\r\n if (catchCtx.type !== 'local') {\r\n const metadata = catchCtx.metadata;\r\n if (catchCtx.type === 'ssh' && metadata) {\r\n catchRemoteContext = `${metadata.username || 'user'}@${metadata.hostname || 'remote'}`;\r\n } else if (catchCtx.type === 'wsl' && metadata) {\r\n catchRemoteContext = `wsl:${metadata.distroName || 'wsl'}`;\r\n } else if (catchCtx.type === 'docker' && metadata) {\r\n catchRemoteContext = `docker:${metadata.containerId?.substring(0, 12) || 'container'}`;\r\n }\r\n }\r\n\r\n // Notify UI: tool failed\r\n if (this.onToolExecutionUpdate) {\r\n // Add cwd to arguments for execute_command tool, and remoteContext for all tools\r\n const toolArgs = toolCall.name === 'execute_command'\r\n ? { ...toolCall.arguments, cwd: this.cwd, remoteContext: catchRemoteContext }\r\n : { ...toolCall.arguments, remoteContext: catchRemoteContext };\r\n\r\n this.onToolExecutionUpdate({\r\n toolName: toolCall.name,\r\n status: 'error',\r\n error: error.message,\r\n arguments: toolArgs\r\n });\r\n }\r\n\r\n toolResults.push({\r\n tool_call_id: toolCall.id,\r\n name: toolCall.name,\r\n result: `Error: ${error.message}`,\r\n });\r\n\r\n // If user cancelled, stop processing remaining tools\r\n if (userCancelledOperation) {\r\n break;\r\n }\r\n }\r\n }\r\n\r\n // STOP AGENT LOOP if shell_input was provided\r\n // Interactive shell input implies handing control back to the shell/user\r\n const hasShellInput = toolCalls.some(tc =>\r\n tc.name === 'execute_command' && tc.arguments && tc.arguments.shell_input\r\n );\r\n\r\n if (hasShellInput) {\r\n try {\r\n quickLog(`[${new Date().toISOString()}] [CLI] Input sent to shell. Stopping agent loop to await output.\\n`);\r\n } catch (e) { }\r\n taskCompleted = true;\r\n }\r\n\r\n // If task_complete was called, stop the agentic loop immediately\r\n if (taskCompleted) {\r\n // Set the final message: use summary if provided, otherwise use the streamed assistantMessage\r\n finalAssistantMessage = taskCompleteSummary || assistantMessage;\r\n break;\r\n }\r\n\r\n // If user cancelled an operation, stop the agentic loop immediately\r\n if (userCancelledOperation) {\r\n // Add assistant message to history with thinking if available\r\n const cancelledAssistantMsg: AIMessage = {\r\n role: 'assistant',\r\n content: assistantMessage || '',\r\n tool_calls: toolCalls, // Store tool calls for MaaS models\r\n };\r\n if (currentTurnThinking) {\r\n cancelledAssistantMsg.thinking = currentTurnThinking;\r\n }\r\n if (currentTurnThinkingSignature) {\r\n cancelledAssistantMsg.thinkingSignature = currentTurnThinkingSignature;\r\n }\r\n this.conversationHistory.push(cancelledAssistantMsg);\r\n\r\n // Add tool results to history\r\n for (const toolResult of toolResults) {\r\n this.conversationHistory.push({\r\n role: 'tool',\r\n tool_call_id: toolResult.tool_call_id,\r\n content: typeof toolResult.result === 'string' ? toolResult.result : JSON.stringify(toolResult.result),\r\n });\r\n }\r\n\r\n // Set final message indicating cancellation\r\n finalAssistantMessage = 'Operation cancelled by user. The task was not completed.';\r\n break;\r\n }\r\n\r\n // Send assistant message to UI first (if there's text)\r\n if (assistantMessage && this.onResponseStreamCallback) {\r\n // Send the message that came with the tool calls\r\n this.onResponseStreamCallback(assistantMessage);\r\n }\r\n\r\n // Add assistant message with tool calls to conversation history\r\n if (toolCalls && toolCalls.length > 0) {\r\n try {\r\n quickLog(`[${new Date().toISOString()}] [CLI] Adapting assistant message: has toolCalls=${toolCalls.length}, first=${JSON.stringify(toolCalls[0])}\\n`);\r\n } catch (e) { }\r\n } else {\r\n try {\r\n quickLog(`[${new Date().toISOString()}] [CLI] Adapting assistant message: NO toolCalls\\n`);\r\n } catch (e) { }\r\n }\r\n\r\n // Filter out tool calls that were already handled directly (e.g., create_plan, mark_task_complete)\r\n const unhandledToolCalls = toolCalls.filter(tc => !handledToolCallIds.has(tc.id));\r\n\r\n // Also filter toolResults to only include results for unhandled tool calls\r\n // CRITICAL: Vertex AI requires equal number of function calls and function responses\r\n const unhandledToolResults = toolResults.filter(tr => !handledToolCallIds.has(tr.tool_call_id));\r\n\r\n // Only add assistant message if there are unhandled tool calls\r\n if (unhandledToolCalls.length > 0) {\r\n const assistantHistoryMsg: AIMessage = {\r\n role: 'assistant',\r\n content: assistantMessage || '',\r\n tool_calls: unhandledToolCalls, // Only include unhandled tool calls\r\n };\r\n // Include thinking from this turn if available (Extended Thinking pattern)\r\n if (currentTurnThinking) {\r\n assistantHistoryMsg.thinking = currentTurnThinking;\r\n }\r\n // Include thinking signature from this turn (required for Claude extended thinking)\r\n if (currentTurnThinkingSignature) {\r\n assistantHistoryMsg.thinkingSignature = currentTurnThinkingSignature;\r\n }\r\n\r\n // Log signature info for debugging multi-turn flows\r\n const geminiSigCount = unhandledToolCalls.filter(tc => !!tc.thoughtSignature).length;\r\n try {\r\n quickLog(`[${new Date().toISOString()}] [CLI] *** STORING ASSISTANT MSG: ${unhandledToolCalls.length} tool_calls, Gemini signatures: ${geminiSigCount}, Claude thinking: ${!!currentTurnThinking}, Claude sig: ${!!currentTurnThinkingSignature}\\n`);\r\n } catch (e) { }\r\n\r\n this.conversationHistory.push(assistantHistoryMsg);\r\n\r\n // Add tool results to conversation history as tool messages\r\n // Format: { tool_call_id, name, result: <object or string> }\r\n // Only add results for unhandled tool calls (handled ones already added their own results)\r\n for (const toolResult of unhandledToolResults) {\r\n // Add tool result to conversation history as tool message\r\n // IMPORTANT: tool_call_id must be a top-level property\r\n this.conversationHistory.push({\r\n role: 'tool',\r\n tool_call_id: toolResult.tool_call_id,\r\n content: typeof toolResult.result === 'string' ? toolResult.result : JSON.stringify(toolResult.result),\r\n });\r\n }\r\n }\r\n\r\n // Rebuild messages array with updated history\r\n // During agent loop: keep ALL thinking for current task\r\n // (Thinking from previous tasks was already stripped at request start)\r\n messages = getMessagesForContext();\r\n\r\n // No need to reset currentTurnThinking - keep accumulating for the task\r\n\r\n // Re-inject subshell context\r\n messages = this.aiContextInjector.injectSubshellContext(messages, currentContext);\r\n\r\n // ── Mid-turn interrupt check ──────────────────────────────────\r\n // If the user sent a message while this turn was executing, inject\r\n // it into the conversation now so the AI sees it on the very next\r\n // turn — instead of waiting for the entire multi-turn task to end.\r\n if (this.interruptQueue.length > 0) {\r\n const nextInterrupt = this.interruptQueue.shift()!;\r\n\r\n // Update queue UI\r\n if (this.onInterruptQueueUpdateCallback) {\r\n this.onInterruptQueueUpdateCallback(this.interruptQueue);\r\n }\r\n\r\n // Notify App.tsx to add the user bubble to messageHistory\r\n if (this.onQueuedMessageDispatchedCallback) {\r\n this.onQueuedMessageDispatchedCallback(nextInterrupt);\r\n }\r\n\r\n quickLog(`[${new Date().toISOString()}] [handleMessage] Processing mid-turn interrupt from user.\\n`);\r\n\r\n // Create a checkpoint for this interrupt so it appears in /revert.\r\n // Store the clean user text (without system wrapper) for autocomplete/input prefill.\r\n if (this.checkpointManager && this.currentChatId) {\r\n try {\r\n const checkpointContext = this.contextManager.getCurrentContext();\r\n let remoteSessionInfo: RemoteSessionInfo | undefined;\r\n let remoteHandler: RemoteFileHandler | undefined;\r\n\r\n if (checkpointContext.type !== 'local' && checkpointContext.handler) {\r\n const metadata = checkpointContext.metadata;\r\n remoteSessionInfo = {\r\n hostname: metadata.hostname,\r\n username: metadata.username,\r\n sessionId: checkpointContext.sessionId,\r\n connectionString: metadata.hostname\r\n ? `${metadata.username || 'user'}@${metadata.hostname}`\r\n : metadata.distroName || metadata.containerId || undefined,\r\n };\r\n\r\n if (typeof checkpointContext.handler.readFile === 'function' &&\r\n typeof checkpointContext.handler.writeFile === 'function' &&\r\n typeof checkpointContext.handler.executeCommand === 'function' &&\r\n typeof checkpointContext.handler.isConnected === 'function') {\r\n remoteHandler = checkpointContext.handler as RemoteFileHandler;\r\n }\r\n }\r\n\r\n // conversationIndex points to the next message slot (the interrupt user message).\r\n const interruptCheckpoint = await this.checkpointManager.startCheckpoint({\r\n prompt: nextInterrupt,\r\n cwd: this.cwd,\r\n contextType: checkpointContext.type as 'local' | 'ssh' | 'wsl' | 'docker',\r\n conversationIndex: this.conversationHistory.length,\r\n remoteSessionInfo,\r\n handler: remoteHandler,\r\n });\r\n\r\n if (interruptCheckpoint) {\r\n this.currentCheckpointId = interruptCheckpoint.id;\r\n context.currentCheckpointId = interruptCheckpoint.id;\r\n quickLog(\r\n `[${new Date().toISOString()}] [Checkpoint] Started mid-turn checkpoint ${interruptCheckpoint.id} for interrupt: \"${nextInterrupt.slice(0, 50)}...\"\\n`\r\n );\r\n }\r\n } catch (checkpointError: any) {\r\n quickLog(\r\n `[${new Date().toISOString()}] [Checkpoint] Failed to create mid-turn interrupt checkpoint: ${checkpointError?.message || checkpointError}\\n`\r\n );\r\n }\r\n }\r\n\r\n // Wrap the interrupt with context for the AI\r\n const wrappedInterrupt = `[SYSTEM NOTE: The user interrupted you with the following message. Please address it, adjust your actions, and proceed with the task.]\\n\\n${nextInterrupt}`;\r\n\r\n // Push the interrupt into conversation history so the AI\r\n // receives both the tool results from THIS turn and the\r\n // user's new message together on the next AI call.\r\n this.conversationHistory.push({\r\n role: 'user',\r\n content: wrappedInterrupt,\r\n });\r\n\r\n // Rebuild messages to include the interrupt\r\n messages = getMessagesForContext();\r\n messages = this.aiContextInjector.injectSubshellContext(messages, currentContext);\r\n }\r\n\r\n // Auto-compact context when usage grows too large during long-running agent loops.\r\n const compactionOutcome = await this.maybeAutoCompactContext(selectedModel, modelContextWindow);\r\n if (compactionOutcome.historyChanged) {\r\n messages = getMessagesForContext();\r\n messages = this.aiContextInjector.injectSubshellContext(messages, currentContext);\r\n }\r\n\r\n continue; // Loop back to AI service\r\n } else {\r\n // No tool calls - check for silent stop or text-only response\r\n\r\n // Case 1: Silent Stop (No tool calls AND no text)\r\n if (!assistantMessage || !assistantMessage.trim()) {\r\n // No tool calls and no message - AI stopped silently\r\n // This usually means the AI thinks it's done but didn't call task_complete\r\n // Prompt it to either continue or complete\r\n conversationLogger.logNarrationDetection('silent_stop', {\r\n turn: turnCount,\r\n assistantMessageLength: 0,\r\n });\r\n\r\n const silentStopPrompt = '⚠️ **SILENT STOP DETECTED**: You ended your turn without any output or tool calls.\\n\\n' +\r\n '**This is not allowed.** You must either:\\n' +\r\n '1. Execute a tool call if more work is needed, OR\\n' +\r\n '2. Output your response text, then call task_complete()\\n\\n' +\r\n '**If you have completed the task**, output your summary now, then call task_complete().\\n' +\r\n '**If more work is needed**, execute the next tool call immediately.';\r\n\r\n conversationLogger.logSystemPrompt('silent_stop_prompt', silentStopPrompt);\r\n this.conversationHistory.push({\r\n role: 'user',\r\n content: silentStopPrompt,\r\n });\r\n }\r\n // Case 2: Text-only response - accept it immediately as final\r\n else {\r\n // Log that we're accepting this as a final answer\r\n conversationLogger.logNarrationDetection('final_answer', {\r\n turn: turnCount,\r\n messagePreview: assistantMessage.substring(0, 200),\r\n action: 'accepting_immediately',\r\n });\r\n\r\n // Accept the text as the final message and break\r\n finalAssistantMessage = assistantMessage;\r\n break;\r\n }\r\n\r\n // Rebuild messages array with updated history\r\n // Backend will inject system prompt\r\n messages = getMessagesForContext();\r\n\r\n // Re-inject subshell context\r\n messages = this.aiContextInjector.injectSubshellContext(messages, currentContext);\r\n\r\n const compactionOutcome = await this.maybeAutoCompactContext(selectedModel, modelContextWindow);\r\n if (compactionOutcome.historyChanged) {\r\n messages = getMessagesForContext();\r\n messages = this.aiContextInjector.injectSubshellContext(messages, currentContext);\r\n }\r\n\r\n // Continue loop to get AI's response (removed 500ms delay for faster response)\r\n continue;\r\n }\r\n\r\n // No tool calls and no message - AI stopped silently\r\n // This usually means the AI thinks it's done but didn't call task_complete\r\n // Prompt it to either continue or complete\r\n const silentStopPrompt = '⚠️ **SILENT STOP DETECTED**: You ended your turn without any output or tool calls.\\n\\n' +\r\n '**This is not allowed.** You must either:\\n' +\r\n '1. Execute a tool call if more work is needed, OR\\n' +\r\n '2. Output your response text, then call task_complete()\\n\\n' +\r\n '**If you have completed the task**, output your summary now, then call task_complete().\\n' +\r\n '**If more work is needed**, execute the next tool call immediately.';\r\n\r\n this.conversationHistory.push({\r\n role: 'user',\r\n content: silentStopPrompt,\r\n });\r\n\r\n // Rebuild messages array with updated history\r\n // Backend will inject system prompt\r\n messages = getMessagesForContext();\r\n\r\n // Re-inject subshell context\r\n messages = this.aiContextInjector.injectSubshellContext(messages, currentContext);\r\n\r\n // Add delay before prompting\r\n await new Promise(resolve => setTimeout(resolve, 500));\r\n\r\n // Increment turn count and continue (give AI one more chance)\r\n turnCount++;\r\n if (turnCount >= MAX_TURNS) {\r\n // If we've hit max turns, force completion\r\n finalAssistantMessage = '⚠️ **Task Incomplete**: The AI stopped responding after multiple prompts.\\n\\n' +\r\n 'The task may be partially complete. Please review the work done and provide more specific instructions if needed.';\r\n break;\r\n }\r\n\r\n\r\n const compactionOutcome = await this.maybeAutoCompactContext(selectedModel, modelContextWindow);\r\n if (compactionOutcome.historyChanged) {\r\n messages = getMessagesForContext();\r\n messages = this.aiContextInjector.injectSubshellContext(messages, currentContext);\r\n }\r\n continue;\r\n }\r\n\r\n // Check if max turns was reached\r\n if (turnCount >= MAX_TURNS) {\r\n // Add a warning message to the final response\r\n const warningMessage = '\\n\\n⚠️ Note: The task reached the maximum number of processing turns. The work may be incomplete. Please review the results and let me know if you need me to continue.';\r\n finalAssistantMessage = (finalAssistantMessage || 'Task processing limit reached.') + warningMessage;\r\n }\r\n\r\n // Send final message to user (without tool execution logs)\r\n // If finalAssistantMessage is empty (task_complete with no summary), don't show anything\r\n const finalMessage = finalAssistantMessage;\r\n\r\n // Only save and display if there's a message\r\n if (finalMessage) {\r\n // Save to conversation history\r\n this.conversationHistory.push({\r\n role: 'assistant',\r\n content: finalMessage,\r\n });\r\n\r\n // Messages are stored locally only via saveCurrentChat() below\r\n } // End of while loop\r\n\r\n // Auto-save conversation to local storage\r\n this.saveCurrentChat();\r\n\r\n\r\n // Log session end\r\n conversationLogger.endSession(finalAssistantMessage, turnCount);\r\n\r\n // Send response back to UI (only if there's a message)\r\n if (this.onResponseCallback && finalAssistantMessage) {\r\n this.onResponseCallback(finalAssistantMessage);\r\n }\r\n\r\n // Fire file change summary callback after every successful completion\r\n // (not gated on task_complete — works for all model types)\r\n if (this.onFileChangeSummaryCallback && this.checkpointManager) {\r\n try {\r\n let remoteHandler: RemoteFileHandler | undefined;\r\n const summaryCtx = this.contextManager.getCurrentContext();\r\n if (summaryCtx.type !== 'local' && summaryCtx.handler) {\r\n const h = summaryCtx.handler;\r\n if (typeof h.readFile === 'function' &&\r\n typeof h.writeFile === 'function' &&\r\n typeof h.executeCommand === 'function' &&\r\n typeof h.isConnected === 'function') {\r\n remoteHandler = h as RemoteFileHandler;\r\n }\r\n }\r\n\r\n const sessionChanges = await this.checkpointManager.getAggregatedSessionChanges(remoteHandler);\r\n if (sessionChanges) {\r\n const { changes, stats } = sessionChanges;\r\n const fileCount = changes.added.length + changes.modified.length + changes.deleted.length;\r\n\r\n if (fileCount > 0) {\r\n const totalInsertions = stats.reduce((sum: number, s: { insertions: number; deletions: number }) => sum + s.insertions, 0);\r\n const totalDeletions = stats.reduce((sum: number, s: { insertions: number; deletions: number }) => sum + s.deletions, 0);\r\n\r\n this.onFileChangeSummaryCallback({ filesChanged: fileCount, insertions: totalInsertions, deletions: totalDeletions });\r\n }\r\n }\r\n } catch {\r\n // Silently ignore errors in file change summary\r\n }\r\n }\r\n\r\n } catch (error: any) {\r\n // Log the error\r\n conversationLogger.logError('handleMessage', error);\r\n\r\n // Check if this was an abort/cancellation (including timeout errors from aborted requests)\r\n if (error.name === 'AbortError' || error.message?.includes('aborted') || error.message?.includes('timed out') || (myAbortController as any).isIntentionalAbort) {\r\n // If intentionally aborted for replacement by new message, return silently\r\n // The new message will take over - no need to show cancellation message\r\n if ((myAbortController as any).isIntentionalAbort && (myAbortController as any).isReplacement) {\r\n return;\r\n }\r\n\r\n conversationLogger.logError('handleMessage', new Error('Request cancelled by user'));\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback('⚠️ Request cancelled by user.');\r\n }\r\n return;\r\n }\r\n throw new Error(`AI Error: ${error.message}`);\r\n } finally {\r\n // Finalize checkpoint if one was created for this message\r\n if (this.currentCheckpointId && this.checkpointManager) {\r\n try {\r\n // Resolve remote handler so finalizeCheckpoint can correctly calculate\r\n // changes for remote checkpoints (new/modified files live on the remote).\r\n let finalizeHandler: RemoteFileHandler | undefined;\r\n const finalizeCtx = this.contextManager.getCurrentContext();\r\n if (finalizeCtx.type !== 'local' && finalizeCtx.handler) {\r\n const h = finalizeCtx.handler;\r\n if (typeof h.readFile === 'function' &&\r\n typeof h.writeFile === 'function' &&\r\n typeof h.executeCommand === 'function' &&\r\n typeof h.isConnected === 'function') {\r\n finalizeHandler = h as RemoteFileHandler;\r\n }\r\n }\r\n await this.checkpointManager.finalizeCheckpoint(this.currentCheckpointId, finalizeHandler);\r\n quickLog(`[${new Date().toISOString()}] [Checkpoint] Finalized checkpoint ${this.currentCheckpointId}\\n`);\r\n } catch (error: any) {\r\n quickLog(`[${new Date().toISOString()}] [Checkpoint] Failed to finalize checkpoint: ${error.message}\\n`);\r\n }\r\n this.currentCheckpointId = undefined;\r\n }\r\n // Clean up abort controller\r\n this.currentAbortController = undefined;\r\n const wasIntentionalAbort = this.requestIntentionallyAborted;\r\n // Always reset after this request fully unwinds so subsequent\r\n // queue dispatch decisions are not blocked by stale abort state.\r\n this.requestIntentionallyAborted = false;\r\n\r\n // Process queued interrupts even after an intentional cancel.\r\n // ESC should cancel the CURRENT turn, not strand queued user prompts.\r\n if (this.interruptQueue.length > 0) {\r\n if (wasIntentionalAbort) {\r\n quickLog(`[${new Date().toISOString()}] [handleMessage] Cancelled current turn; continuing with queued interrupt(s).\\n`);\r\n }\r\n // We aren't checking `myAbortController.isIntentionalAbort` here because we don't set it anymore\r\n // Next message in queue\r\n const nextInterrupt = this.interruptQueue.shift()!;\r\n if (this.onInterruptQueueUpdateCallback) {\r\n this.onInterruptQueueUpdateCallback(this.interruptQueue);\r\n }\r\n\r\n // Notify UI that this queued message is now being dispatched for processing.\r\n // App.tsx uses this to add the user message to messageHistory at the correct time\r\n // (i.e., when the message actually starts being processed, not when it was first queued).\r\n if (this.onQueuedMessageDispatchedCallback) {\r\n this.onQueuedMessageDispatchedCallback(nextInterrupt);\r\n }\r\n\r\n quickLog(`[${new Date().toISOString()}] [handleMessage] Processing queued interrupt from user.\\n`);\r\n\r\n const wrappedMessage = `[SYSTEM NOTE: The user interrupted you with the following message. Please address it, adjust your actions, and proceed with the task.]\\n\\n${nextInterrupt}`;\r\n\r\n // Start next message processing asynchronously so we don't block finally block returning\r\n setTimeout(() => {\r\n this.handleMessage(wrappedMessage, { interruptCleanText: nextInterrupt }).catch(err => {\r\n conversationLogger.logError('Queued interrupt handleMessage', err);\r\n });\r\n }, 0);\r\n } else {\r\n void this.maybeProcessSessionCommandQueue();\r\n }\r\n }\r\n }\r\n\r\n private async handleSlashCommand(command: string): Promise<void> {\r\n const parts = command.slice(1).split(' ');\r\n const cmd = parts[0].toLowerCase();\r\n const args = parts.slice(1);\r\n\r\n let responseMessage = '';\r\n\r\n switch (cmd) {\r\n case 'help':\r\n responseMessage = `Available Commands:\\n\\n` +\r\n `/help - Show this help message\\n` +\r\n `/init - Analyze project and create/load centaurus.md context file\\n` +\r\n `/chat - Manage chat sessions (resume previous chats)\\n` +\r\n `/clear - Clear conversation and start a new chat\\n` +\r\n `/sync - Sync data to/from cloud (upload/restore)\\n` +\r\n `/config - View current configuration\\n` +\r\n `/model - Select from available Google models\\n` +\r\n `/plan - Toggle plan mode for complex implementations\\n` +\r\n `/mcp - Manage configured MCP servers and tools\\n` +\r\n `/docs - Open Centaurus documentation in browser\\n` +\r\n `/copy-chat-context - Copy chat as readable text to clipboard\\n` +\r\n `/session-limits - View session quota usage and limits\\n` +\r\n `/quality - Toggle enhanced quality features (thinking protocol, validation)\\n` +\r\n `/autonomous - Toggle autonomous mode (Silent Operator with task_complete)\\n` +\r\n `/sign-in - Sign in with Google (if not already signed in)\\n` +\r\n `/logout - Sign out, clear session, and exit CLI\\n` +\r\n `/exit - Exit the application\\n\\n` +\r\n `Keyboard Shortcuts:\\n\\n` +\r\n `Ctrl+D - Cycle modes (Agent → Terminal → Auto)\\n` +\r\n `Ctrl+T - Toggle auto-accept mode\\n` +\r\n `Ctrl+C - Cancel operation / Exit (press twice)\\n` +\r\n `Tab - Autocomplete files/directories (in command mode)\\n` +\r\n `Ctrl+Z - Undo last input change\\n` +\r\n `Ctrl+A - Select all text`;\r\n break;\r\n\r\n case 'session-limits': {\r\n const config = sessionQuotaManager.getCurrentConfig();\r\n const messagesUsed = sessionQuotaManager.getMessagesUsed();\r\n const remaining = sessionQuotaManager.getRemainingMessages();\r\n const timeRemaining = sessionQuotaManager.getFormattedTimeRemaining();\r\n const maxMessages = config.maxMessagesPerSession;\r\n\r\n // Calculate percentage used (cap at 100% for display)\r\n const percentUsed = maxMessages > 0 ? Math.min(100, Math.round((messagesUsed / maxMessages) * 100)) : 0;\r\n\r\n // Create a visual progress bar (clamp to valid range)\r\n const barLength = 20;\r\n const filledLength = Math.min(barLength, Math.max(0, Math.round((messagesUsed / maxMessages) * barLength)));\r\n const emptyLength = barLength - filledLength;\r\n const progressBar = '█'.repeat(filledLength) + '░'.repeat(emptyLength);\r\n\r\n // Status message based on quota\r\n const quotaStatus = remaining <= 0\r\n ? '\\n\\n⚠️ Session quota exhausted! AI requests are blocked until reset.'\r\n : '';\r\n\r\n responseMessage = `📊 Session Limits\\n\\n` +\r\n `Plan: free\\n` +\r\n `Session Window: ${config.sessionDurationMs / (60 * 60 * 1000)} hours\\n\\n` +\r\n `Messages Used: ${messagesUsed} / ${maxMessages} (${percentUsed}%)\\n` +\r\n `Messages Left: ${Math.max(0, remaining)}\\n` +\r\n `Progress: [${progressBar}]\\n` +\r\n `Time Remaining: ${timeRemaining || 'Session not started'}${quotaStatus}`;\r\n break;\r\n }\r\n\r\n case 'init':\r\n try {\r\n // Define the context file names in priority order\r\n const contextFiles = ['centaurus.md', 'gemini.md', 'claude.md'];\r\n let foundFile: string | null = null;\r\n let foundFilePath: string | null = null;\r\n\r\n // Check for existing context files in priority order\r\n for (const fileName of contextFiles) {\r\n const filePath = path.join(this.cwd, fileName);\r\n if (fs.existsSync(filePath)) {\r\n foundFile = fileName;\r\n foundFilePath = filePath;\r\n break;\r\n }\r\n }\r\n\r\n if (foundFile) {\r\n // Context file exists - read and load it\r\n try {\r\n const content = fs.readFileSync(foundFilePath!, 'utf-8');\r\n\r\n // Check if content is empty\r\n if (!content.trim()) {\r\n responseMessage = `⚠️ Found ${foundFile} but it's empty.\\n\\n` +\r\n `Please either:\\n` +\r\n `1. Delete the file and run /init again to generate new documentation\\n` +\r\n `2. Add content to the file manually`;\r\n break;\r\n }\r\n\r\n // If the found file is not centaurus.md, create a copy\r\n if (foundFile !== 'centaurus.md') {\r\n const centaurusPath = path.join(this.cwd, 'centaurus.md');\r\n\r\n // Check if centaurus.md already exists\r\n if (fs.existsSync(centaurusPath)) {\r\n responseMessage = `✅ Found both ${foundFile} and centaurus.md. Loaded project context from centaurus.md`;\r\n } else {\r\n // Create a copy as centaurus.md\r\n fs.copyFileSync(foundFilePath!, centaurusPath);\r\n responseMessage = `✅ Found ${foundFile} and created a copy as centaurus.md. Loaded project context.`;\r\n }\r\n } else {\r\n // Already centaurus.md\r\n responseMessage = `✅ Loaded project context from centaurus.md`;\r\n }\r\n\r\n // Add the context to conversation history so the AI actually has it\r\n this.conversationHistory.push({\r\n role: 'user',\r\n content: `Here is the project context from ${foundFile}:\\n\\n${content}`\r\n });\r\n\r\n } catch (readError: any) {\r\n responseMessage = `❌ Error reading ${foundFile}: ${readError.message}\\n\\n` +\r\n `Please check file permissions and try again.`;\r\n }\r\n } else {\r\n // No context file exists - trigger AI analysis\r\n responseMessage = `🔍 I will now analyze the contents of this directory and then create a centaurus.md`;\r\n\r\n // Send initial response\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback(responseMessage);\r\n }\r\n\r\n // Create a specialized prompt for project analysis\r\n const initPrompt = `You are tasked with analyzing the current project directory and creating a comprehensive documentation file called \"centaurus.md\".\r\n\r\n**Your Task:**\r\n1. Use the list_directory tool to explore the project structure starting from the current directory\r\n2. Use read_file to examine key files such as:\r\n - package.json, requirements.txt, or similar dependency files\r\n - README files\r\n - Configuration files (tsconfig.json, .eslintrc, etc.)\r\n - Main entry points (index.js, main.py, App.tsx, etc.)\r\n3. Use grep_search to find important patterns and understand the codebase\r\n4. Create a comprehensive centaurus.md file using the write_file tool\r\n\r\n**The centaurus.md file should include:**\r\n\r\n# [Project Name]\r\n\r\n## Overview\r\nBrief description of what this project does and its purpose\r\n\r\n## Technology Stack\r\n- **Languages**: List programming languages used\r\n- **Frameworks**: Main frameworks (React, Express, Django, etc.)\r\n- **Key Dependencies**: Important libraries and tools\r\n- **Build Tools**: Webpack, Vite, Maven, etc.\r\n\r\n## Directory Structure\r\n\\`\\`\\`\r\n/\r\n /src - Main source code\r\n /tests - Test files\r\n /config - Configuration files\r\n ...\r\n\\`\\`\\`\r\n\r\nBrief explanation of each major directory's purpose\r\n\r\n## Key Components\r\nDescribe the main modules, classes, or components:\r\n- **Component Name**: What it does, where it's located\r\n- **Component Name**: What it does, where it's located\r\n\r\n## Architecture\r\nExplain how the components interact:\r\n- Data flow\r\n- Communication patterns (REST APIs, GraphQL, event-driven, etc.)\r\n- State management\r\n- Database architecture (if applicable)\r\n\r\n## Development Workflow\r\n- How to install dependencies\r\n- How to run the development server\r\n- How to run tests\r\n- How to build for production\r\n\r\n## Important Conventions\r\n- Code organization patterns\r\n- Naming conventions\r\n- File structure patterns\r\n- Any special patterns or best practices used\r\n\r\n## Entry Points\r\n- Main files where execution starts\r\n- API endpoints (if applicable)\r\n- CLI commands (if applicable)\r\n\r\n## Quick Reference\r\nUseful commands, key files to know about, common tasks\r\n\r\n**IMPORTANT INSTRUCTIONS:**\r\n- Be thorough but concise\r\n- Use clear, professional language\r\n- Include code examples where helpful\r\n- Create the file in the current working directory: ${this.cwd}\r\n- After creating the file, call task_complete with a summary\r\n\r\n**Current working directory**: ${this.cwd}\r\n\r\nStart by listing the directory structure to understand what you're working with.`;\r\n\r\n // Add to conversation history\r\n this.conversationHistory.push({\r\n role: 'user',\r\n content: initPrompt\r\n });\r\n\r\n // Trigger the AI to process this\r\n await this.handleMessage('');\r\n\r\n // Don't send another response - the AI will respond\r\n return;\r\n }\r\n } catch (error: any) {\r\n responseMessage = `❌ Error executing /init command: ${error.message}\\n\\n` +\r\n `Please try again or check file permissions.`;\r\n }\r\n break;\r\n case 'sign-in':\r\n // Check if already authenticated\r\n if (apiClient.isAuthenticated()) {\r\n try {\r\n // Verify session is valid\r\n const user = await apiClient.getCurrentUser();\r\n responseMessage = `✅ You are already signed in as ${user.fullName} (${user.email})`;\r\n } catch (error) {\r\n // Session is invalid, proceed with sign-in\r\n responseMessage = '🔐 Starting authentication process...\\n';\r\n\r\n // Send initial response\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback(responseMessage);\r\n }\r\n\r\n // Attempt authentication\r\n const result = await authenticateWithGoogle();\r\n\r\n if (result.success) {\r\n // Enable backend sync after successful authentication\r\n this.configManager.enableBackendSync();\r\n this.conversationStarted = false; // Reset conversation to start fresh\r\n\r\n responseMessage = `✅ Successfully signed in as ${result.user?.fullName || 'User'}!\\n\\n` +\r\n `Cloud sync is now enabled. Your conversations and settings will be saved.`;\r\n } else {\r\n responseMessage = `❌ Authentication failed: ${result.error || 'Unknown error'}`;\r\n }\r\n }\r\n } else {\r\n // Not authenticated, proceed with sign-in\r\n responseMessage = '🔐 Starting authentication process...\\n';\r\n\r\n // Send initial response\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback(responseMessage);\r\n }\r\n\r\n // Attempt authentication\r\n const result = await authenticateWithGoogle();\r\n\r\n if (result.success) {\r\n // Enable backend sync after successful authentication\r\n this.configManager.enableBackendSync();\r\n this.conversationStarted = false; // Reset conversation to start fresh\r\n\r\n responseMessage = `✅ Successfully signed in as ${result.user?.fullName || 'User'}!\\n\\n` +\r\n `Cloud sync is now enabled. Your conversations and settings will be saved.`;\r\n } else {\r\n responseMessage = `❌ Authentication failed: ${result.error || 'Unknown error'}`;\r\n }\r\n }\r\n break;\r\n case 'logout':\r\n try {\r\n // Cancel workflow learning if active before logout\r\n const logoutCancelMsg = this.cancelWorkflowLearning('Logging out');\r\n if (logoutCancelMsg && this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(logoutCancelMsg);\r\n }\r\n\r\n await apiClient.logout();\r\n responseMessage = '✅ Logged out successfully.\\n\\n' +\r\n 'Your session has been cleared.\\n' +\r\n 'Exiting CLI...';\r\n\r\n // Send response to UI\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback(responseMessage);\r\n }\r\n\r\n // Wait a moment for the message to display, then exit\r\n setTimeout(() => {\r\n process.exit(0);\r\n }, 1000);\r\n\r\n return; // Don't send response again at the end\r\n } catch (error: any) {\r\n responseMessage = `❌ Logout failed: ${error.message}\\n\\nExiting CLI anyway...`;\r\n\r\n // Send response to UI\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback(responseMessage);\r\n }\r\n\r\n // Exit even if logout failed\r\n setTimeout(() => {\r\n process.exit(0);\r\n }, 1000);\r\n\r\n return; // Don't send response again at the end\r\n }\r\n case 'plan':\r\n // Don't allow toggling plan mode if in command mode\r\n if (this.commandMode) {\r\n responseMessage = '❌ Cannot toggle plan mode while in command mode. Press Ctrl+D to exit command mode first.';\r\n break;\r\n }\r\n\r\n this.planMode = !this.planMode;\r\n\r\n // Notify UI of plan mode change\r\n if (this.onPlanModeChange) {\r\n this.onPlanModeChange(this.planMode);\r\n }\r\n\r\n // Don't send any response message - the status bar shows the mode\r\n return;\r\n case 'quality':\r\n // Toggle enhanced quality features\r\n const currentQuality = this.configManager.get('enhancedQuality');\r\n const newQuality = currentQuality === false; // Toggle (default is true)\r\n\r\n this.configManager.set('enhancedQuality', newQuality);\r\n\r\n responseMessage = newQuality\r\n ? '✅ Enhanced quality features enabled\\n\\n' +\r\n 'The AI will now use:\\n' +\r\n '• Structured thinking protocol before actions\\n' +\r\n '• Enhanced command execution hygiene\\n' +\r\n '• Intelligent file editing validation\\n' +\r\n '• Professional SRE/Architect persona'\r\n : '⚠️ Enhanced quality features disabled\\n\\n' +\r\n 'The AI will use the basic system prompt without:\\n' +\r\n '• Thinking protocol\\n' +\r\n '• Advanced validation\\n' +\r\n '• Enhanced tool descriptions';\r\n break;\r\n case 'autonomous':\r\n // Toggle autonomous mode (Silent Operator)\r\n const currentAutonomous = this.configManager.get('autonomousMode');\r\n const newAutonomous = !currentAutonomous; // Toggle (default is false)\r\n\r\n this.configManager.set('autonomousMode', newAutonomous);\r\n\r\n responseMessage = newAutonomous\r\n ? '✅ Autonomous Mode enabled (Silent Operator)\\n\\n' +\r\n 'The AI will now:\\n' +\r\n '• Work silently without narrating actions\\n' +\r\n '• Use Touch-First safety (never guess file paths)\\n' +\r\n '• Apply surgical precision to file edits\\n' +\r\n '• Output summary text, then call task_complete() when done\\n' +\r\n '• Inject intelligent error recovery hints\\n\\n' +\r\n 'This is the industry-standard autonomous agent mode.'\r\n : '⚠️ Autonomous Mode disabled\\n\\n' +\r\n 'The AI will use the standard enhanced prompt with:\\n' +\r\n '• Verbose communication after each action\\n' +\r\n '• Standard tool descriptions\\n' +\r\n '• Manual completion detection';\r\n break;\r\n case 'clear':\r\n // Cancel workflow learning if active before clearing\r\n const clearCancelMsg = this.cancelWorkflowLearning('Clearing chat session');\r\n if (clearCancelMsg && this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(clearCancelMsg);\r\n }\r\n\r\n // Start a new chat session (clears history and generates new chat ID)\r\n this.startNewChat();\r\n // Don't send any response message - the UI will handle clearing\r\n return;\r\n case 'config':\r\n if (args.length >= 2 && args[0].toLowerCase() === 'set') {\r\n // Set config value: /config set <key> <value>\r\n const configKey = args[1];\r\n const configValue = args.slice(2).join(' ');\r\n\r\n if (!configValue) {\r\n responseMessage = `❌ Error: No value provided.\\nUsage: /config set <key> <value>`;\r\n } else if (configKey === 'model') {\r\n // Validate model\r\n if (!isValidModel(configValue)) {\r\n responseMessage = `❌ ${getInvalidModelError(configValue)}`;\r\n break;\r\n }\r\n\r\n try {\r\n this.configManager.set('model', configValue);\r\n responseMessage = `✅ Configuration updated: ${configKey} = ${configValue}`;\r\n } catch (error: any) {\r\n responseMessage = `❌ Failed to update configuration: ${error.message}`;\r\n }\r\n } else if (configKey === 'enhancedQuality' || configKey === 'autonomousMode') {\r\n // Parse boolean value\r\n const boolValue = configValue.toLowerCase() === 'true' || configValue === '1';\r\n\r\n try {\r\n this.configManager.set(configKey, boolValue);\r\n responseMessage = `✅ Configuration updated: ${configKey} = ${boolValue}`;\r\n } catch (error: any) {\r\n responseMessage = `❌ Failed to update configuration: ${error.message}`;\r\n }\r\n } else {\r\n responseMessage = `❌ Error: Unknown config key: ${configKey}\\nValid keys: model, enhancedQuality, autonomousMode`;\r\n }\r\n } else {\r\n // View config\r\n const config = this.configManager.load();\r\n\r\n // Import version checker to get current version\r\n const { getCurrentVersion } = await import('./utils/version-checker.js');\r\n const currentVersion = getCurrentVersion();\r\n\r\n responseMessage = `Current Configuration:\\n\\n` +\r\n `Version: ${currentVersion}\\n` +\r\n `Model: ${config.model || 'gemini-2.5-flash (default)'}\\n` +\r\n `AI Auto-Suggest: ${config.aiAutoSuggest === true ? '✅ Enabled' : '❌ Disabled'}\\n` +\r\n `Authentication: ${apiClient.isAuthenticated() ? '✅ Signed in' : '❌ Not signed in'}`;\r\n }\r\n break;\r\n\r\n case 'settings':\r\n if (args.length >= 2 && args[0].toLowerCase() === 'auto-suggest') {\r\n // Handle /settings auto-suggest <on/off>\r\n const value = args[1].toLowerCase();\r\n\r\n if (value === 'on') {\r\n this.configManager.set('aiAutoSuggest', true);\r\n if (this.onAiAutoSuggestChange) {\r\n this.onAiAutoSuggestChange(true);\r\n }\r\n responseMessage = '✅ **AI Auto-Suggestions Enabled**\\n\\n' +\r\n 'From now on, I will suggest commands after 5 seconds of inactivity.\\n' +\r\n 'Suggestions will appear in grey text. Use the **Right Arrow** key to accept them.';\r\n } else if (value === 'off') {\r\n this.configManager.set('aiAutoSuggest', false);\r\n if (this.onAiAutoSuggestChange) {\r\n this.onAiAutoSuggestChange(false);\r\n }\r\n responseMessage = '✅ **AI Auto-Suggestions Disabled**\\n\\n' +\r\n 'I will no longer provide AI-powered command suggestions.';\r\n } else {\r\n responseMessage = '❌ Invalid option. Usage: `/settings auto-suggest on` or `/settings auto-suggest off`';\r\n }\r\n } else {\r\n\r\n responseMessage = '❌ Invalid command format.\\n\\nUsage:\\n- `/settings auto-suggest on`\\n- `/settings auto-suggest off`';\r\n }\r\n break;\r\n\r\n case 'model':\r\n case 'models':\r\n // Handle subcommands: local, cloud\r\n const modelSubCommand = args[0]?.toLowerCase();\r\n\r\n if (modelSubCommand === 'local') {\r\n // Local Ollama models\r\n try {\r\n // Check if Ollama is running\r\n const status = await ollamaService.isOllamaRunning();\r\n if (!status.available) {\r\n responseMessage = `❌ Cannot connect to Ollama\r\n\r\n${status.error || 'Ollama is not running.'}\r\n\r\nTo use local models:\r\n1. Install Ollama from: https://ollama.ai\r\n2. Start Ollama by running: ollama serve\r\n3. Pull a model: ollama pull llama3\r\n\r\nThen try /models local again.`;\r\n break;\r\n }\r\n\r\n // Get available local models\r\n const localModels = await ollamaService.getLocalModels();\r\n\r\n if (localModels.length === 0) {\r\n responseMessage = `📭 No local models found\r\n\r\nOllama is running (v${status.version}) but no models are downloaded.\r\n\r\nTo download models, run:\r\n ollama pull llama3\r\n ollama pull codellama\r\n ollama pull mistral\r\n\r\nThen try /models local again.`;\r\n break;\r\n }\r\n\r\n // Show picker for local model selection\r\n if (this.onShowPickerCallback) {\r\n const config = this.configManager.load();\r\n const currentModelName = config.modelName || '';\r\n const isCurrentLocal = config.isLocalModel === true;\r\n\r\n this.onShowPickerCallback({\r\n message: 'Select Local Model (Ollama)',\r\n type: 'local-model' as any, // Cast to bypass type check, will be handled in handlePickerSelection\r\n choices: localModels.map((model) => {\r\n const size = OllamaService.formatModelSize(model.size);\r\n const isCurrent = isCurrentLocal && currentModelName === model.name;\r\n const supportsTools = OllamaService.modelSupportsTools(model.name);\r\n const toolsBadge = supportsTools ? ' [Tools]' : '';\r\n return {\r\n label: `${model.name} (${size})${toolsBadge}${isCurrent ? ' [CURRENT]' : ''}`,\r\n value: model.name\r\n };\r\n })\r\n });\r\n return; // Don't send a text response, picker will handle it\r\n }\r\n } catch (error: any) {\r\n responseMessage = OllamaService.getHelpfulErrorMessage(error);\r\n }\r\n break;\r\n }\r\n\r\n if (modelSubCommand === 'cloud' || args.length === 0) {\r\n // Cloud models (default behavior when no subcommand or 'cloud' specified)\r\n if (this.onShowPickerCallback) {\r\n const config = this.configManager.load();\r\n const currentModelName = config.modelName || '';\r\n const isCurrentCloud = config.isLocalModel !== true;\r\n\r\n // Fetch models from backend\r\n const modelsConfig = await fetchModelsConfig();\r\n\r\n this.onShowPickerCallback({\r\n message: 'Select Cloud Model',\r\n type: 'model',\r\n choices: modelsConfig.models.map((modelConfig, index) => {\r\n const isCurrent = isCurrentCloud && currentModelName === modelConfig.name;\r\n return {\r\n label: `${modelConfig.name} - ${modelConfig.description}${isCurrent ? ' [CURRENT]' : ''}`,\r\n value: `${index}` // Use index as unique identifier\r\n };\r\n })\r\n });\r\n return; // Don't send a text response, picker will handle it\r\n }\r\n } else {\r\n // Unrecognized subcommand - show help\r\n responseMessage = `Usage: /models [local|cloud]\r\n\r\n /models local - Select from locally installed Ollama models\r\n /models cloud - Select from cloud models (Centaurus backend)\r\n /models - Default: show cloud models`;\r\n }\r\n break;\r\n\r\n case 'mcp':\r\n if (this.mcpCommandHandler) {\r\n responseMessage = await this.mcpCommandHandler.handleCommand(args);\r\n } else {\r\n responseMessage = '❌ MCP is not initialized. Please restart the CLI.';\r\n }\r\n break;\r\n\r\n case 'docs':\r\n // Open documentation URL in default browser\r\n const docsUrl = 'https://centauruslabs.in/docs';\r\n const { exec } = await import('child_process');\r\n const platform = process.platform;\r\n\r\n if (platform === 'win32') {\r\n exec(`start \"\" \"${docsUrl}\"`);\r\n } else if (platform === 'darwin') {\r\n exec(`open \"${docsUrl}\"`);\r\n } else {\r\n exec(`xdg-open \"${docsUrl}\"`);\r\n }\r\n\r\n responseMessage = `📖 Opening documentation in your browser...\\n\\n${docsUrl}`;\r\n break;\r\n\r\n case 'chat':\r\n // Chat management commands\r\n const chatSubCommand = args[0]?.toLowerCase();\r\n\r\n if (chatSubCommand === 'resume' || !chatSubCommand) {\r\n // Show list of saved chats for resuming (interactive picker)\r\n const chats = localChatStorage.listChats();\r\n\r\n if (chats.length === 0) {\r\n responseMessage = 'No saved chats found.\\n\\nStart a new conversation and it will be automatically saved!';\r\n } else {\r\n // Format chat list for picker display\r\n if (this.onShowChatPickerCallback) {\r\n this.onShowChatPickerCallback(chats, this.currentChatId);\r\n return; // Don't send a text response, picker will handle it\r\n } else {\r\n // Fallback: show as text if no picker callback\r\n responseMessage = 'Saved Chats:\\n\\n' +\r\n chats.slice(0, 10).map((chat, i) => {\r\n const date = new Date(chat.updatedAt).toLocaleDateString();\r\n const time = new Date(chat.updatedAt).toLocaleTimeString();\r\n return `${i + 1}. ${chat.title}\\n ${date} ${time} | ${chat.messageCount} messages`;\r\n }).join('\\n\\n') +\r\n (chats.length > 10 ? `\\n\\n...and ${chats.length - 10} more chats` : '');\r\n }\r\n }\r\n } else if (chatSubCommand === 'list') {\r\n // Show read-only list of saved chats (no selection)\r\n const chats = localChatStorage.listChats();\r\n\r\n if (chats.length === 0) {\r\n responseMessage = 'No saved chats found.\\n\\nStart a new conversation and it will be automatically saved!';\r\n } else {\r\n // Format chat list for read-only display\r\n if (this.onShowChatListCallback) {\r\n this.onShowChatListCallback(chats, this.currentChatId);\r\n return; // Don't send a text response, UI will handle it\r\n } else {\r\n // Fallback: show as text\r\n responseMessage = '📚 Saved Chats:\\\\n\\\\n' +\r\n chats.slice(0, 10).map((chat, i) => {\r\n const date = new Date(chat.updatedAt).toLocaleDateString();\r\n const time = new Date(chat.updatedAt).toLocaleTimeString();\r\n const isCurrent = chat.id === this.currentChatId ? ' (current)' : '';\r\n return `${i + 1}. ${chat.title}${isCurrent}\\\\n 📅 ${date} ${time} | 💬 ${chat.messageCount} messages`;\r\n }).join('\\\\n\\\\n') +\r\n (chats.length > 10 ? `\\\\n\\\\n...and ${chats.length - 10} more chats` : '');\r\n }\r\n }\r\n } else if (chatSubCommand === 'delete') {\r\n // Show list of saved chats for deletion\r\n const chats = localChatStorage.listChats();\r\n\r\n if (chats.length === 0) {\r\n responseMessage = 'No saved chats to delete.';\r\n } else {\r\n // Format chat list for delete picker display\r\n if (this.onShowChatDeletePickerCallback) {\r\n this.onShowChatDeletePickerCallback(chats, this.currentChatId);\r\n return; // Don't send a text response, picker will handle it\r\n } else {\r\n responseMessage = '❌ Delete picker not available. Please use the UI to delete chats.';\r\n }\r\n }\r\n } else if (chatSubCommand === 'new') {\r\n // Start a new chat session (old one is already saved)\r\n this.startNewChat();\r\n // Clear UI messages\r\n if (this.onRestoreMessagesCallback) {\r\n this.onRestoreMessagesCallback([]);\r\n }\r\n responseMessage = '✅ Started a new chat session!\\n\\nYour previous conversation has been saved and can be resumed with /chat resume.';\r\n } else if (chatSubCommand === 'rename') {\r\n // Show list of saved chats for renaming\r\n const chats = localChatStorage.listChats();\r\n\r\n if (chats.length === 0) {\r\n responseMessage = 'No saved chats to rename.';\r\n } else {\r\n // Format chat list for rename picker display\r\n if (this.onShowChatRenamePickerCallback) {\r\n this.onShowChatRenamePickerCallback(chats, this.currentChatId);\r\n return; // Don't send a text response, picker will handle it\r\n } else {\r\n responseMessage = '❌ Rename picker not available. Please use the UI to rename chats.';\r\n }\r\n }\r\n } else {\r\n responseMessage = `Unknown /chat subcommand: ${chatSubCommand}\\\\n\\\\nUsage:\\\\n /chat resume - Resume a previous chat session\\\\n /chat list - List all saved chats\\\\n /chat delete - Delete a saved chat\\\\n /chat new - Start a new chat session\\\\n /chat rename - Rename a saved chat`;\r\n }\r\n break;\r\n\r\n case 'copy-chat-context':\r\n try {\r\n const { formatChatForClipboard } = await import('./utils/chat-formatter.js');\r\n const { copyTextToClipboard } = await import('./utils/text-clipboard.js');\r\n\r\n if (this.uiMessageHistory.length === 0) {\r\n responseMessage = 'No messages in current chat to copy.';\r\n } else {\r\n const formattedChat = formatChatForClipboard(this.uiMessageHistory);\r\n const success = await copyTextToClipboard(formattedChat);\r\n\r\n if (success) {\r\n responseMessage = '✅ Chat content copied to clipboard!';\r\n } else {\r\n responseMessage = '❌ Failed to copy to clipboard.\\n\\n' +\r\n 'This might happen if clipboard access is not available in your environment.\\n' +\r\n 'Try running the CLI in a terminal with clipboard access.';\r\n }\r\n }\r\n } catch (error: any) {\r\n responseMessage = `❌ Error copying chat content: ${error.message}`;\r\n }\r\n break;\r\n\r\n case 'exit':\r\n process.exit(0);\r\n break;\r\n\r\n case 'revert': {\r\n const checkpointArg = args[0];\r\n if (!checkpointArg) {\r\n responseMessage = '❌ Usage: `/revert <checkpoint-id>`\\nType `/revert ` and use autocomplete to select a checkpoint.';\r\n break;\r\n }\r\n\r\n if (!this.checkpointManager) {\r\n responseMessage = '❌ No checkpoints available in this session.';\r\n break;\r\n }\r\n\r\n try {\r\n // Smart context matching: determine if we can revert based on current vs checkpoint context\r\n const currentContext = this.contextManager.getCurrentContext();\r\n const allCheckpoints = this.checkpointManager.list();\r\n const targetCheckpoint = allCheckpoints.find(c => c.id === checkpointArg);\r\n\r\n if (!targetCheckpoint) {\r\n responseMessage = `❌ Checkpoint \"${checkpointArg}\" not found.`;\r\n break;\r\n }\r\n\r\n // Context validation for revert\r\n const cpContextType = targetCheckpoint.contextType;\r\n const currentContextType = currentContext.type;\r\n\r\n if (cpContextType !== 'local' && currentContextType === 'local') {\r\n // Remote checkpoint but we're local — can't reconnect to SSH to revert files\r\n const sessionType = cpContextType.toUpperCase();\r\n const sessionInfo = targetCheckpoint.remoteSessionInfo;\r\n const target = sessionInfo?.connectionString || sessionInfo?.hostname || 'the remote machine';\r\n responseMessage = `❌ This checkpoint was created during a ${sessionType} session (${target}).\\n` +\r\n `You are not currently connected to that session.\\n\\n` +\r\n `Please reconnect to the ${sessionType} session first, then retry /revert.`;\r\n break;\r\n }\r\n\r\n if (cpContextType === 'local' && currentContextType !== 'local') {\r\n // Local checkpoint but we're in a remote session — wrong context\r\n responseMessage = `❌ This checkpoint was created in a local session.\\n` +\r\n `You are currently in a ${currentContextType.toUpperCase()} session.\\n\\n` +\r\n `Please exit your remote session first, then retry /revert.`;\r\n break;\r\n }\r\n\r\n if (cpContextType !== 'local' && currentContextType !== 'local' && cpContextType !== currentContextType) {\r\n // Both remote but different types (e.g., SSH checkpoint but in Docker now)\r\n responseMessage = `❌ This checkpoint was created in a ${cpContextType.toUpperCase()} session, ` +\r\n `but you are currently in a ${currentContextType.toUpperCase()} session.\\n\\n` +\r\n `Please connect to the correct ${cpContextType.toUpperCase()} session first.`;\r\n break;\r\n }\r\n\r\n // For remote checkpoints with mismatched hosts, validate the connection target\r\n if (cpContextType !== 'local' && targetCheckpoint.remoteSessionInfo) {\r\n const cpHost = targetCheckpoint.remoteSessionInfo.hostname;\r\n const currentHost = currentContext.metadata.hostname;\r\n if (cpHost && currentHost && cpHost !== currentHost) {\r\n responseMessage = `❌ This checkpoint was created on ${cpHost}, ` +\r\n `but you are currently connected to ${currentHost}.\\n\\n` +\r\n `Please connect to ${cpHost} first, then retry /revert.`;\r\n break;\r\n }\r\n }\r\n\r\n // Build handler for remote revert if needed\r\n let revertHandler: RemoteFileHandler | undefined;\r\n if (cpContextType !== 'local' && currentContext.handler) {\r\n if (typeof currentContext.handler.readFile === 'function' &&\r\n typeof currentContext.handler.writeFile === 'function' &&\r\n typeof currentContext.handler.executeCommand === 'function' &&\r\n typeof currentContext.handler.isConnected === 'function') {\r\n revertHandler = currentContext.handler as RemoteFileHandler;\r\n }\r\n }\r\n\r\n const result = await this.checkpointManager.revertToCheckpoint(checkpointArg, revertHandler);\r\n\r\n // Find checkpoint index for UI truncation\r\n const checkpoints = this.checkpointManager.list();\r\n const checkpointIndex = checkpoints.findIndex(c => c.id === checkpointArg);\r\n\r\n // Truncate conversation history to the checkpoint\r\n if (checkpointIndex >= 0) {\r\n const checkpoint = result.checkpoint;\r\n const checkpointPromptForUi = this.normalizePromptForInputBar(checkpoint.prompt);\r\n\r\n // Populate the input bar with the reverted prompt\r\n if (this.onSetInputCallback) {\r\n this.onSetInputCallback(checkpointPromptForUi);\r\n }\r\n\r\n // Use conversationIndex from metadata if available (robust)\r\n // This avoids issues with duplicate prompts getting truncated at the wrong occurrence\r\n if (typeof checkpoint.conversationIndex === 'number' &&\r\n checkpoint.conversationIndex >= 0 &&\r\n checkpoint.conversationIndex < this.conversationHistory.length) {\r\n\r\n // Truncate to index (exclusive) to remove the message from history\r\n // This allows the user to \"edit\" the message in the input bar\r\n this.conversationHistory = this.conversationHistory.slice(0, checkpoint.conversationIndex);\r\n quickLog(`[${new Date().toISOString()}] [Revert] Truncated conversation history to index ${checkpoint.conversationIndex} (exclusive)\\n`);\r\n\r\n } else {\r\n // Fallback to string matching (legacy or missing index)\r\n const targetPrompt = checkpointPromptForUi;\r\n // Find the user message with this prompt in conversationHistory\r\n let foundIndex = -1;\r\n // Use a broader search to find the matching message\r\n // The prompt might be a substring or exact match\r\n const searchPrompt = targetPrompt.slice(0, 50);\r\n\r\n for (let i = 0; i < this.conversationHistory.length; i++) {\r\n const msg = this.conversationHistory[i];\r\n if (msg.role === 'user' && msg.content && msg.content.includes(searchPrompt)) {\r\n foundIndex = i;\r\n break;\r\n }\r\n }\r\n\r\n if (foundIndex >= 0) {\r\n // Keep messages up to but NOT including this user message\r\n this.conversationHistory = this.conversationHistory.slice(0, foundIndex);\r\n quickLog(`[${new Date().toISOString()}] [Revert] Truncated conversation history to index ${foundIndex} (exclusive, using string match)\\n`);\r\n }\r\n }\r\n\r\n // Fix: Also truncate UI message history to ensure consistency\r\n // Use uiMessageIndex from metadata if available (robust)\r\n if (typeof checkpoint.uiMessageIndex === 'number' &&\r\n checkpoint.uiMessageIndex >= 0 &&\r\n checkpoint.uiMessageIndex < this.uiMessageHistory.length) {\r\n\r\n // Truncate to index (exclusive), subtract 1 to exclude the user's message\r\n // (uiMessageIndex was recorded AFTER the user message was added, so it includes it)\r\n // The user's message is placed into the input bar instead (via onSetInputCallback)\r\n const truncateUiIndex = Math.max(0, checkpoint.uiMessageIndex - 1);\r\n this.uiMessageHistory = this.uiMessageHistory.slice(0, truncateUiIndex);\r\n quickLog(`[${new Date().toISOString()}] [Revert] Truncated UI message history to index ${truncateUiIndex} (exclusive, adjusted from ${checkpoint.uiMessageIndex})\\n`);\r\n\r\n } else {\r\n // Fallback to string matching (legacy or missing index)\r\n const targetPrompt = checkpointPromptForUi;\r\n const searchPrompt = targetPrompt.slice(0, 50);\r\n\r\n let foundUiIndex = -1;\r\n for (let i = 0; i < this.uiMessageHistory.length; i++) {\r\n const msg = this.uiMessageHistory[i];\r\n if (msg.role === 'user' && msg.content && msg.content.includes(searchPrompt)) {\r\n foundUiIndex = i;\r\n break;\r\n }\r\n }\r\n\r\n if (foundUiIndex >= 0) {\r\n // Keep messages up to but NOT including this user message\r\n this.uiMessageHistory = this.uiMessageHistory.slice(0, foundUiIndex);\r\n quickLog(`[${new Date().toISOString()}] [Revert] Truncated UI message history to index ${foundUiIndex} (exclusive, using string match)\\n`);\r\n }\r\n }\r\n\r\n // Recompute token count from the truncated history.\r\n // This keeps the context indicator in sync even before any follow-up prompt.\r\n await this.updateTokenCount().catch(err => {\r\n quickLog(`[${new Date().toISOString()}] [Revert] Failed to update token count after revert: ${err}\\n`);\r\n });\r\n\r\n // After revert, re-check full reverted context and auto-compact again if needed.\r\n // This ensures reverted chats also honor the same context thresholds.\r\n try {\r\n const modelConfig = this.configManager.load();\r\n const modelForCompaction = modelConfig.model || modelConfig.modelName || 'gemini-2.5-flash';\r\n const { getModelContextWindowSync } = await import('./config/models.js');\r\n const maxTokensForRevert = getModelContextWindowSync(modelForCompaction);\r\n const revertCompaction = await this.maybeAutoCompactContext(modelForCompaction, maxTokensForRevert);\r\n\r\n quickLog(\r\n `[${new Date().toISOString()}] [Revert] Post-revert compaction: triggered=${revertCompaction.triggered}, compacted=${revertCompaction.compactedCount}, usage=${revertCompaction.afterUsagePercent.toFixed(1)}%\\n`\r\n );\r\n } catch (compactionError: any) {\r\n quickLog(\r\n `[${new Date().toISOString()}] [Revert] Post-revert auto-compaction check failed: ${compactionError?.message || compactionError}\\n`\r\n );\r\n }\r\n\r\n // Remove the reverted checkpoint AND all checkpoints created after it\r\n // They should not appear in autocomplete anymore\r\n this.checkpointManager.removeCheckpointsFrom(checkpointArg);\r\n\r\n // Build the success message BEFORE calling handleChatPickerSelection\r\n // This will be passed as a parameter and included in the same React setState call\r\n quickLog(`[${new Date().toISOString()}] [Revert] Building success message for checkpoint \"${checkpointArg}\"\\n`);\r\n const truncatedPrompt = checkpointPromptForUi.length > 50\r\n ? checkpointPromptForUi.slice(0, 50) + '...'\r\n : checkpointPromptForUi;\r\n let revertSuccessMessage = `✅ Reverted to: \"${truncatedPrompt}\"\\n` +\r\n `Restored ${result.restored} files, removed ${result.removed} files.`;\r\n if (result.errors.length > 0) {\r\n revertSuccessMessage += `\\n⚠️ Warnings: ${result.errors.join(', ')}`;\r\n }\r\n\r\n // Save the truncated state to disk\r\n if (this.currentChatId) {\r\n quickLog(`[${new Date().toISOString()}] [Revert] Saving truncated chat to disk (chatId: ${this.currentChatId})\\n`);\r\n quickLog(`[${new Date().toISOString()}] [Revert] uiMessageHistory.length BEFORE save: ${this.uiMessageHistory.length}\\n`);\r\n this.saveCurrentChat({ allowEmpty: true });\r\n\r\n // Reload the chat to force UI update ensures consistency\r\n // This simulates a /chat resume command which correctly populates the UI\r\n // Pass skipLoadedMessage=true and the revert success message\r\n // The success message is appended to restoredMessages in handleChatPickerSelection\r\n // This ensures it's part of the same React setState call, avoiding race conditions\r\n quickLog(`[${new Date().toISOString()}] [Revert] Calling handleChatPickerSelection with revertSuccessMessage=\"${revertSuccessMessage.substring(0, 50)}...\"\\n`);\r\n const currentContext = this.contextManager.getCurrentContext();\r\n const preserveRemote = currentContext.type !== 'local' && typeof currentContext.handler?.isConnected === 'function'\r\n ? currentContext.handler.isConnected()\r\n : currentContext.type !== 'local';\r\n await this.handleChatPickerSelection(this.currentChatId, true, revertSuccessMessage, preserveRemote);\r\n\r\n // Clear responseMessage so it doesn't get sent via onDirectMessageCallback\r\n // (the message was already included in the restored messages above)\r\n quickLog(`[${new Date().toISOString()}] [Revert] handleChatPickerSelection completed, clearing responseMessage\\n`);\r\n responseMessage = '';\r\n } else {\r\n // Fallback: if no current chat, show the message via normal callback\r\n quickLog(`[${new Date().toISOString()}] [Revert] No currentChatId, using fallback responseMessage\\n`);\r\n responseMessage = revertSuccessMessage;\r\n }\r\n }\r\n } catch (error: any) {\r\n responseMessage = `❌ Failed to revert: ${error.message}`;\r\n }\r\n break;\r\n }\r\n\r\n case 'add-command':\r\n case 'add-command-auto-detect':\r\n // Handle custom command auto-detect management\r\n const { CustomCommandsManager } = await import('./utils/custom-commands-manager.js');\r\n const customCmdManager = CustomCommandsManager.getInstance();\r\n await customCmdManager.initialize();\r\n\r\n const addCmdSubCommand = args[0]?.toLowerCase();\r\n\r\n if (!addCmdSubCommand || addCmdSubCommand === 'list') {\r\n // List all custom commands\r\n const commands = customCmdManager.listCommands();\r\n if (commands.length === 0) {\r\n responseMessage = '📋 No custom commands configured.\\n\\n' +\r\n 'Add commands with: /add-command add <command>\\n' +\r\n 'These commands will be detected as terminal commands in Auto mode.';\r\n } else {\r\n responseMessage = `📋 Custom Terminal Commands (${commands.length}):\\n\\n` +\r\n commands.map(cmd => ` • ${cmd}`).join('\\n') +\r\n '\\n\\nUsage:\\n' +\r\n ' /add-command add <command> - Add a command\\n' +\r\n ' /add-command delete <command> - Delete a command';\r\n }\r\n } else if (addCmdSubCommand === 'add') {\r\n // Add a custom command\r\n const cmdToAdd = args.slice(1).join(' ').trim();\r\n if (!cmdToAdd) {\r\n responseMessage = '❌ Please specify a command to add.\\n\\nUsage: /add-command add <command>';\r\n } else {\r\n const result = await customCmdManager.addCommand(cmdToAdd);\r\n if (result.success) {\r\n responseMessage = `✅ ${result.message}\\n\\nThis word will now be detected as a terminal command in Auto mode.`;\r\n } else {\r\n responseMessage = `❌ ${result.message}`;\r\n }\r\n }\r\n } else if (addCmdSubCommand === 'delete') {\r\n // Delete a custom command\r\n const cmdToDelete = args.slice(1).join(' ').trim();\r\n if (!cmdToDelete) {\r\n responseMessage = '❌ Please specify a command to delete.\\n\\nUsage: /add-command delete <command>';\r\n } else {\r\n const result = await customCmdManager.deleteCommand(cmdToDelete);\r\n if (result.success) {\r\n responseMessage = `✅ ${result.message}`;\r\n } else {\r\n responseMessage = `❌ ${result.message}`;\r\n }\r\n }\r\n } else {\r\n responseMessage = `Unknown /add-command subcommand: ${addCmdSubCommand}\\n\\n` +\r\n 'Usage:\\n' +\r\n ' /add-command list - List all custom commands\\n' +\r\n ' /add-command add <command> - Add a custom command\\n' +\r\n ' /add-command delete <command> - Delete a custom command';\r\n }\r\n break;\r\n\r\n case 'background-task':\r\n case 'bkg':\r\n case 'bg-task':\r\n // Background task management commands\r\n const bkgSubCommand = args[0]?.toLowerCase();\r\n\r\n if (bkgSubCommand === 'list' || !bkgSubCommand) {\r\n // Show list of running background tasks\r\n const runningTasks = BackgroundTaskManager.getRunningTasks();\r\n\r\n if (runningTasks.length === 0) {\r\n responseMessage = '📭 No background tasks running.\\n\\nSwitch to Background mode (Ctrl+D) to run commands in the background.';\r\n } else {\r\n // Show picker for task selection\r\n if (this.onShowBackgroundTaskPickerCallback) {\r\n this.onShowBackgroundTaskPickerCallback(runningTasks);\r\n return; // Don't send text response, picker will handle it\r\n } else {\r\n // Fallback: show as text\r\n responseMessage = '🔄 Running Background Tasks:\\n\\n' +\r\n runningTasks.map((task, i) => {\r\n const durationSec = Math.round(task.durationMs / 1000);\r\n return `${i + 1}. ${task.command}\\n 📁 ${task.cwd} | ⏱️ ${durationSec}s running`;\r\n }).join('\\n\\n');\r\n }\r\n }\r\n } else if (bkgSubCommand === 'cancel') {\r\n // Show list of running tasks for cancellation\r\n const runningTasks = BackgroundTaskManager.getRunningTasks();\r\n\r\n if (runningTasks.length === 0) {\r\n responseMessage = '📭 No background tasks to cancel.';\r\n } else {\r\n // Show picker for task cancellation\r\n if (this.onShowBackgroundTaskCancelPickerCallback) {\r\n this.onShowBackgroundTaskCancelPickerCallback(runningTasks);\r\n return; // Don't send text response, picker will handle it\r\n } else {\r\n responseMessage = '❌ Cancel picker not available. Use /background-task list to see tasks.';\r\n }\r\n }\r\n } else {\r\n responseMessage = `Unknown /background-task subcommand: ${bkgSubCommand}\\n\\n` +\r\n 'Usage:\\n' +\r\n ' /background-task list - List running background tasks\\n' +\r\n ' /background-task cancel - Cancel a running background task';\r\n }\r\n break;\r\n\r\n case 'workflow':\r\n case 'wf':\r\n // Workflow management commands\r\n const wfSubCommand = args[0]?.toLowerCase();\r\n\r\n if (!wfSubCommand) {\r\n // Show workflow help\r\n responseMessage = `📋 **Workflow Commands:**\r\n\r\n **/workflow new** - Create a new workflow\r\n **/workflow list** - List saved workflows\r\n **/workflow run <name>** - Run a workflow\r\n **/workflow view <name>** - View workflow steps\r\n **/workflow delete <name>** - Delete a workflow\r\n\r\n**What are workflows?**\r\nWorkflows let you save and replay sequences of commands and instructions.\r\nCreate once, run many times across different machines.`;\r\n } else if (wfSubCommand === 'new' || wfSubCommand === 'create') {\r\n // Check for nested subcommand (manual or learn-workflow)\r\n const newSubCommand = args[1]?.toLowerCase();\r\n\r\n if (newSubCommand === 'manual') {\r\n // If learning mode is active, cancel it instead of starting manual mode\r\n if (this.workflowLearningActive) {\r\n responseMessage = this.cancelWorkflowLearning('Switching to manual mode');\r\n break; // Don't start manual mode, just show cancellation message\r\n }\r\n\r\n // Show workflow creator screen (manual mode)\r\n if (this.onShowWorkflowCreatorCallback) {\r\n this.onShowWorkflowCreatorCallback();\r\n return; // Don't send text response, UI will handle it\r\n } else {\r\n responseMessage = '❌ Workflow creator not available. Please update the CLI.';\r\n }\r\n } else if (newSubCommand === 'learn-workflow') {\r\n // Toggle workflow learning mode\r\n const result = this.toggleWorkflowLearning();\r\n if (result === null) {\r\n return; // UI will handle the screen change\r\n }\r\n responseMessage = result;\r\n } else if (newSubCommand) {\r\n // Unknown subcommand\r\n responseMessage = `❌ Unknown workflow new subcommand: ${newSubCommand}. Use 'manual' or 'learn-workflow'.`;\r\n } else {\r\n // No subcommand - show help for new options\r\n responseMessage = `📋 **Create a New Workflow:**\r\n\r\n **/workflow new manual** - Manually create by typing steps\r\n **/workflow new learn-workflow** - Learn from your commands and prompts\r\n\r\n**Manual Mode:** Type each step one at a time, toggle between command/instruction mode.\r\n\r\n**Learn Mode:** Start learning, then run commands and prompts naturally. Run the command again to save.`;\r\n }\r\n } else if (wfSubCommand === 'list' || wfSubCommand === 'ls') {\r\n // List saved workflows\r\n const workflows = workflowStorage.list();\r\n\r\n if (workflows.length === 0) {\r\n responseMessage = '📭 No workflows saved yet.\\n\\nCreate one with: /workflow new';\r\n } else {\r\n responseMessage = `📋 **Saved Workflows (${workflows.length}):**\\n\\n`;\r\n for (const wf of workflows) {\r\n const date = new Date(wf.updatedAt).toLocaleDateString();\r\n responseMessage += `• **${wf.name}** (${wf.stepCount} steps)\\n`;\r\n if (wf.description) {\r\n responseMessage += ` ${wf.description}\\n`;\r\n }\r\n responseMessage += ` Last updated: ${date}\\n\\n`;\r\n }\r\n responseMessage += 'Use `/workflow run <name>` to execute a workflow.';\r\n }\r\n } else if (wfSubCommand === 'run' || wfSubCommand === 'execute') {\r\n // Run a workflow\r\n const wfName = args.slice(1).join(' ').trim();\r\n\r\n if (!wfName) {\r\n responseMessage = 'Usage: /workflow run <name>\\n\\nUse /workflow list to see available workflows.';\r\n } else {\r\n // Cancel workflow learning if active before running a different workflow\r\n const runCancelMsg = this.cancelWorkflowLearning('Running another workflow');\r\n if (runCancelMsg && this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(runCancelMsg);\r\n }\r\n\r\n const workflow = workflowStorage.load(wfName);\r\n\r\n if (!workflow) {\r\n responseMessage = `❌ Workflow '${wfName}' not found.\\n\\nUse /workflow list to see available workflows.`;\r\n } else {\r\n // Start workflow execution\r\n responseMessage = `🚀 Starting workflow: **${workflow.name}**\\n\\n` +\r\n `${workflow.steps.length} steps to execute...\\n\\n` +\r\n workflowStorage.formatWorkflowForDisplay(workflow);\r\n\r\n // Send initial response\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(responseMessage);\r\n }\r\n\r\n // Trigger workflow execution by sending the workflow to the AI\r\n // Format the workflow as a prompt for the AI to execute\r\n const workflowPrompt = this.buildWorkflowPrompt(workflow);\r\n\r\n // Use setTimeout to allow the UI to update before starting execution\r\n setTimeout(() => {\r\n this.handleMessage(workflowPrompt, { isolatedWorkflow: true }).catch((err: any) => {\r\n quickLog(`[${new Date().toISOString()}] [Workflow] Error executing workflow: ${err.message}\\n`);\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(`❌ Error executing workflow: ${err.message}`);\r\n }\r\n });\r\n }, 100);\r\n\r\n return;\r\n }\r\n }\r\n } else if (wfSubCommand === 'view' || wfSubCommand === 'show') {\r\n // View a workflow's steps\r\n const wfName = args.slice(1).join(' ').trim();\r\n\r\n if (!wfName) {\r\n responseMessage = 'Usage: /workflow view <name>';\r\n } else {\r\n const workflow = workflowStorage.load(wfName);\r\n\r\n if (!workflow) {\r\n responseMessage = `❌ Workflow '${wfName}' not found.\\n\\nUse /workflow list to see available workflows.`;\r\n } else {\r\n responseMessage = workflowStorage.formatWorkflowForDisplay(workflow);\r\n }\r\n }\r\n } else if (wfSubCommand === 'delete' || wfSubCommand === 'rm' || wfSubCommand === 'remove') {\r\n // Delete a workflow\r\n const wfName = args.slice(1).join(' ').trim();\r\n\r\n if (!wfName) {\r\n responseMessage = 'Usage: /workflow delete <name>';\r\n } else {\r\n const result = workflowStorage.delete(wfName);\r\n\r\n if (result.success) {\r\n responseMessage = `✅ Workflow '${wfName}' deleted successfully.`;\r\n } else {\r\n responseMessage = `❌ ${result.error}`;\r\n }\r\n }\r\n } else {\r\n responseMessage = `Unknown /workflow subcommand: ${wfSubCommand}\\n\\n` +\r\n 'Usage:\\n' +\r\n ' /workflow new - Create a new workflow\\n' +\r\n ' /workflow list - List saved workflows\\n' +\r\n ' /workflow run <name> - Run a workflow\\n' +\r\n ' /workflow view <name> - View workflow steps\\n' +\r\n ' /workflow delete <name> - Delete a workflow';\r\n }\r\n break;\r\n\r\n case 'rules':\r\n const rulesSubCommand = args[0]?.toLowerCase();\r\n\r\n if (!rulesSubCommand) {\r\n responseMessage = `📜 **Rule Commands:**\r\n\r\n **/rules list** - List saved rules\r\n **/rules add** - Create a new reusable rule\r\n **/rules edit <name>** - Edit an existing rule\r\n **/rules delete <name>** - Delete a saved rule\r\n\r\n**How to use rules**\r\nAfter saving a rule, reference it in any prompt with \\`@rules:<name>\\`.`;\r\n } else if (rulesSubCommand === 'list' || rulesSubCommand === 'ls') {\r\n const rules = rulesStorage.list();\r\n\r\n if (rules.length === 0) {\r\n responseMessage = '📭 No rules saved yet.\\n\\nCreate one with: /rules add';\r\n } else {\r\n responseMessage = `📜 **Saved Rules (${rules.length}):**\\n\\n`;\r\n for (const rule of rules) {\r\n const date = new Date(rule.updatedAt).toLocaleDateString();\r\n responseMessage += `• **${rule.name}**\\n`;\r\n responseMessage += ` ${rule.preview}\\n`;\r\n responseMessage += ` Last updated: ${date}\\n`;\r\n responseMessage += ` Use in prompts: @rules:${rule.name}\\n\\n`;\r\n }\r\n }\r\n } else if (rulesSubCommand === 'add' || rulesSubCommand === 'new' || rulesSubCommand === 'create') {\r\n if (this.onShowRulesEditorCallback) {\r\n this.onShowRulesEditorCallback({ mode: 'add' });\r\n return;\r\n }\r\n\r\n responseMessage = '❌ Rules editor not available. Please update the CLI.';\r\n } else if (rulesSubCommand === 'edit') {\r\n const ruleName = args.slice(1).join(' ').trim();\r\n\r\n if (!ruleName) {\r\n responseMessage = 'Usage: /rules edit <name>';\r\n break;\r\n }\r\n\r\n const rule = rulesStorage.load(ruleName);\r\n if (!rule) {\r\n responseMessage = `❌ Rule '${ruleName}' not found.\\n\\nUse /rules list to see available rules.`;\r\n break;\r\n }\r\n\r\n if (this.onShowRulesEditorCallback) {\r\n this.onShowRulesEditorCallback({\r\n mode: 'edit',\r\n initialName: rule.name,\r\n initialContent: rule.content\r\n });\r\n return;\r\n }\r\n\r\n responseMessage = '❌ Rules editor not available. Please update the CLI.';\r\n } else if (rulesSubCommand === 'delete' || rulesSubCommand === 'rm' || rulesSubCommand === 'remove') {\r\n const ruleName = args.slice(1).join(' ').trim();\r\n\r\n if (!ruleName) {\r\n responseMessage = 'Usage: /rules delete <name>';\r\n break;\r\n }\r\n\r\n const result = rulesStorage.delete(ruleName);\r\n if (result.success) {\r\n const deletedRuleName = rulesStorage.sanitizeName(ruleName);\r\n responseMessage = `✅ Rule '${deletedRuleName}' deleted successfully.`;\r\n } else {\r\n responseMessage = `❌ ${result.error}`;\r\n }\r\n } else {\r\n responseMessage = `Unknown /rules subcommand: ${rulesSubCommand}\\n\\n` +\r\n 'Usage:\\n' +\r\n ' /rules list - List saved rules\\n' +\r\n ' /rules add - Create a new rule\\n' +\r\n ' /rules edit <name> - Edit a saved rule\\n' +\r\n ' /rules delete <name> - Delete a saved rule';\r\n }\r\n break;\r\n\r\n case 'sync':\r\n // Sync local data to/from cloud\r\n if (!apiClient.isAuthenticated()) {\r\n responseMessage = '❌ You must be signed in to sync data.\\n\\nUse /sign-in to authenticate first.';\r\n break;\r\n }\r\n\r\n // Parse subcommand\r\n const syncSubcommand = args[0]?.toLowerCase();\r\n\r\n if (!syncSubcommand) {\r\n // Show sync help\r\n responseMessage = '☁️ Sync Commands:\\n\\n' +\r\n ' /sync upload - Upload local data to cloud (overwrites cloud data)\\n' +\r\n ' /sync restore - Download cloud data and restore locally (overwrites local data)\\n\\n' +\r\n 'Use these commands to backup or restore your chat history and settings.';\r\n break;\r\n }\r\n\r\n if (syncSubcommand === 'upload') {\r\n try {\r\n responseMessage = '☁️ Uploading data to cloud...';\r\n\r\n // Send initial response\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(responseMessage);\r\n }\r\n\r\n // Gather all local data\r\n const config = this.configManager.load();\r\n const chats = localChatStorage.listChats();\r\n\r\n // Load full chat data for each chat\r\n const fullChats = chats.map(chatMeta => {\r\n const fullChat = localChatStorage.loadChat(chatMeta.id);\r\n return fullChat;\r\n }).filter(chat => chat !== null);\r\n\r\n // Create sync data structure\r\n const syncData = {\r\n version: 1,\r\n exportedAt: new Date().toISOString(),\r\n config: {\r\n model: config.model,\r\n modelName: config.modelName,\r\n enhancedQuality: config.enhancedQuality,\r\n autonomousMode: config.autonomousMode,\r\n },\r\n chats: fullChats,\r\n metadata: {\r\n totalChats: fullChats.length,\r\n totalMessages: fullChats.reduce((sum, chat) => sum + (chat?.messageCount || 0), 0),\r\n }\r\n };\r\n\r\n // Upload to backend\r\n const result = await apiClient.uploadSyncData(syncData);\r\n\r\n responseMessage = `✅ Data uploaded successfully!\\n\\n` +\r\n `📊 Upload Summary:\\n` +\r\n ` • Chats: ${fullChats.length}\\n` +\r\n ` • Messages: ${syncData.metadata.totalMessages}\\n` +\r\n ` • Version: ${result.version}\\n` +\r\n ` • Updated: ${new Date(result.updatedAt).toLocaleString()}\\n\\n` +\r\n `Your local data is now backed up to the cloud.`;\r\n } catch (error: any) {\r\n responseMessage = `❌ Upload failed: ${error.message}\\n\\nPlease try again later.`;\r\n }\r\n } else if (syncSubcommand === 'restore') {\r\n try {\r\n responseMessage = '☁️ Downloading data from cloud...';\r\n\r\n // Send initial response\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(responseMessage);\r\n }\r\n\r\n // Get sync data from backend\r\n const cloudData = await apiClient.getSyncData();\r\n\r\n if (!cloudData) {\r\n responseMessage = '❌ No cloud data found.\\n\\nUse /sync upload first to backup your data.';\r\n break;\r\n }\r\n\r\n const syncData = cloudData.syncData;\r\n\r\n // Validate sync data structure\r\n if (!syncData || typeof syncData !== 'object') {\r\n responseMessage = '❌ Invalid cloud data format.\\n\\nPlease try uploading again with /sync upload.';\r\n break;\r\n }\r\n\r\n // Restore config if present\r\n if (syncData.config) {\r\n const currentConfig = this.configManager.load();\r\n if (syncData.config.model) {\r\n currentConfig.model = syncData.config.model;\r\n }\r\n if (syncData.config.modelName) {\r\n currentConfig.modelName = syncData.config.modelName;\r\n }\r\n if (typeof syncData.config.enhancedQuality === 'boolean') {\r\n currentConfig.enhancedQuality = syncData.config.enhancedQuality;\r\n }\r\n if (typeof syncData.config.autonomousMode === 'boolean') {\r\n currentConfig.autonomousMode = syncData.config.autonomousMode;\r\n }\r\n this.configManager.save(currentConfig);\r\n }\r\n\r\n // Restore chats if present\r\n let restoredChats = 0;\r\n let restoredMessages = 0;\r\n if (syncData.chats && Array.isArray(syncData.chats)) {\r\n for (const chat of syncData.chats) {\r\n if (chat && chat.id) {\r\n // Import each chat to local storage\r\n const success = localChatStorage.importChat(chat);\r\n if (success) {\r\n restoredChats++;\r\n restoredMessages += chat.messageCount || chat.messages?.length || 0;\r\n }\r\n }\r\n }\r\n }\r\n\r\n responseMessage = `✅ Data restored successfully!\\n\\n` +\r\n `📊 Restore Summary:\\n` +\r\n ` • Chats restored: ${restoredChats}\\n` +\r\n ` • Messages restored: ${restoredMessages}\\n` +\r\n ` • Cloud version: ${cloudData.version}\\n` +\r\n ` • Last updated: ${new Date(cloudData.updatedAt).toLocaleString()}\\n\\n` +\r\n `Your local data has been restored from the cloud.\\n` +\r\n `Note: Existing local chats with the same ID were overwritten.`;\r\n } catch (error: any) {\r\n responseMessage = `❌ Restore failed: ${error.message}\\n\\nPlease try again later.`;\r\n }\r\n } else {\r\n responseMessage = `❌ Unknown sync subcommand: ${syncSubcommand}\\n\\n` +\r\n 'Available subcommands:\\n' +\r\n ' /sync upload - Upload local data to cloud\\n' +\r\n ' /sync restore - Download and restore cloud data locally';\r\n }\r\n break;\r\n\r\n default:\r\n responseMessage = `Unknown command: /${cmd}\\nType /help for available commands.`;\r\n }\r\n\r\n // Send command response directly to history (not streaming)\r\n if (responseMessage && this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(responseMessage);\r\n }\r\n }\r\n\r\n /**\r\n * Set callback for showing chat picker\r\n */\r\n setOnShowChatPickerCallback(callback: (chats: LocalChatMeta[], currentChatId: string | null) => void): void {\r\n this.onShowChatPickerCallback = callback;\r\n }\r\n\r\n /**\r\n * Set callback for showing chat delete picker\r\n */\r\n setOnShowChatDeletePickerCallback(callback: (chats: LocalChatMeta[], currentChatId: string | null) => void): void {\r\n this.onShowChatDeletePickerCallback = callback;\r\n }\r\n\r\n /**\r\n * Set callback for showing chat list (read-only view)\r\n */\r\n setOnShowChatListCallback(callback: (chats: LocalChatMeta[], currentChatId: string | null) => void): void {\r\n this.onShowChatListCallback = callback;\r\n }\r\n\r\n /**\r\n * Set callback for showing chat rename picker\r\n */\r\n setOnShowChatRenamePickerCallback(callback: (chats: LocalChatMeta[], currentChatId: string | null) => void): void {\r\n this.onShowChatRenamePickerCallback = callback;\r\n }\r\n\r\n /**\r\n * Set callback for background mode change\r\n */\r\n setOnBackgroundModeChange(callback: (backgroundMode: boolean) => void): void {\r\n this.onBackgroundModeChange = callback;\r\n }\r\n\r\n /**\r\n * Set callback for background task count change\r\n */\r\n setOnBackgroundTaskCountChange(callback: (count: number) => void): void {\r\n this.onBackgroundTaskCountChange = callback;\r\n // Subscribe to BackgroundTaskManager count changes\r\n BackgroundTaskManager.on('countChanged', (count: number) => {\r\n this.onBackgroundTaskCountChange?.(count);\r\n });\r\n }\r\n\r\n /**\r\n * Set callback for setting Auto mode (used after background task starts)\r\n */\r\n setOnSetAutoMode(callback: (enabled: boolean) => void): void {\r\n this.onSetAutoMode = callback;\r\n }\r\n\r\n /**\r\n * Set callback for showing background task picker\r\n */\r\n setOnShowBackgroundTaskPickerCallback(callback: (tasks: BackgroundTaskInfo[]) => void): void {\r\n this.onShowBackgroundTaskPickerCallback = callback;\r\n }\r\n\r\n /**\r\n * Set callback for showing background task cancel picker\r\n */\r\n setOnShowBackgroundTaskCancelPickerCallback(callback: (tasks: BackgroundTaskInfo[]) => void): void {\r\n this.onShowBackgroundTaskCancelPickerCallback = callback;\r\n }\r\n\r\n /**\r\n * Toggle background mode\r\n */\r\n toggleBackgroundMode(): void {\r\n this.backgroundMode = !this.backgroundMode;\r\n if (this.onBackgroundModeChange) {\r\n this.onBackgroundModeChange(this.backgroundMode);\r\n }\r\n }\r\n\r\n /**\r\n * Get current background mode state\r\n */\r\n getBackgroundMode(): boolean {\r\n return this.backgroundMode;\r\n }\r\n\r\n /**\r\n * Handle background task cancellation\r\n */\r\n handleBackgroundTaskCancel(taskId: string): void {\r\n const task = BackgroundTaskManager.getTask(taskId);\r\n const success = BackgroundTaskManager.cancelTask(taskId);\r\n\r\n if (success) {\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(`🛑 Cancelled background task: ${task?.command || taskId}`);\r\n }\r\n } else {\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(`❌ Failed to cancel task. It may have already completed.`);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Handle background task selection - show task output in focus mode\r\n */\r\n handleBackgroundTaskSelection(taskId: string): void {\r\n const task = BackgroundTaskManager.getTask(taskId);\r\n if (!task) {\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(`❌ Task not found: ${taskId}`);\r\n }\r\n return;\r\n }\r\n\r\n // Get the full output from the task\r\n const output = BackgroundTaskManager.getTaskOutput(taskId);\r\n\r\n // If we have the view callback, use it to show in focus mode (InteractiveShell)\r\n if (this.onBackgroundTaskViewCallback) {\r\n // Create event handlers for the App.tsx useEffect to subscribe to\r\n let onOutputHandler: ((chunk: string) => void) | undefined;\r\n let onCompleteHandler: ((exitCode: number) => void) | undefined;\r\n\r\n // Subscribe to future output updates for this task\r\n const outputListener = (taskIdEvent: string, chunk: string) => {\r\n if (taskIdEvent === taskId && onOutputHandler) {\r\n onOutputHandler(chunk);\r\n }\r\n };\r\n\r\n // Subscribe to task completion\r\n const completeListener = (taskIdEvent: string, exitCode: number) => {\r\n if (taskIdEvent === taskId && onCompleteHandler) {\r\n onCompleteHandler(exitCode);\r\n // Cleanup listeners\r\n BackgroundTaskManager.off('taskOutput', outputListener);\r\n BackgroundTaskManager.off('taskCompleted', completeListener);\r\n }\r\n };\r\n\r\n BackgroundTaskManager.on('taskOutput', outputListener);\r\n BackgroundTaskManager.on('taskCompleted', completeListener);\r\n\r\n // Invoke the callback to set up shellState in App.tsx\r\n this.onBackgroundTaskViewCallback({\r\n id: taskId,\r\n command: task.command,\r\n cwd: task.cwd,\r\n output,\r\n isRunning: task.isRunning,\r\n onOutput: (handler) => { onOutputHandler = handler; },\r\n onComplete: (handler) => { onCompleteHandler = handler; }\r\n });\r\n\r\n // If task is already complete, immediately trigger completion\r\n if (!task.isRunning && task.exitCode !== undefined) {\r\n setTimeout(() => {\r\n if (onCompleteHandler) {\r\n onCompleteHandler(task.exitCode!);\r\n }\r\n }, 100);\r\n }\r\n } else {\r\n // Fallback: show as static message\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(`📺 Viewing output for: ${task.command}\\n\\n${output || '(no output yet)'}`);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Set callback for showing a background task in focus mode\r\n */\r\n setOnBackgroundTaskViewCallback(callback: (task: { id: string; command: string; cwd: string; output: string; isRunning: boolean; onOutput: (handler: (chunk: string) => void) => void; onComplete: (handler: (exitCode: number) => void) => void }) => void): void {\r\n this.onBackgroundTaskViewCallback = callback;\r\n }\r\n\r\n /**\r\n * Handle chat rename operation\r\n */\r\n handleChatRename(chatId: string, newTitle: string): void {\r\n const chat = localChatStorage.loadChat(chatId);\r\n const oldTitle = chat?.title || 'Unknown chat';\r\n\r\n const success = localChatStorage.renameChat(chatId, newTitle);\r\n\r\n if (success) {\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(`✏️ Renamed chat: \"${oldTitle}\" → \"${newTitle}\"`);\r\n }\r\n } else {\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback('❌ Failed to rename the chat.');\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Handle chat delete picker selection\r\n */\r\n async handleChatDeleteSelection(chatId: string): Promise<void> {\r\n const chat = localChatStorage.loadChat(chatId);\r\n const chatTitle = chat?.title || 'Unknown chat';\r\n const isDeletingCurrentChat = this.currentChatId === chatId;\r\n\r\n // Get the backend conversation ID (UUID) if available for file deletion\r\n // Files are stored with the backend UUID, not the local chat ID\r\n const fileDeleteId = chat?.backendConversationId || chatId;\r\n\r\n const success = localChatStorage.deleteChat(chatId);\r\n\r\n if (success) {\r\n if (this.checkpointManager) {\r\n this.checkpointManager.deleteCheckpointsForChat(chatId);\r\n }\r\n\r\n // Clean up any files associated with this conversation (images in GCS)\r\n // Use backendConversationId (UUID) if available, as that's what files are stored under\r\n // This is a fire-and-forget operation - don't block on it\r\n if (apiClient.isAuthenticated()) {\r\n apiClient.deleteConversationFiles(fileDeleteId).catch(() => {\r\n // Silently ignore - files might not exist or backend error\r\n });\r\n }\r\n\r\n // If we deleted the current chat, start a new chat\r\n if (isDeletingCurrentChat) {\r\n this.startNewChat();\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(`🗑️ Deleted current chat: \"${chatTitle}\"\\n\\n🆕 Started a new chat session.`);\r\n }\r\n // Clear UI messages via restore callback with empty array\r\n if (this.onRestoreMessagesCallback) {\r\n this.onRestoreMessagesCallback([]);\r\n }\r\n } else {\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(`🗑️ Deleted chat: \"${chatTitle}\"`);\r\n }\r\n }\r\n } else {\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback('❌ Failed to delete the selected chat.');\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Handle background mode execution - runs command in background and returns immediately\r\n * Supports both local and remote shells (SSH, WSL, Docker)\r\n */\r\n private handleBackgroundModeExecution(command: string): void {\r\n if (!command.trim()) {\r\n return;\r\n }\r\n\r\n // Get current context to determine if we're in a remote shell\r\n const currentContext = this.contextManager.getCurrentContext();\r\n\r\n let taskId: string;\r\n let effectiveCwd: string;\r\n let remoteContextDisplay: string | undefined;\r\n\r\n if (currentContext.type === 'local') {\r\n // Local execution - use PTY\r\n effectiveCwd = this.cwd;\r\n taskId = BackgroundTaskManager.startTask(command, effectiveCwd);\r\n } else {\r\n // Remote execution - use appropriate remote PTY\r\n const metadata = currentContext.metadata;\r\n effectiveCwd = metadata?.workingDirectory || '~';\r\n\r\n // Build remote context display string\r\n if (currentContext.type === 'ssh' && metadata) {\r\n remoteContextDisplay = `${metadata.username || 'user'}@${metadata.hostname || 'remote'}`;\r\n } else if (currentContext.type === 'wsl' && metadata) {\r\n remoteContextDisplay = `wsl:${metadata.distroName || 'wsl'}`;\r\n } else if (currentContext.type === 'docker' && metadata) {\r\n remoteContextDisplay = `docker:${metadata.containerId?.substring(0, 12) || 'container'}`;\r\n }\r\n\r\n // Create the remote PTY process and register with BackgroundTaskManager\r\n if (currentContext.type === 'ssh') {\r\n const sshClient = currentContext.handler?.client;\r\n if (!sshClient) {\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback('❌ SSH client not available for background task');\r\n }\r\n return;\r\n }\r\n\r\n // Start remote task first to get callbacks\r\n const remoteTask = BackgroundTaskManager.startRemoteTask(\r\n command,\r\n effectiveCwd,\r\n remoteContextDisplay || 'ssh'\r\n );\r\n taskId = remoteTask.id;\r\n\r\n // Create SSH PTY with the callbacks from BackgroundTaskManager\r\n const sshPty = runSSHCommand(\r\n sshClient,\r\n command,\r\n effectiveCwd,\r\n remoteTask.onData,\r\n remoteTask.onExit\r\n );\r\n remoteTask.setRemotePty(sshPty);\r\n\r\n } else if (currentContext.type === 'wsl') {\r\n const distribution = metadata?.distroName || 'Ubuntu';\r\n\r\n // Start remote task first to get callbacks\r\n const remoteTask = BackgroundTaskManager.startRemoteTask(\r\n command,\r\n effectiveCwd,\r\n remoteContextDisplay || 'wsl'\r\n );\r\n taskId = remoteTask.id;\r\n\r\n // Create WSL PTY with the callbacks from BackgroundTaskManager\r\n const wslPty = runWSLCommand(\r\n distribution,\r\n command,\r\n effectiveCwd,\r\n remoteTask.onData,\r\n remoteTask.onExit\r\n );\r\n remoteTask.setRemotePty(wslPty);\r\n\r\n } else if (currentContext.type === 'docker') {\r\n const containerId = metadata?.containerId;\r\n if (!containerId) {\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback('❌ Docker container ID not available for background task');\r\n }\r\n return;\r\n }\r\n\r\n // Check if Docker is nested inside SSH\r\n const parentContext = this.contextManager.getParentContext();\r\n\r\n // Start remote task first to get callbacks\r\n const remoteTask = BackgroundTaskManager.startRemoteTask(\r\n command,\r\n effectiveCwd,\r\n remoteContextDisplay || 'docker'\r\n );\r\n taskId = remoteTask.id;\r\n\r\n if (parentContext && parentContext.type === 'ssh') {\r\n // Nested Docker inside SSH: route docker exec command through SSH\r\n const sshClient = parentContext.handler?.client;\r\n if (!sshClient) {\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback('❌ SSH client not available for nested Docker background task');\r\n }\r\n return;\r\n }\r\n\r\n // Build docker exec command to run via SSH\r\n const escapedCommand = command.replace(/\"/g, '\\\\\"');\r\n const dockerCommand = `docker exec -w \"${effectiveCwd}\" ${containerId} sh -c \"${escapedCommand}\"`;\r\n\r\n // Create SSH PTY to run the docker command\r\n const sshPty = runSSHCommand(\r\n sshClient,\r\n dockerCommand,\r\n parentContext.metadata.workingDirectory || '~',\r\n remoteTask.onData,\r\n remoteTask.onExit\r\n );\r\n remoteTask.setRemotePty(sshPty);\r\n } else {\r\n // Local Docker: use standard runDockerCommand\r\n const dockerPty = runDockerCommand(\r\n containerId,\r\n command,\r\n effectiveCwd,\r\n remoteTask.onData,\r\n remoteTask.onExit\r\n );\r\n remoteTask.setRemotePty(dockerPty);\r\n }\r\n\r\n } else {\r\n // Unknown remote type - fall back to local\r\n effectiveCwd = this.cwd;\r\n taskId = BackgroundTaskManager.startTask(command, effectiveCwd);\r\n }\r\n }\r\n\r\n // Notify user that task started\r\n const count = BackgroundTaskManager.getRunningCount();\r\n const locationInfo = remoteContextDisplay\r\n ? `\\n📍 Running on: ${remoteContextDisplay}`\r\n : '';\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(`🚀 Background task started: ${command}${locationInfo}\\n\\nTask ID: ${taskId}\\nUse /background-task list to see running tasks.`);\r\n }\r\n\r\n // Optionally record to history for AI context\r\n this.recordShellCommandToHistory(command, '[Running in background]', effectiveCwd);\r\n\r\n // Reset to Auto mode after starting task\r\n this.backgroundMode = false;\r\n if (this.onBackgroundModeChange) {\r\n this.onBackgroundModeChange(false);\r\n }\r\n // Enable Auto mode in InputBox\r\n if (this.onSetAutoMode) {\r\n this.onSetAutoMode(true);\r\n }\r\n }\r\n\r\n\r\n /**\r\n * Record a user shell command and its output to conversation history\r\n * This allows the AI to see what commands the user ran in command mode\r\n */\r\n private recordShellCommandToHistory(command: string, output: string, cwd: string, exitCode?: number): void {\r\n // Add as a user message so AI knows what the user did\r\n const shellCommandInfo = exitCode !== undefined && exitCode !== 0\r\n ? `[User ran shell command in ${cwd}]\\nCommand: ${command}\\nExit Code: ${exitCode}\\nOutput:\\n${output}`\r\n : `[User ran shell command in ${cwd}]\\nCommand: ${command}\\nOutput:\\n${output || '(no output)'}`;\r\n\r\n this.conversationHistory.push({\r\n role: 'user',\r\n content: shellCommandInfo\r\n });\r\n }\r\n\r\n /**\r\n * Save current conversation to local storage\r\n */\r\n private saveCurrentChat(options?: { allowEmpty?: boolean }): void {\r\n const allowEmpty = options?.allowEmpty ?? false;\r\n\r\n // Only save if there are messages (AI conversation or shell commands),\r\n // unless explicitly forced (revert-to-first-message edge case).\r\n if (this.conversationHistory.length === 0 && !allowEmpty) {\r\n return;\r\n }\r\n\r\n // Generate chat ID if not exists\r\n if (!this.currentChatId) {\r\n if (allowEmpty) {\r\n // Avoid creating brand-new empty chats.\r\n return;\r\n }\r\n this.currentChatId = localChatStorage.generateChatId();\r\n }\r\n\r\n // Convert to StoredMessage format (AI context)\r\n const storedMessages: StoredMessage[] = this.conversationHistory.map(msg => ({\r\n role: msg.role as StoredMessage['role'],\r\n content: msg.content as string,\r\n tool_calls: msg.tool_calls,\r\n tool_call_id: msg.tool_call_id,\r\n }));\r\n\r\n // Convert UI messages to serializable format\r\n const storedUIMessages: StoredUIMessage[] = this.uiMessageHistory.map(msg => ({\r\n id: msg.id,\r\n role: msg.role,\r\n content: msg.content,\r\n timestamp: msg.timestamp?.toISOString(),\r\n toolExecution: msg.toolExecution ? {\r\n toolName: msg.toolExecution.toolName,\r\n status: msg.toolExecution.status,\r\n result: msg.toolExecution.result,\r\n error: msg.toolExecution.error,\r\n arguments: msg.toolExecution.arguments,\r\n } : undefined,\r\n shouldStream: msg.shouldStream,\r\n isCommandMode: msg.isCommandMode,\r\n tool_call_id: msg.tool_call_id,\r\n tool_calls: msg.tool_calls,\r\n thinkingDuration: msg.thinkingDuration,\r\n taskCompletion: msg.taskCompletion,\r\n planAccepted: msg.planAccepted,\r\n connectionStatus: msg.connectionStatus, // For SSH/WSL/Docker connection status boxes\r\n }));\r\n\r\n // Capture remote context if in remote environment (SSH/WSL/Docker)\r\n // Use null to explicitly clear remote context when not in remote (so resuming won't reconnect)\r\n let remoteContext: StoredRemoteContext | null = null;\r\n let remoteContextStack: StoredRemoteContext[] | null = null;\r\n const currentContext = this.contextManager.getCurrentContext();\r\n\r\n // Build remoteContextStack for nested sessions\r\n if (currentContext.type !== 'local' && this.connectionCommandStack.length > 0) {\r\n remoteContextStack = [];\r\n\r\n // Build stack of contexts from the connectionCommandStack and cwdStack\r\n // cwdStack[0] = local CWD before first remote\r\n // cwdStack[1] = first remote CWD before second remote (if nested)\r\n // connectionCommandStack[0] = first connection command\r\n // connectionCommandStack[1] = second connection command (if nested)\r\n for (let i = 0; i < this.connectionCommandStack.length; i++) {\r\n const connCmd = this.connectionCommandStack[i];\r\n // Detect the type from the command\r\n let ctxType: 'ssh' | 'wsl' | 'docker' = 'ssh';\r\n if (connCmd.startsWith('docker ') || connCmd.startsWith('docker-compose ')) {\r\n ctxType = 'docker';\r\n } else if (connCmd.startsWith('wsl')) {\r\n ctxType = 'wsl';\r\n }\r\n\r\n // Get the CWD before this remote connection\r\n const cwdBefore = i < this.cwdStack.length ? this.cwdStack[i] : process.cwd();\r\n\r\n // For the last (current) context, use the actual metadata\r\n const isLastContext = i === this.connectionCommandStack.length - 1;\r\n const remoteCwd = isLastContext ? currentContext.metadata.workingDirectory : (this.cwdStack[i + 1] || '~');\r\n\r\n const storedCtx: StoredRemoteContext = {\r\n type: ctxType,\r\n connectionCommand: connCmd,\r\n remoteCwd: remoteCwd,\r\n localCwdBeforeRemote: cwdBefore,\r\n metadata: isLastContext ? {\r\n hostname: currentContext.metadata.hostname,\r\n username: currentContext.metadata.username,\r\n distroName: currentContext.metadata.distroName,\r\n containerId: currentContext.metadata.containerId,\r\n port: currentContext.metadata.port,\r\n } : {}\r\n };\r\n\r\n remoteContextStack.push(storedCtx);\r\n }\r\n\r\n // For backward compatibility, also set remoteContext to the last (current) context\r\n if (remoteContextStack.length > 0) {\r\n remoteContext = remoteContextStack[remoteContextStack.length - 1];\r\n }\r\n }\r\n\r\n // Determine the local CWD to save (use base of cwdStack if in remote, otherwise current cwd)\r\n const cwdToSave = currentContext.type !== 'local' && this.cwdStack.length > 0\r\n ? this.cwdStack[0]\r\n : this.cwd;\r\n\r\n const checkpointIndex: StoredCheckpointMeta[] | undefined = this.checkpointManager\r\n ? this.checkpointManager.list().map(cp => this.toStoredCheckpointMeta(cp))\r\n : undefined;\r\n\r\n try {\r\n localChatStorage.saveChat(\r\n this.currentChatId,\r\n storedMessages,\r\n storedUIMessages,\r\n cwdToSave,\r\n remoteContext,\r\n remoteContextStack,\r\n checkpointIndex\r\n );\r\n\r\n // Also store the backend conversation ID (UUID) for file deletion\r\n // This is the ID used for GCS file storage, not the local chat ID\r\n const backendId = conversationManager.getCurrentConversationId();\r\n if (backendId && backendId !== this.currentChatId) {\r\n localChatStorage.setBackendConversationId(this.currentChatId, backendId);\r\n }\r\n } catch (error) {\r\n // Silently ignore save errors - don't interrupt the user\r\n }\r\n }\r\n\r\n /**\r\n * Load a chat from local storage and restore it\r\n */\r\n loadChat(chatId: string, options?: { preserveCwd?: boolean }): boolean {\r\n const chat = localChatStorage.loadChat(chatId);\r\n if (!chat) {\r\n return false;\r\n }\r\n\r\n // Clear current history and load the saved chat\r\n this.conversationHistory = chat.messages.map(msg => ({\r\n role: msg.role,\r\n content: msg.content,\r\n tool_calls: msg.tool_calls,\r\n tool_call_id: msg.tool_call_id,\r\n }));\r\n\r\n // IMPORTANT: Also restore UI message history from saved chat\r\n // This ensures that when the chat is saved again (e.g., after revert), \r\n // the correct UI messages are preserved\r\n if (chat.uiMessages && chat.uiMessages.length > 0) {\r\n this.uiMessageHistory = chat.uiMessages.map(msg => ({\r\n id: msg.id,\r\n role: msg.role,\r\n content: msg.content,\r\n timestamp: msg.timestamp ? new Date(msg.timestamp) : undefined,\r\n toolExecution: msg.toolExecution,\r\n shouldStream: false,\r\n isCommandMode: msg.isCommandMode,\r\n tool_call_id: msg.tool_call_id,\r\n tool_calls: msg.tool_calls,\r\n thinkingDuration: msg.thinkingDuration,\r\n taskCompletion: msg.taskCompletion,\r\n planAccepted: msg.planAccepted,\r\n connectionStatus: msg.connectionStatus,\r\n }));\r\n }\r\n\r\n // Set the current chat ID to continue the conversation\r\n this.currentChatId = chatId;\r\n this.conversationStarted = true;\r\n\r\n // Restore checkpoint state for this chat immediately so /revert works\r\n // right after /chat resume, even before a new AI message is sent.\r\n this.ensureCheckpointManagerForChat(chatId, chat.checkpointIndex);\r\n\r\n // Restore CWD if saved (important for commands to run in correct directory)\r\n // Allow skipping when preserving an active remote session\r\n if (chat.cwd && !options?.preserveCwd) {\r\n this.cwd = chat.cwd;\r\n // Update context manager if available\r\n if (this.contextManager) {\r\n this.contextManager.updateWorkingDirectory(chat.cwd);\r\n }\r\n // Notify UI of CWD change\r\n if (this.onCwdChange) {\r\n this.onCwdChange(chat.cwd);\r\n }\r\n }\r\n\r\n // Reset context limit state when loading a chat\r\n // We'll recalculate it based on the loaded conversation\r\n this.setContextLimitReachedState(false);\r\n\r\n // Update token count to reflect loaded conversation\r\n // This will also check if the loaded chat is near the limit\r\n this.updateTokenCount().catch(err => {\r\n quickLog(`[${new Date().toISOString()}] [loadChatFromPicker] Failed to update token count: ${err}\\n`);\r\n });\r\n\r\n // Clean up any orphaned tool calls from previous interrupted sessions\r\n // This prevents \"improperly formed request\" errors when continuing conversations\r\n this.cleanupOrphanedToolCalls();\r\n quickLog(`[${new Date().toISOString()}] [loadChat] Cleaned up conversation history after load\\n`);\r\n\r\n return true;\r\n }\r\n\r\n /**\r\n * Handle chat picker selection\r\n * @param chatId The ID of the chat to load\r\n * @param skipLoadedMessage If true, skip showing the \"Loaded chat\" message (used during revert to avoid React setState race condition)\r\n * @param revertSuccessMessage If provided, this message will be appended to restoredMessages before calling onRestoreMessagesCallback (used during revert to include success message in the same React setState call, avoiding race conditions)\r\n * @param preserveRemoteSession If true, keep current SSH/WSL/Docker connection and skip reconnect logic (used during revert in a live remote session)\r\n */\r\n async handleChatPickerSelection(\r\n chatId: string,\r\n skipLoadedMessage: boolean = false,\r\n revertSuccessMessage?: string,\r\n preserveRemoteSession: boolean = false\r\n ): Promise<void> {\r\n const chat = localChatStorage.loadChat(chatId);\r\n\r\n if (!chat) {\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback('❌ Failed to load the selected chat.');\r\n }\r\n return;\r\n }\r\n\r\n // IMPORTANT: Clean up current remote session before loading new chat\r\n // This ensures that switching from a remote chat to a local chat properly resets the state\r\n const currentContext = this.contextManager.getCurrentContext();\r\n const shouldPreserveRemote = preserveRemoteSession && currentContext.type !== 'local';\r\n if (!shouldPreserveRemote && currentContext.type !== 'local') {\r\n // Disconnect from current remote session\r\n if (currentContext.handler) {\r\n try {\r\n await currentContext.handler.disconnect();\r\n } catch (error) {\r\n // Ignore disconnect errors - we're switching chats anyway\r\n }\r\n }\r\n\r\n // Pop context to return to local\r\n this.contextManager.popContext();\r\n\r\n // Clear remote context tracking\r\n this.cwdStack = [];\r\n this.connectionCommandStack = [];\r\n }\r\n\r\n // Load AI context\r\n this.loadChat(chatId, shouldPreserveRemote ? { preserveCwd: true } : undefined);\r\n\r\n // If we're preserving a live remote session (revert case), restore the current remote CWD\r\n if (shouldPreserveRemote) {\r\n this.cwd = currentContext.metadata.workingDirectory;\r\n this.contextManager.updateWorkingDirectory(this.cwd);\r\n if (this.onCwdChange) {\r\n this.onCwdChange(this.cwd);\r\n }\r\n }\r\n\r\n // Restore UI messages if available\r\n quickLog(`[${new Date().toISOString()}] [handleChatPickerSelection] chat.uiMessages exists: ${!!chat.uiMessages}, length: ${chat.uiMessages?.length ?? 0}\\n`);\r\n quickLog(`[${new Date().toISOString()}] [handleChatPickerSelection] onRestoreMessagesCallback exists: ${!!this.onRestoreMessagesCallback}\\n`);\r\n if (this.onRestoreMessagesCallback) {\r\n const sourceUiMessages = chat.uiMessages ?? [];\r\n // Convert StoredUIMessage back to Message format\r\n const restoredMessages: Message[] = sourceUiMessages.map(msg => ({\r\n id: msg.id,\r\n role: msg.role,\r\n content: msg.content,\r\n timestamp: msg.timestamp ? new Date(msg.timestamp) : undefined,\r\n toolExecution: msg.toolExecution,\r\n shouldStream: false, // Don't stream restored messages\r\n isCommandMode: msg.isCommandMode,\r\n tool_call_id: msg.tool_call_id,\r\n tool_calls: msg.tool_calls,\r\n thinkingDuration: msg.thinkingDuration,\r\n taskCompletion: msg.taskCompletion,\r\n planAccepted: msg.planAccepted,\r\n connectionStatus: msg.connectionStatus, // For SSH/WSL/Docker connection status boxes\r\n }));\r\n\r\n // If a revert success message was provided, append it to the restored messages\r\n // This ensures the message is part of the same React setState call, avoiding race conditions\r\n quickLog(`[${new Date().toISOString()}] [handleChatPickerSelection] revertSuccessMessage provided: ${!!revertSuccessMessage}\\n`);\r\n if (revertSuccessMessage) {\r\n const revertSystemMessage: Message = {\r\n id: `revert-success-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`,\r\n role: 'assistant',\r\n content: revertSuccessMessage,\r\n timestamp: new Date(),\r\n shouldStream: false,\r\n };\r\n restoredMessages.push(revertSystemMessage);\r\n quickLog(`[${new Date().toISOString()}] [handleChatPickerSelection] Appended revert success message, restoredMessages.length: ${restoredMessages.length}\\n`);\r\n }\r\n\r\n // Determine if this is a remote chat that needs reconnection\r\n const pendingContextStack = shouldPreserveRemote\r\n ? null\r\n : (chat.remoteContextStack ?? (chat.remoteContext ? [chat.remoteContext] : null));\r\n const hasRemoteContext = pendingContextStack && pendingContextStack.length > 0;\r\n\r\n // IMPORTANT: Append the \"Loaded chat\" or \"Reconnecting\" status message to restoredMessages\r\n // BEFORE calling onRestoreMessagesCallback. This prevents the message from being added\r\n // separately via onDirectMessageCallback between the two-phase restore (Phase 1: clear,\r\n // Phase 2: 50ms delayed restore), which would cause Ink's Static component to increment\r\n // its rendered count and skip the first restored message.\r\n if (!skipLoadedMessage && !revertSuccessMessage) {\r\n if (hasRemoteContext) {\r\n // For remote chats, include the reconnection notification\r\n const nestingInfo = pendingContextStack!.length > 1\r\n ? ` (${pendingContextStack!.length} levels: ${pendingContextStack!.map((c: any) => c.type).join(' > ')})`\r\n : '';\r\n const reconnectMessage: Message = {\r\n id: `reconnect-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`,\r\n role: 'assistant',\r\n content: `🔄 Reconnecting to session${nestingInfo}...`,\r\n timestamp: new Date(),\r\n shouldStream: false,\r\n };\r\n restoredMessages.push(reconnectMessage);\r\n quickLog(`[${new Date().toISOString()}] [handleChatPickerSelection] Appended reconnect message to restoredMessages\\n`);\r\n } else {\r\n // For local chats, include the \"Loaded chat\" confirmation\r\n const loadedMessage: Message = {\r\n id: `loaded-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`,\r\n role: 'assistant',\r\n content: `✅ Loaded chat: \"${chat.title}\"\\n\\nYou have ${chat.messageCount} messages in AI context. Continue your conversation!`,\r\n timestamp: new Date(),\r\n shouldStream: false,\r\n };\r\n restoredMessages.push(loadedMessage);\r\n quickLog(`[${new Date().toISOString()}] [handleChatPickerSelection] Appended loaded chat message to restoredMessages\\n`);\r\n }\r\n }\r\n\r\n quickLog(`[${new Date().toISOString()}] [handleChatPickerSelection] Calling onRestoreMessagesCallback with ${restoredMessages.length} messages\\n`);\r\n this.onRestoreMessagesCallback(restoredMessages);\r\n quickLog(`[${new Date().toISOString()}] [handleChatPickerSelection] onRestoreMessagesCallback completed\\n`);\r\n }\r\n\r\n // Attempt to restore remote context if chat was saved while in remote environment\r\n // Support both remoteContextStack (for nested sessions) and single remoteContext (backward compat)\r\n const contextStackToRestore = shouldPreserveRemote\r\n ? null\r\n : (chat.remoteContextStack ?? (chat.remoteContext ? [chat.remoteContext] : null));\r\n\r\n if (contextStackToRestore && contextStackToRestore.length > 0) {\r\n // Get the base local CWD from the first context's localCwdBeforeRemote\r\n const baseLocalCwd = contextStackToRestore[0].localCwdBeforeRemote;\r\n\r\n // Initialize stacks\r\n this.cwdStack = [baseLocalCwd];\r\n this.connectionCommandStack = [];\r\n\r\n // Note: The initial \"Reconnecting to session...\" notification is now included\r\n // in the restoredMessages array (if uiMessages were restored) to avoid Ink's Static\r\n // component count desync. Only show via onDirectMessageCallback if no UI restore happened.\r\n const nestingInfo = contextStackToRestore.length > 1\r\n ? ` (${contextStackToRestore.length} levels: ${contextStackToRestore.map(c => c.type).join(' > ')})`\r\n : '';\r\n if (this.onDirectMessageCallback && !this.onRestoreMessagesCallback) {\r\n this.onDirectMessageCallback(`🔄 Reconnecting to session${nestingInfo}...`);\r\n }\r\n\r\n this.isReconnecting = true;\r\n try {\r\n // Sequential reconnection through the stack\r\n let previousCwd = baseLocalCwd;\r\n for (let i = 0; i < contextStackToRestore.length; i++) {\r\n const remoteCtx = contextStackToRestore[i];\r\n const { type, connectionCommand, remoteCwd } = remoteCtx;\r\n const levelInfo = contextStackToRestore.length > 1 ? ` [${i + 1}/${contextStackToRestore.length}]` : '';\r\n\r\n // Show connecting status for this level\r\n // Only show via onDirectMessageCallback if no UI restore happened (same reason as reconnecting message above)\r\n if (this.onDirectMessageCallback && !this.onRestoreMessagesCallback) {\r\n this.onDirectMessageCallback(`🔄${levelInfo} Connecting to ${type.toUpperCase()}...`);\r\n }\r\n\r\n if (this.onConnectionStatusUpdate) {\r\n this.onConnectionStatusUpdate({\r\n type: type,\r\n status: 'connecting',\r\n connectionString: this.buildConnectionString(type, remoteCtx.metadata)\r\n });\r\n }\r\n\r\n try {\r\n // Detect and connect using the saved command\r\n const detection = this.commandDetector.detect(connectionCommand);\r\n if (!detection) {\r\n throw new Error(`Could not detect handler for: ${connectionCommand}`);\r\n }\r\n\r\n // Create a new instance of the handler to ensure each level of the connection\r\n // has its own state (client, stream, etc.), critical for nested sessions of the same type\r\n const handler = detection.handler.createNew();\r\n\r\n let context: SubshellContext;\r\n\r\n // Check if this is a nested connection (i > 0 means we're inside a remote session)\r\n if (i > 0) {\r\n const currentContext = this.contextManager.getCurrentContext();\r\n if (handler.connectFromRemote) {\r\n context = await handler.connectFromRemote(connectionCommand, previousCwd, currentContext);\r\n } else {\r\n throw new Error(`Nested connections not supported by ${type} handler`);\r\n }\r\n } else {\r\n // First level: connect from local\r\n context = await handler.connect(connectionCommand, previousCwd);\r\n }\r\n\r\n // Push to stacks\r\n this.connectionCommandStack.push(connectionCommand);\r\n if (i > 0) {\r\n this.cwdStack.push(previousCwd); // Save the previous remote CWD for nested exits\r\n }\r\n this.contextManager.pushContext(context);\r\n\r\n // Navigate to saved remote CWD\r\n if (remoteCwd && remoteCwd !== context.metadata.workingDirectory) {\r\n try {\r\n await this.contextManager.executeCommand(`cd \"${remoteCwd}\"`);\r\n } catch (cdError) {\r\n // Failed to cd to saved path - warn but continue\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(`⚠️ Could not restore ${type} directory: ${remoteCwd}`);\r\n }\r\n }\r\n }\r\n\r\n // Update previousCwd for next iteration\r\n previousCwd = this.contextManager.getCurrentContext().metadata.workingDirectory;\r\n\r\n // Show success for this level\r\n if (this.onConnectionStatusUpdate) {\r\n this.onConnectionStatusUpdate({\r\n type: type,\r\n status: 'connected',\r\n connectionString: this.buildConnectionString(type, remoteCtx.metadata)\r\n });\r\n }\r\n\r\n } catch (error: any) {\r\n // Connection failed at this level - fall back to local mode\r\n // If we partially connected, disconnect all and reset\r\n while (this.contextManager.getCurrentContext().type !== 'local') {\r\n const ctx = this.contextManager.getCurrentContext();\r\n if (ctx.handler) {\r\n try { await ctx.handler.disconnect(); } catch (e) { /* ignore */ }\r\n }\r\n this.contextManager.popContext();\r\n }\r\n\r\n this.cwdStack = [];\r\n this.connectionCommandStack = [];\r\n\r\n if (this.onConnectionStatusUpdate) {\r\n this.onConnectionStatusUpdate({\r\n type: type,\r\n status: 'error',\r\n connectionString: this.buildConnectionString(type, remoteCtx.metadata),\r\n error: error.message\r\n });\r\n }\r\n\r\n const failedAt = contextStackToRestore.length > 1 ? ` at level ${i + 1} (${type})` : '';\r\n if (this.onDirectMessageCallback) {\r\n this.onDirectMessageCallback(`⚠️ Loaded chat: \"${chat.title}\"\\n\\n❌ Could not reconnect${failedAt}: ${error.message}\\n\\n📁 Restored to local directory: ${baseLocalCwd}`);\r\n }\r\n return;\r\n }\r\n }\r\n\r\n // All levels connected successfully\r\n if (this.onDirectMessageCallback) {\r\n const successInfo = contextStackToRestore.length > 1\r\n ? `🔗 Reconnected through ${contextStackToRestore.length} levels`\r\n : '';\r\n if (successInfo) {\r\n this.onDirectMessageCallback(successInfo);\r\n }\r\n }\r\n return;\r\n } finally {\r\n this.isReconnecting = false;\r\n void this.maybeProcessSessionCommandQueue();\r\n }\r\n }\r\n\r\n // No remote context - show regular confirmation and restore CWD\r\n if (!shouldPreserveRemote && chat.cwd && !chat.remoteContext) {\r\n if (fs.existsSync(chat.cwd)) {\r\n this.cwd = chat.cwd;\r\n this.contextManager.updateWorkingDirectory(chat.cwd);\r\n if (this.onCwdChange) {\r\n this.onCwdChange(chat.cwd);\r\n }\r\n }\r\n }\r\n\r\n // Skip loaded message if called from revert (to avoid React setState race condition)\r\n // Also skip if we already included it in the restoredMessages array (when uiMessages were restored)\r\n const uiMessagesWereRestored = !!this.onRestoreMessagesCallback;\r\n if (this.onDirectMessageCallback && !skipLoadedMessage && !uiMessagesWereRestored) {\r\n const responseMessage = `✅ Loaded chat: \"${chat.title}\"\\n\\nYou have ${chat.messageCount} messages in AI context. Continue your conversation!`;\r\n this.onDirectMessageCallback(responseMessage);\r\n }\r\n }\r\n\r\n /**\r\n * Build a connection string for display based on remote context metadata\r\n */\r\n private buildConnectionString(type: 'ssh' | 'wsl' | 'docker', metadata?: StoredRemoteContext['metadata']): string {\r\n if (!metadata) return type;\r\n\r\n if (type === 'ssh' && metadata.hostname) {\r\n return `${metadata.username || 'user'}@${metadata.hostname}`;\r\n } else if (type === 'wsl' && metadata.distroName) {\r\n return metadata.distroName;\r\n } else if (type === 'docker' && metadata.containerId) {\r\n return metadata.containerId.substring(0, 12);\r\n }\r\n return type;\r\n }\r\n\r\n /**\r\n * Get the current chat ID\r\n */\r\n getCurrentChatId(): string | null {\r\n return this.currentChatId;\r\n }\r\n\r\n /**\r\n * Start a new chat session\r\n */\r\n startNewChat(): void {\r\n const currentContext = this.contextManager.getCurrentContext();\r\n this.conversationHistory = [];\r\n this.currentChatId = null;\r\n this.conversationStarted = false;\r\n this.uiMessageHistory = [];\r\n this.currentCheckpointId = undefined;\r\n\r\n if (this.checkpointManager) {\r\n this.checkpointManager.setCurrentChatId(null);\r\n }\r\n\r\n // Only clear stacks if we're in local context\r\n // If we're in a remote context (SSH/WSL/Docker), we want to PRESERVE the connection state\r\n // so the new chat continues in the same environment\r\n if (currentContext.type === 'local') {\r\n this.cwdStack = [];\r\n this.connectionCommandStack = [];\r\n }\r\n // Else: Preserve cwdStack and connectionCommandStack for remote sessions\r\n\r\n // Reset context limit state\r\n this.setContextLimitReachedState(false);\r\n\r\n // Update token count to reflect empty conversation\r\n this.updateTokenCount().catch(err => {\r\n quickLog(`[${new Date().toISOString()}] [startNewChat] Failed to update token count: ${err}\\n`);\r\n });\r\n }\r\n\r\n /**\r\n * Update UI message history (called from App.tsx via callback)\r\n * Also triggers auto-save to ensure the latest messages are persisted\r\n * \r\n * Note: Only saves when there's real user/AI content, not just slash command system messages.\r\n * This prevents empty chats from being created when user only runs /chat commands.\r\n */\r\n updateUIMessageHistory(messages: Message[]): void {\r\n this.uiMessageHistory = messages;\r\n\r\n // Auto-save when UI history changes, but only if there's real content\r\n // Real content is defined as: user or assistant messages (not just system slash command messages)\r\n // This ensures:\r\n // - /chat rename, /chat new, etc. don't create new chats on their own\r\n // - System messages from slash commands aren't saved permanently\r\n // - Only actual AI conversation content triggers saves\r\n\r\n const hasRealContent = this.conversationHistory.length > 0;\r\n\r\n // Only save if there's actual AI conversation history\r\n // The conversationHistory only contains real user/assistant/tool messages from AI interactions\r\n if (messages.length > 0 && hasRealContent) {\r\n this.saveCurrentChat();\r\n }\r\n }\r\n\r\n /**\r\n * Set callback for restoring UI messages\r\n */\r\n setOnRestoreMessagesCallback(callback: (messages: Message[]) => void): void {\r\n this.onRestoreMessagesCallback = callback;\r\n }\r\n\r\n /**\r\n * Set callback for setting input value (e.g., for revert)\r\n */\r\n setOnSetInput(callback: (value: string) => void): void {\r\n this.onSetInputCallback = callback;\r\n }\r\n\r\n\r\n\r\n /**\r\n * Get environment context for backend\r\n * Returns structured environment information to be sent to backend\r\n */\r\n private getEnvironmentContext(): { os: 'windows' | 'macos' | 'linux'; platform: string; shell: string; cwd: string; homeDir: string } {\r\n const platform = process.platform;\r\n const isWindows = platform === 'win32';\r\n const homeDir = process.env.HOME || process.env.USERPROFILE || '~';\r\n const shell = process.env.SHELL || process.env.ComSpec || (isWindows ? 'cmd' : 'bash');\r\n\r\n return {\r\n os: isWindows ? 'windows' : platform === 'darwin' ? 'macos' : 'linux',\r\n platform,\r\n shell,\r\n cwd: this.cwd,\r\n homeDir,\r\n };\r\n }\r\n\r\n /**\r\n * Get current mode\r\n * Returns the current mode (default, plan, or command)\r\n */\r\n private getMode(): 'default' | 'plan' | 'command' {\r\n if (this.commandMode) return 'command';\r\n if (this.planMode) return 'plan';\r\n return 'default';\r\n }\r\n\r\n private getPlanModeInstructions(): string {\r\n return `\\n\\n## PLAN MODE ACTIVE (MODE=plan)\r\n\r\nYou are currently in PLAN MODE. In this mode, you MUST:\r\n\r\n1. **DO NOT execute any implementation tools** (no write_to_file, edit_file, execute_command, etc.)\r\n2. **Call the \\`create_plan\\` tool** to present a structured plan to the user FIRST\r\n\r\n### How to Create a Plan:\r\n\r\nAnalyze the user's request, then call \\`create_plan\\` with:\r\n- A clear title describing what will be accomplished\r\n- A brief summary of the approach\r\n- An ordered list of specific, actionable tasks\r\n\r\nExample:\r\n\\`\\`\\`\r\ncreate_plan(\r\n title: \"Create Python CSV Filter Script\",\r\n summary: \"Build a Python script that reads a CSV file, filters rows based on criteria, and writes output\",\r\n tasks: [\r\n { description: \"Create sample input.csv with test data\", complexity: \"low\" },\r\n { description: \"Write csv_filter.py with read/filter/write logic\", complexity: \"medium\" },\r\n { description: \"Execute and verify the script works correctly\", complexity: \"low\" }\r\n ]\r\n)\r\n\\`\\`\\`\r\n\r\n### After Plan Approval:\r\n\r\nOnce the user approves the plan:\r\n1. Execute each task in order\r\n2. After completing each task, call \\`mark_task_complete(task_number: N)\\`\r\n3. After all tasks are done, call \\`task_complete\\` with a summary\r\n\r\n**CRITICAL: In plan mode, ALWAYS call create_plan FIRST before any other tools.**`;\r\n }\r\n\r\n /**\r\n * Toggle command mode on/off\r\n */\r\n toggleCommandMode(): void {\r\n if (this.commandMode) {\r\n // Exiting command mode - restore previous mode\r\n this.commandMode = false;\r\n this.planMode = this.previousMode === 'planning';\r\n\r\n // Notify UI\r\n if (this.onCommandModeChange) {\r\n this.onCommandModeChange(false);\r\n }\r\n if (this.onPlanModeChange) {\r\n this.onPlanModeChange(this.planMode);\r\n }\r\n } else {\r\n // Entering command mode - save current mode\r\n this.previousMode = this.planMode ? 'planning' : 'execution';\r\n this.commandMode = true;\r\n this.planMode = false;\r\n\r\n // Notify UI\r\n if (this.onCommandModeChange) {\r\n this.onCommandModeChange(true);\r\n }\r\n if (this.onPlanModeChange) {\r\n this.onPlanModeChange(false);\r\n }\r\n }\r\n }\r\n\r\n\r\n\r\n /**\r\n * Set the current interactive process\r\n * This is called by the execute_command tool when starting an interactive command\r\n */\r\n setCurrentInteractiveProcess(process: shellUtils.InteractiveProcess | undefined): void {\r\n this.currentInteractiveProcess = process;\r\n }\r\n\r\n /**\r\n * Handle command execution in command mode\r\n */\r\n private async handleCommandModeExecution(command: string): Promise<void> {\r\n if (!command.trim()) {\r\n return;\r\n }\r\n\r\n // Check if this is an interactive editor command (vim, nano, etc.)\r\n // These need full terminal control\r\n const currentContextForEditor = this.contextManager.getCurrentContext();\r\n\r\n if (isInteractiveEditorCommand(command)) {\r\n if (this.onInteractiveEditorMode) {\r\n if (currentContextForEditor.type === 'local') {\r\n // Local context: delegate to App for full terminal takeover\r\n this.onInteractiveEditorMode(true, command, this.cwd);\r\n } else {\r\n // Remote context (SSH, WSL, Docker) - pass remote context to App\r\n // Use the remote context's working directory, not the local Windows CWD\r\n const remoteCwd = currentContextForEditor.metadata?.workingDirectory || '~';\r\n const parentContext = this.contextManager.getParentContext() || undefined; // Convert null to undefined\r\n this.onInteractiveEditorMode(true, command, remoteCwd, currentContextForEditor, parentContext);\r\n }\r\n return;\r\n }\r\n }\r\n\r\n try {\r\n // Check for exit command in subshell\r\n if (command.trim() === 'exit') {\r\n const currentContext = this.contextManager.getCurrentContext();\r\n if (currentContext.type !== 'local') {\r\n // Disconnect from subshell\r\n if (currentContext.handler) {\r\n await currentContext.handler.disconnect();\r\n }\r\n\r\n // Pop context - this returns us to the parent context\r\n this.contextManager.popContext();\r\n\r\n // Check the NEW current context after popping\r\n const newContext = this.contextManager.getCurrentContext();\r\n\r\n // Pop the previous CWD from stack - this handles any nesting level\r\n const previousCwd = this.cwdStack.pop();\r\n if (previousCwd) {\r\n this.cwd = previousCwd;\r\n this.contextManager.updateWorkingDirectory(previousCwd);\r\n if (this.onCwdChange) {\r\n this.onCwdChange(previousCwd);\r\n }\r\n } else if (newContext.type !== 'local') {\r\n // Fallback: use parent context's CWD if no stack entry\r\n const parentCwd = newContext.metadata?.workingDirectory || '~';\r\n this.cwd = parentCwd;\r\n if (this.onCwdChange) {\r\n this.onCwdChange(parentCwd);\r\n }\r\n }\r\n\r\n if (newContext.type === 'local') {\r\n // Clear tracking when back to local\r\n this.connectionCommandStack.pop();\r\n }\r\n\r\n // Save chat - include remote context info if still in remote\r\n this.saveCurrentChat();\r\n\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback('✅ Exited subshell');\r\n }\r\n return;\r\n }\r\n }\r\n\r\n // WARPIFY MODE: SSH/WSL/Docker commands now run as NORMAL PTY commands\r\n // The old flow intercepted these commands and used the ssh2/etc libraries directly.\r\n // The new flow lets them run in focus mode, user enters password in terminal,\r\n // then presses Alt+E to \"warpify\" the session for AI context awareness.\r\n // The detection code is preserved below (commented) for reference.\r\n /*\r\n // Detect subshell commands (OLD FLOW - DISABLED FOR WARPIFY)\r\n const detection = this.commandDetector.detect(command);\r\n if (detection) {\r\n // ... old interception logic ...\r\n // This triggered password prompt BEFORE running the command\r\n }\r\n */\r\n\r\n\r\n // Special handling for cd command - change the actual working directory\r\n // Check for chained commands: cd path && cmd or cd path ; cmd\r\n const chainedCdMatch = command.match(/^cd\\s+([^;&]+?)\\s*(&&|;)\\s*(.+)$/);\r\n const simpleCdMatch = command.match(/^cd\\s+(.+)$/);\r\n\r\n // Determine if this starts with cd\r\n const cdMatch = chainedCdMatch || simpleCdMatch;\r\n if (cdMatch) {\r\n const currentContext = this.contextManager.getCurrentContext();\r\n const targetDir = (chainedCdMatch ? chainedCdMatch[1] : cdMatch[1]).trim();\r\n const hasChainedCommand = !!chainedCdMatch;\r\n const chainedCommand = chainedCdMatch ? chainedCdMatch[3] : null;\r\n\r\n if (currentContext.type === 'local') {\r\n // Local cd handling\r\n const newCwd = path.resolve(this.cwd, targetDir);\r\n\r\n if (!fs.existsSync(newCwd)) {\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback(`❌ Directory not found: ${targetDir}`);\r\n }\r\n return;\r\n }\r\n\r\n if (!fs.statSync(newCwd).isDirectory()) {\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback(`❌ Not a directory: ${targetDir}`);\r\n }\r\n return;\r\n }\r\n\r\n this.cwd = newCwd;\r\n this.contextManager.updateWorkingDirectory(newCwd);\r\n\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback(`Changed directory to: ${newCwd}`);\r\n }\r\n\r\n // If there's a chained command, execute it in the new directory\r\n if (hasChainedCommand && chainedCommand) {\r\n // Recursively handle the next command\r\n await this.handleCommandModeExecution(chainedCommand);\r\n }\r\n return;\r\n } else {\r\n // Subshell cd handling - execute just the cd command via handler\r\n const cdOnlyCommand = `cd ${targetDir}`;\r\n const result = await this.contextManager.executeCommand(cdOnlyCommand);\r\n\r\n if (result.exitCode === 0) {\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback(`Changed directory to: ${currentContext.metadata.workingDirectory}`);\r\n }\r\n\r\n // If there's a chained command, execute it in the new directory\r\n if (hasChainedCommand && chainedCommand) {\r\n // Recursively handle the next command\r\n await this.handleCommandModeExecution(chainedCommand);\r\n }\r\n } else {\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback(`❌ ${result.stderr || 'Failed to change directory'}`);\r\n }\r\n }\r\n return;\r\n }\r\n }\r\n\r\n // Get current context to determine correct CWD for notification\r\n const currentContext = this.contextManager.getCurrentContext();\r\n const effectiveCwd = currentContext.type !== 'local'\r\n ? currentContext.metadata?.workingDirectory || '~'\r\n : this.cwd;\r\n\r\n // Build remote context prefix for SSH/Docker/WSL (for path display in UI)\r\n let remoteContext: string | undefined;\r\n if (currentContext.type !== 'local') {\r\n const metadata = currentContext.metadata;\r\n if (currentContext.type === 'ssh' && metadata) {\r\n remoteContext = `${metadata.username || 'user'}@${metadata.hostname || 'remote'}`;\r\n } else if (currentContext.type === 'wsl' && metadata) {\r\n remoteContext = `wsl:${metadata.distroName || 'wsl'}`;\r\n } else if (currentContext.type === 'docker' && metadata) {\r\n remoteContext = `docker:${metadata.containerId?.substring(0, 12) || 'container'}`;\r\n }\r\n }\r\n\r\n // Notify UI that command is executing\r\n if (this.onToolExecutionUpdate) {\r\n this.onToolExecutionUpdate({\r\n toolName: 'execute_command',\r\n status: 'executing',\r\n arguments: { command, cwd: effectiveCwd, isPty: shellUtils.isPtyAvailable(), remoteContext, commandModeExecution: true }\r\n });\r\n }\r\n\r\n // Execute with streaming support for local commands\r\n if (currentContext.type === 'local') {\r\n // Use interactive execution to support stdin\r\n let output = '';\r\n\r\n await new Promise<void>((resolve) => {\r\n this.currentInteractiveProcess = shellUtils.executeCommandInteractive(\r\n command,\r\n this.cwd,\r\n (data: string) => {\r\n // PTY-style: single merged stream\r\n output += data;\r\n if (this.onToolStreamingOutput) {\r\n this.onToolStreamingOutput({ toolName: 'execute_command', chunk: data, type: 'stdout' });\r\n }\r\n },\r\n (exitCode: number) => {\r\n // Notify UI of completion\r\n if (this.onToolExecutionUpdate) {\r\n if (exitCode !== 0) {\r\n this.onToolExecutionUpdate({\r\n toolName: 'execute_command',\r\n status: 'error',\r\n result: output,\r\n error: `Exit Code: ${exitCode}`,\r\n arguments: { command, cwd: this.cwd, remoteContext, commandModeExecution: true }\r\n });\r\n } else {\r\n this.onToolExecutionUpdate({\r\n toolName: 'execute_command',\r\n status: 'completed',\r\n result: output || 'Command executed successfully',\r\n arguments: { command, cwd: this.cwd, remoteContext, commandModeExecution: true }\r\n });\r\n }\r\n }\r\n\r\n // Record shell command to AI conversation history\r\n this.recordShellCommandToHistory(command, output, this.cwd, exitCode);\r\n\r\n this.currentInteractiveProcess = undefined;\r\n resolve();\r\n }\r\n );\r\n });\r\n } else if (currentContext.type === 'wsl') {\r\n // WSL execution with PTY for proper TTY handling (sudo, etc.)\r\n const remoteCwd = currentContext.metadata?.workingDirectory || '~';\r\n const distribution = currentContext.metadata?.distroName || 'Ubuntu';\r\n\r\n let output = '';\r\n\r\n await new Promise<void>((resolve) => {\r\n const wslPty = runWSLCommand(\r\n distribution,\r\n command,\r\n remoteCwd,\r\n (data: string) => {\r\n // Stream output to UI\r\n output += data;\r\n if (this.onToolStreamingOutput) {\r\n this.onToolStreamingOutput({ toolName: 'execute_command', chunk: data, type: 'stdout' });\r\n }\r\n },\r\n (exitCode: number) => {\r\n // Notify UI of completion\r\n if (this.onToolExecutionUpdate) {\r\n if (exitCode !== 0) {\r\n this.onToolExecutionUpdate({\r\n toolName: 'execute_command',\r\n status: 'error',\r\n result: output,\r\n error: `Exit Code: ${exitCode}`,\r\n arguments: { command, cwd: remoteCwd, remoteContext, commandModeExecution: true }\r\n });\r\n } else {\r\n this.onToolExecutionUpdate({\r\n toolName: 'execute_command',\r\n status: 'completed',\r\n result: output || 'Command executed successfully',\r\n arguments: { command, cwd: remoteCwd, remoteContext, commandModeExecution: true }\r\n });\r\n }\r\n }\r\n\r\n // Record shell command to AI conversation history\r\n this.recordShellCommandToHistory(command, output, remoteCwd, exitCode);\r\n\r\n this.currentInteractiveProcess = undefined;\r\n resolve();\r\n }\r\n );\r\n\r\n // Set up interactive process for stdin\r\n this.currentInteractiveProcess = {\r\n process: null,\r\n write: (data: string) => wslPty.write(data),\r\n kill: () => wslPty.kill(),\r\n signal: (sig: NodeJS.Signals) => {\r\n if (sig === 'SIGINT') {\r\n wslPty.write('\\x03'); // Ctrl+C\r\n return;\r\n }\r\n // For termination signals (SIGTERM/SIGKILL), force-close the PTY so\r\n // command-mode queue processing is not left waiting on remote shells.\r\n if (sig === 'SIGTERM' || sig === 'SIGKILL') {\r\n wslPty.kill();\r\n }\r\n },\r\n resize: (cols: number, rows: number) => wslPty.resize(cols, rows),\r\n isPty: true\r\n };\r\n });\r\n } else if (currentContext.type === 'docker') {\r\n // Docker execution - check if nested inside SSH\r\n const parentContext = this.contextManager.getParentContext();\r\n const remoteCwd = currentContext.metadata?.workingDirectory || '~';\r\n const containerId = currentContext.metadata?.containerId || '';\r\n\r\n let output = '';\r\n\r\n if (parentContext && parentContext.type === 'ssh') {\r\n // Nested Docker inside SSH: route docker exec command through SSH\r\n const sshClient = parentContext.handler?.client;\r\n\r\n if (!sshClient) {\r\n throw new Error('SSH client not available for nested Docker session');\r\n }\r\n\r\n // Build docker exec command to run via SSH\r\n const escapedCommand = command.replace(/\"/g, '\\\\\"');\r\n const dockerCommand = `docker exec -w \"${remoteCwd}\" ${containerId} sh -c \"${escapedCommand}\"`;\r\n\r\n await new Promise<void>((resolve) => {\r\n const sshPty = runSSHCommand(\r\n sshClient,\r\n dockerCommand,\r\n parentContext.metadata.workingDirectory || '~',\r\n (data: string) => {\r\n // Stream output to UI\r\n output += data;\r\n if (this.onToolStreamingOutput) {\r\n this.onToolStreamingOutput({ toolName: 'execute_command', chunk: data, type: 'stdout' });\r\n }\r\n },\r\n (exitCode: number) => {\r\n // Notify UI of completion\r\n if (this.onToolExecutionUpdate) {\r\n if (exitCode !== 0) {\r\n this.onToolExecutionUpdate({\r\n toolName: 'execute_command',\r\n status: 'error',\r\n result: output,\r\n error: `Exit Code: ${exitCode}`,\r\n arguments: { command, cwd: remoteCwd, remoteContext, commandModeExecution: true }\r\n });\r\n } else {\r\n this.onToolExecutionUpdate({\r\n toolName: 'execute_command',\r\n status: 'completed',\r\n result: output || 'Command executed successfully',\r\n arguments: { command, cwd: remoteCwd, remoteContext, commandModeExecution: true }\r\n });\r\n }\r\n }\r\n\r\n // Record shell command to AI conversation history\r\n this.recordShellCommandToHistory(command, output, remoteCwd, exitCode);\r\n\r\n this.currentInteractiveProcess = undefined;\r\n resolve();\r\n }\r\n );\r\n\r\n // Set up interactive process for stdin\r\n this.currentInteractiveProcess = {\r\n process: null,\r\n write: (data: string) => sshPty.write(data),\r\n kill: () => sshPty.kill(),\r\n signal: (sig: NodeJS.Signals) => {\r\n if (sig === 'SIGINT') {\r\n sshPty.write('\\x03'); // Ctrl+C\r\n return;\r\n }\r\n if (sig === 'SIGTERM' || sig === 'SIGKILL') {\r\n sshPty.kill();\r\n }\r\n },\r\n resize: (cols: number, rows: number) => sshPty.resize(cols, rows),\r\n isPty: true\r\n };\r\n });\r\n } else {\r\n // Local Docker: use standard runDockerCommand\r\n await new Promise<void>((resolve) => {\r\n const dockerPty = runDockerCommand(\r\n containerId,\r\n command,\r\n remoteCwd,\r\n (data: string) => {\r\n // Stream output to UI\r\n output += data;\r\n if (this.onToolStreamingOutput) {\r\n this.onToolStreamingOutput({ toolName: 'execute_command', chunk: data, type: 'stdout' });\r\n }\r\n },\r\n (exitCode: number) => {\r\n // Notify UI of completion\r\n if (this.onToolExecutionUpdate) {\r\n if (exitCode !== 0) {\r\n this.onToolExecutionUpdate({\r\n toolName: 'execute_command',\r\n status: 'error',\r\n result: output,\r\n error: `Exit Code: ${exitCode}`,\r\n arguments: { command, cwd: remoteCwd, remoteContext, commandModeExecution: true }\r\n });\r\n } else {\r\n this.onToolExecutionUpdate({\r\n toolName: 'execute_command',\r\n status: 'completed',\r\n result: output || 'Command executed successfully',\r\n arguments: { command, cwd: remoteCwd, remoteContext, commandModeExecution: true }\r\n });\r\n }\r\n }\r\n\r\n // Record shell command to AI conversation history\r\n this.recordShellCommandToHistory(command, output, remoteCwd, exitCode);\r\n\r\n this.currentInteractiveProcess = undefined;\r\n resolve();\r\n }\r\n );\r\n\r\n // Set up interactive process for stdin\r\n this.currentInteractiveProcess = {\r\n process: null,\r\n write: (data: string) => dockerPty.write(data),\r\n kill: () => dockerPty.kill(),\r\n signal: (sig: NodeJS.Signals) => {\r\n if (sig === 'SIGINT') {\r\n dockerPty.write('\\x03'); // Ctrl+C\r\n return;\r\n }\r\n if (sig === 'SIGTERM' || sig === 'SIGKILL') {\r\n dockerPty.kill();\r\n }\r\n },\r\n resize: (cols: number, rows: number) => dockerPty.resize(cols, rows),\r\n isPty: true\r\n };\r\n });\r\n }\r\n } else if (currentContext.type === 'ssh') {\r\n // SSH execution with PTY for proper TTY handling\r\n const remoteCwd = currentContext.metadata?.workingDirectory || '~';\r\n const sshClient = currentContext.handler?.client;\r\n\r\n if (!sshClient) {\r\n throw new Error('SSH client not available');\r\n }\r\n\r\n let output = '';\r\n\r\n await new Promise<void>((resolve) => {\r\n const sshPty = runSSHCommand(\r\n sshClient,\r\n command,\r\n remoteCwd,\r\n (data: string) => {\r\n // Stream output to UI\r\n output += data;\r\n if (this.onToolStreamingOutput) {\r\n this.onToolStreamingOutput({ toolName: 'execute_command', chunk: data, type: 'stdout' });\r\n }\r\n },\r\n (exitCode: number) => {\r\n // Notify UI of completion\r\n if (this.onToolExecutionUpdate) {\r\n if (exitCode !== 0) {\r\n this.onToolExecutionUpdate({\r\n toolName: 'execute_command',\r\n status: 'error',\r\n result: output,\r\n error: `Exit Code: ${exitCode}`,\r\n arguments: { command, cwd: remoteCwd, remoteContext, commandModeExecution: true }\r\n });\r\n } else {\r\n this.onToolExecutionUpdate({\r\n toolName: 'execute_command',\r\n status: 'completed',\r\n result: output || 'Command executed successfully',\r\n arguments: { command, cwd: remoteCwd, remoteContext, commandModeExecution: true }\r\n });\r\n }\r\n }\r\n\r\n // Record shell command to AI conversation history\r\n this.recordShellCommandToHistory(command, output, remoteCwd, exitCode);\r\n\r\n this.currentInteractiveProcess = undefined;\r\n resolve();\r\n }\r\n );\r\n\r\n // Set up interactive process for stdin\r\n this.currentInteractiveProcess = {\r\n process: null,\r\n write: (data: string) => sshPty.write(data),\r\n kill: () => sshPty.kill(),\r\n signal: (sig: NodeJS.Signals) => {\r\n if (sig === 'SIGINT') {\r\n sshPty.write('\\x03'); // Ctrl+C\r\n return;\r\n }\r\n if (sig === 'SIGTERM' || sig === 'SIGKILL') {\r\n sshPty.kill();\r\n }\r\n },\r\n resize: (cols: number, rows: number) => sshPty.resize(cols, rows),\r\n isPty: true\r\n };\r\n });\r\n }\r\n\r\n // Save conversation after user shell command execution\r\n // This ensures shell commands are saved to chat history like AI messages\r\n this.saveCurrentChat();\r\n\r\n } catch (error: any) {\r\n if (this.onResponseCallback) {\r\n this.onResponseCallback(`❌ Error: ${error.message}`);\r\n }\r\n }\r\n }\r\n}\r\n"],"mappings":"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","names":["message","result","compactionOutcome","silentStopPrompt","currentContext","cmd","process"]}
|