edge-pi-cli 0.1.1 → 0.1.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"interactive-mode.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/interactive-mode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAqBH,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAgB,cAAc,EAAE,MAAM,SAAS,CAAC;AAW5F,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AA8B7C,MAAM,WAAW,sBAAsB;IACtC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;IACjB,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC;IAC7B,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,sFAAsF;IACtF,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6FAA6F;IAC7F,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IAC5E,2DAA2D;IAC3D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,iEAAiE;IACjE,aAAa,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAG3G","sourcesContent":["/**\n * Interactive mode using @mariozechner/pi-tui.\n *\n * Replaces the old readline-based REPL with a proper TUI that matches\n * the UX patterns from @mariozechner/pi-coding-agent:\n * - Editor component for input with submit/escape handling\n * - Markdown rendering for assistant responses\n * - Tool execution components with collapsible output\n * - Footer with model/provider info and token stats\n * - Container-based layout (header → chat → pending → editor → footer)\n * - Context compaction (manual /compact + auto mode)\n */\n\nimport { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n\tCombinedAutocompleteProvider,\n\tContainer,\n\tEditor,\n\tKey,\n\tLoader,\n\tmatchesKey,\n\tProcessTerminal,\n\ttype SelectItem,\n\tSelectList,\n\ttype SlashCommand,\n\tSpacer,\n\tText,\n\tTUI,\n} from \"@mariozechner/pi-tui\";\nimport type { ImagePart } from \"ai\";\nimport chalk from \"chalk\";\nimport type { CodingAgent, CodingAgentConfig, ModelMessage, SessionManager } from \"edge-pi\";\nimport {\n\ttype CompactionResult,\n\ttype CompactionSettings,\n\tcompact,\n\tDEFAULT_COMPACTION_SETTINGS,\n\testimateContextTokens,\n\tprepareCompaction,\n\tSessionManager as SessionManagerClass,\n\tshouldCompact,\n} from \"edge-pi\";\nimport type { AuthStorage } from \"../../auth/auth-storage.js\";\nimport type { ContextFile } from \"../../context.js\";\nimport { getLatestModels } from \"../../model-factory.js\";\nimport type { PromptTemplate } from \"../../prompts.js\";\nimport { expandPromptTemplate } from \"../../prompts.js\";\nimport type { SettingsManager } from \"../../settings.js\";\nimport type { Skill } from \"../../skills.js\";\nimport { executeBashCommand } from \"../../utils/bash-executor.js\";\nimport { type ClipboardImage, extensionForImageMimeType, readClipboardImage } from \"../../utils/clipboard-image.js\";\nimport { formatPendingMessages, parseBashInput } from \"./bash-helpers.js\";\nimport { AssistantMessageComponent } from \"./components/assistant-message.js\";\nimport { BashExecutionComponent } from \"./components/bash-execution.js\";\nimport { CompactionSummaryComponent } from \"./components/compaction-summary.js\";\nimport { FooterComponent } from \"./components/footer.js\";\nimport { ToolExecutionComponent, type ToolOutput } from \"./components/tool-execution.js\";\nimport { UserMessageComponent } from \"./components/user-message.js\";\nimport { getEditorTheme, getMarkdownTheme, getSelectListTheme } from \"./theme.js\";\n\n/** Default context window size (used when model doesn't report one). */\nconst DEFAULT_CONTEXT_WINDOW = 200_000;\n\n/** Extract display-friendly output from a tool result. Handles both plain strings and structured objects with text/image fields. */\nfunction extractToolOutput(output: unknown): ToolOutput {\n\tif (typeof output === \"string\") {\n\t\treturn { text: output };\n\t}\n\tif (typeof output === \"object\" && output !== null && \"text\" in output && typeof (output as any).text === \"string\") {\n\t\tconst obj = output as any;\n\t\treturn {\n\t\t\ttext: obj.text,\n\t\t\t...(obj.image && { image: obj.image }),\n\t\t};\n\t}\n\treturn { text: JSON.stringify(output) };\n}\n\nexport interface InteractiveModeOptions {\n\tinitialMessage?: string;\n\tinitialMessages?: string[];\n\tsessionManager?: SessionManager;\n\tskills?: Skill[];\n\tcontextFiles?: ContextFile[];\n\tprompts?: PromptTemplate[];\n\tverbose?: boolean;\n\tprovider: string;\n\tmodelId: string;\n\tauthStorage?: AuthStorage;\n\t/** Settings manager for persisting user preferences (provider, model, compaction). */\n\tsettingsManager?: SettingsManager;\n\t/** Path to the `fd` binary for @ file autocomplete, or undefined if unavailable. */\n\tfdPath?: string;\n\t/** Called when the user switches model via Ctrl+L. Returns a new agent for the new model. */\n\tonModelChange?: (provider: string, modelId: string) => Promise<CodingAgent>;\n\t/** Context window size for the model. Defaults to 200k. */\n\tcontextWindow?: number;\n\t/** Directory where session files are stored. Required for /resume. */\n\tsessionDir?: string;\n\t/** Agent config used to recreate agents when resuming sessions. */\n\tagentConfig?: CodingAgentConfig;\n\t/** When true, show the session picker immediately on startup. */\n\tresumeOnStart?: boolean;\n}\n\n/**\n * Run the interactive TUI mode with streaming output.\n */\nexport async function runInteractiveMode(agent: CodingAgent, options: InteractiveModeOptions): Promise<void> {\n\tconst mode = new InteractiveMode(agent, options);\n\tawait mode.run();\n}\n\n// ============================================================================\n// InteractiveMode class\n// ============================================================================\n\nclass InteractiveMode {\n\tprivate agent: CodingAgent;\n\tprivate options: InteractiveModeOptions;\n\tprivate currentProvider: string;\n\tprivate currentModelId: string;\n\n\tprivate ui!: TUI;\n\tprivate headerContainer!: Container;\n\tprivate chatContainer!: Container;\n\tprivate pendingContainer!: Container;\n\tprivate pendingMessagesContainer!: Container;\n\tprivate editorContainer!: Container;\n\tprivate editor!: Editor;\n\tprivate editorTheme!: import(\"@mariozechner/pi-tui\").EditorTheme;\n\n\t// Message queues\n\tprivate steeringMessages: string[] = [];\n\tprivate followUpMessages: string[] = [];\n\tprivate isStreaming = false;\n\n\t// Inline bash state\n\tprivate isBashMode = false;\n\tprivate isBashRunning = false;\n\tprivate bashAbortController: AbortController | null = null;\n\tprivate bashComponent: import(\"./components/bash-execution.js\").BashExecutionComponent | undefined = undefined;\n\tprivate footer!: FooterComponent;\n\n\t// Loading animation during agent processing\n\tprivate loadingAnimation: Loader | undefined = undefined;\n\n\t// Streaming state\n\tprivate streamingComponent: AssistantMessageComponent | undefined = undefined;\n\tprivate streamingText = \"\";\n\tprivate hadToolResults = false;\n\n\t// Tool execution tracking: toolCallId → component\n\tprivate pendingTools = new Map<string, ToolExecutionComponent>();\n\n\t// Tool output expansion state\n\tprivate toolOutputExpanded = false;\n\n\t// Callback for resolving user input promise\n\tprivate onInputCallback?: (text: string) => void;\n\n\t// Pending clipboard images to attach to the next message\n\tprivate pendingImages: ClipboardImage[] = [];\n\n\t// Compaction state\n\tprivate contextWindow: number;\n\tprivate compactionSettings: CompactionSettings;\n\tprivate autoCompaction = true;\n\tprivate isCompacting = false;\n\tprivate compactionAbortController: AbortController | null = null;\n\n\tconstructor(agent: CodingAgent, options: InteractiveModeOptions) {\n\t\tthis.agent = agent;\n\t\tthis.options = options;\n\t\tthis.currentProvider = options.provider;\n\t\tthis.currentModelId = options.modelId;\n\t\tthis.contextWindow = options.contextWindow ?? DEFAULT_CONTEXT_WINDOW;\n\n\t\t// Initialize compaction settings from persisted settings if available\n\t\tconst savedCompaction = options.settingsManager?.getCompaction();\n\t\tthis.compactionSettings = {\n\t\t\t...DEFAULT_COMPACTION_SETTINGS,\n\t\t\t...(savedCompaction?.reserveTokens !== undefined && { reserveTokens: savedCompaction.reserveTokens }),\n\t\t\t...(savedCompaction?.keepRecentTokens !== undefined && { keepRecentTokens: savedCompaction.keepRecentTokens }),\n\t\t};\n\t\tthis.autoCompaction = options.settingsManager?.getCompactionEnabled() ?? true;\n\t}\n\n\tasync run(): Promise<void> {\n\t\tthis.initUI();\n\t\tthis.updateFooterTokens();\n\n\t\t// Show session picker immediately if --resume was passed\n\t\tif (this.options.resumeOnStart) {\n\t\t\tawait this.handleResume();\n\t\t}\n\n\t\t// Process initial messages\n\t\tconst { initialMessage, initialMessages = [] } = this.options;\n\n\t\tconst allInitial: string[] = [];\n\t\tif (initialMessage) allInitial.push(initialMessage);\n\t\tallInitial.push(...initialMessages);\n\n\t\tfor (const msg of allInitial) {\n\t\t\tthis.chatContainer.addChild(new UserMessageComponent(msg, getMarkdownTheme()));\n\t\t\tthis.ui.requestRender();\n\t\t\tawait this.streamPrompt(msg);\n\t\t}\n\n\t\t// Main interactive loop\n\t\twhile (true) {\n\t\t\tconst userInput = await this.getUserInput();\n\t\t\tawait this.handleUserInput(userInput);\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// UI Setup\n\t// ========================================================================\n\n\tprivate initUI(): void {\n\t\tconst { provider, modelId, skills = [], contextFiles = [], prompts = [], verbose, sessionManager } = this.options;\n\n\t\tthis.ui = new TUI(new ProcessTerminal());\n\n\t\t// Header\n\t\tthis.headerContainer = new Container();\n\t\tconst logo = chalk.bold(\"epi\") + chalk.dim(` - ${provider}/${modelId}`);\n\n\t\tconst hints = [\n\t\t\t`${chalk.dim(\"Escape\")} to abort`,\n\t\t\t`${chalk.dim(\"!\")} inline bash`,\n\t\t\t`${chalk.dim(\"Alt+Enter\")} follow-up while streaming`,\n\t\t\t`${chalk.dim(\"Ctrl+C\")} to exit`,\n\t\t\t`${chalk.dim(\"Ctrl+E\")} to expand tools`,\n\t\t\t`${chalk.dim(\"Ctrl+L\")} to switch model`,\n\t\t\t`${chalk.dim(\"Ctrl+V\")} to paste image`,\n\t\t\t`${chalk.dim(\"↑/↓\")} to browse history`,\n\t\t\t`${chalk.dim(\"@\")} for file references`,\n\t\t\t`${chalk.dim(\"/\")} for commands`,\n\t\t].join(\"\\n\");\n\n\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\tthis.headerContainer.addChild(new Text(`${logo}\\n${hints}`, 1, 0));\n\t\tthis.headerContainer.addChild(new Spacer(1));\n\n\t\tif (verbose && sessionManager?.getSessionFile()) {\n\t\t\tthis.headerContainer.addChild(new Text(chalk.dim(`Session: ${sessionManager.getSessionFile()}`), 1, 0));\n\t\t}\n\n\t\t// Show loaded context, skills, and prompts at startup\n\t\tthis.showLoadedResources(contextFiles, skills, prompts);\n\n\t\t// Chat area\n\t\tthis.chatContainer = new Container();\n\n\t\t// Pending messages (loading animations, status)\n\t\tthis.pendingContainer = new Container();\n\n\t\t// Pending steering/follow-up messages\n\t\tthis.pendingMessagesContainer = new Container();\n\n\t\t// Editor with slash command autocomplete\n\t\tthis.editorTheme = getEditorTheme();\n\t\tthis.editor = new Editor(this.ui, this.editorTheme);\n\t\tthis.editor.setAutocompleteProvider(this.buildAutocompleteProvider());\n\t\tthis.editorContainer = new Container();\n\t\tthis.editorContainer.addChild(this.editor);\n\n\t\t// Footer\n\t\tthis.footer = new FooterComponent(this.currentProvider, this.currentModelId);\n\t\tthis.footer.setAutoCompaction(this.autoCompaction);\n\t\tthis.footer.setSubscription(this.isSubscriptionProvider());\n\n\t\t// Assemble layout\n\t\tthis.ui.addChild(this.headerContainer);\n\t\tthis.ui.addChild(this.chatContainer);\n\t\tthis.ui.addChild(this.pendingMessagesContainer);\n\t\tthis.ui.addChild(this.pendingContainer);\n\t\tthis.ui.addChild(this.editorContainer);\n\t\tthis.ui.addChild(this.footer);\n\n\t\tthis.ui.setFocus(this.editor);\n\t\tthis.setupKeyHandlers();\n\n\t\tthis.ui.start();\n\t}\n\n\t// ========================================================================\n\t// Key Handlers\n\t// ========================================================================\n\n\tprivate setupKeyHandlers(): void {\n\t\tthis.editor.onChange = (text: string) => {\n\t\t\tconst wasBashMode = this.isBashMode;\n\t\t\tthis.isBashMode = text.trimStart().startsWith(\"!\");\n\t\t\tif (wasBashMode !== this.isBashMode) {\n\t\t\t\tthis.updateEditorBorderColor();\n\t\t\t}\n\t\t};\n\t\tthis.editor.onSubmit = (text: string) => {\n\t\t\ttext = text.trim();\n\t\t\tif (!text) return;\n\n\t\t\t// If agent is streaming, Enter becomes a steering message\n\t\t\tif (this.isStreaming) {\n\t\t\t\tthis.agent.steer({ role: \"user\", content: [{ type: \"text\", text }] });\n\t\t\t\tthis.steeringMessages.push(text);\n\t\t\t\tthis.editor.setText(\"\");\n\t\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.editor.addToHistory(text);\n\t\t\tthis.editor.setText(\"\");\n\n\t\t\tif (this.onInputCallback) {\n\t\t\t\tthis.onInputCallback(text);\n\t\t\t}\n\t\t};\n\n\t\tconst origHandleInput = this.editor.handleInput.bind(this.editor);\n\t\tthis.editor.handleInput = (data: string) => {\n\t\t\t// Escape: abort if agent is running or compacting\n\t\t\tif (matchesKey(data, Key.escape)) {\n\t\t\t\tif (this.isBashRunning && this.bashAbortController) {\n\t\t\t\t\tthis.bashAbortController.abort();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (this.isCompacting && this.compactionAbortController) {\n\t\t\t\t\tthis.compactionAbortController.abort();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (this.loadingAnimation) {\n\t\t\t\t\tthis.agent.abort();\n\t\t\t\t\tthis.stopLoading();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Ctrl+C: exit\n\t\t\tif (matchesKey(data, Key.ctrl(\"c\"))) {\n\t\t\t\tthis.shutdown();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Ctrl+D: exit if editor is empty\n\t\t\tif (matchesKey(data, Key.ctrl(\"d\"))) {\n\t\t\t\tif (this.editor.getText().length === 0) {\n\t\t\t\t\tthis.shutdown();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Ctrl+E: toggle tool output expansion\n\t\t\tif (matchesKey(data, Key.ctrl(\"e\"))) {\n\t\t\t\tthis.toggleToolExpansion();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Ctrl+L: select model\n\t\t\tif (matchesKey(data, Key.ctrl(\"l\"))) {\n\t\t\t\tthis.handleModelSelect();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Ctrl+V: paste image from clipboard\n\t\t\tif (matchesKey(data, Key.ctrl(\"v\"))) {\n\t\t\t\tthis.handleClipboardImagePaste();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Alt+Enter (Option+Enter on Mac): follow-up while streaming (or submit normally when idle)\n\t\t\tif (matchesKey(data, Key.alt(\"enter\"))) {\n\t\t\t\tconst text = this.editor.getText().trim();\n\t\t\t\tif (!text) return;\n\n\t\t\t\tif (this.isStreaming) {\n\t\t\t\t\tthis.agent.followUp({ role: \"user\", content: [{ type: \"text\", text }] });\n\t\t\t\t\tthis.followUpMessages.push(text);\n\t\t\t\t\tthis.editor.setText(\"\");\n\t\t\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Not streaming: treat like regular submit\n\t\t\t\tthis.editor.onSubmit?.(text);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Alt+Up (Option+Up on Mac): dequeue all queued messages back into the editor\n\t\t\tif (matchesKey(data, Key.alt(\"up\"))) {\n\t\t\t\tconst restored = this.clearAllQueues();\n\t\t\t\tif (restored.length > 0) {\n\t\t\t\t\tthis.editor.setText(restored.join(\"\\n\\n\"));\n\t\t\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\torigHandleInput(data);\n\t\t};\n\t}\n\n\t// ========================================================================\n\t// User Input\n\t// ========================================================================\n\n\tprivate getUserInput(): Promise<string> {\n\t\treturn new Promise((resolve) => {\n\t\t\tthis.onInputCallback = (text: string) => {\n\t\t\t\tthis.onInputCallback = undefined;\n\t\t\t\tresolve(text);\n\t\t\t};\n\t\t});\n\t}\n\n\tprivate async handleUserInput(input: string): Promise<void> {\n\t\t// Handle commands\n\t\tif (input === \"/help\") {\n\t\t\tthis.showHelp();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/skills\") {\n\t\t\tthis.showSkills();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/quit\" || input === \"/exit\") {\n\t\t\tthis.shutdown();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/model\") {\n\t\t\tawait this.handleModelSelect();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/login\") {\n\t\t\tawait this.handleLogin();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/logout\") {\n\t\t\tawait this.handleLogout();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/compact\" || input.startsWith(\"/compact \")) {\n\t\t\tconst customInstructions = input.startsWith(\"/compact \") ? input.slice(9).trim() : undefined;\n\t\t\tawait this.handleCompactCommand(customInstructions);\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/auto-compact\") {\n\t\t\tthis.toggleAutoCompaction();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/resume\") {\n\t\t\tawait this.handleResume();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input.startsWith(\"/skill:\")) {\n\t\t\tconst skillName = input.slice(\"/skill:\".length).trim();\n\t\t\tawait this.handleSkillInvocation(skillName);\n\t\t\treturn;\n\t\t}\n\n\t\t// Handle bash commands (! for normal, !! for excluded from context)\n\t\tconst bashParsed = parseBashInput(input);\n\t\tif (bashParsed) {\n\t\t\tif (this.isBashRunning) {\n\t\t\t\tthis.showStatus(chalk.yellow(\"A bash command is already running. Press Escape to cancel it first.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait this.handleBashCommand(bashParsed.command, bashParsed.excludeFromContext);\n\t\t\tthis.isBashMode = false;\n\t\t\tthis.updateEditorBorderColor();\n\t\t\treturn;\n\t\t}\n\n\t\t// Try expanding prompt templates\n\t\tconst { prompts = [] } = this.options;\n\t\tconst expanded = expandPromptTemplate(input, prompts);\n\n\t\t// Capture and clear pending images\n\t\tconst images = this.pendingImages.length > 0 ? [...this.pendingImages] : undefined;\n\t\tthis.pendingImages = [];\n\n\t\t// Regular message (use expanded text if a prompt template was matched)\n\t\tconst imageLabel = images ? chalk.dim(` (${images.length} image${images.length > 1 ? \"s\" : \"\"})`) : \"\";\n\t\tthis.chatContainer.addChild(new UserMessageComponent(`${expanded}${imageLabel}`, getMarkdownTheme()));\n\t\tthis.ui.requestRender();\n\t\tawait this.streamPrompt(expanded, images);\n\t}\n\n\tprivate async handleBashCommand(command: string, excludeFromContext: boolean): Promise<void> {\n\t\tconst { sessionManager } = this.options;\n\n\t\tthis.bashAbortController = new AbortController();\n\t\tthis.isBashRunning = true;\n\n\t\tthis.bashComponent = new BashExecutionComponent(command, this.ui, excludeFromContext);\n\t\tif (this.toolOutputExpanded) {\n\t\t\tthis.bashComponent.setExpanded(true);\n\t\t}\n\t\tthis.chatContainer.addChild(this.bashComponent);\n\t\tthis.ui.requestRender();\n\n\t\ttry {\n\t\t\tconst result = await executeBashCommand(command, {\n\t\t\t\tsignal: this.bashAbortController.signal,\n\t\t\t\tonChunk: (chunk) => {\n\t\t\t\t\tthis.bashComponent?.appendOutput(chunk);\n\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tthis.bashComponent.setComplete(result.exitCode, result.cancelled, result.truncated, result.fullOutputPath);\n\t\t\tthis.ui.requestRender();\n\n\t\t\tif (!excludeFromContext) {\n\t\t\t\tconst msgText = `Ran \\`${command}\\`\\n\\n\\`\\`\\`\\n${result.output.trimEnd()}\\n\\`\\`\\``;\n\t\t\t\tconst userMsg: ModelMessage = { role: \"user\", content: [{ type: \"text\", text: msgText }] };\n\t\t\t\tthis.agent.setMessages([...this.agent.messages, userMsg]);\n\t\t\t\tsessionManager?.appendMessage(userMsg);\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.isBashRunning = false;\n\t\t\tthis.bashAbortController = null;\n\t\t\tthis.bashComponent = undefined;\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Autocomplete\n\t// ========================================================================\n\n\tprivate buildAutocompleteProvider(): CombinedAutocompleteProvider {\n\t\tconst { skills = [], prompts = [], fdPath } = this.options;\n\n\t\tconst commands: SlashCommand[] = [\n\t\t\t{ name: \"help\", description: \"Show available commands\" },\n\t\t\t{ name: \"resume\", description: \"Resume a previous session\" },\n\t\t\t{ name: \"compact\", description: \"Manually compact the session context\" },\n\t\t\t{ name: \"auto-compact\", description: \"Toggle automatic context compaction\" },\n\t\t\t{ name: \"login\", description: \"Login to an OAuth provider\" },\n\t\t\t{ name: \"logout\", description: \"Logout from an OAuth provider\" },\n\t\t\t{ name: \"skills\", description: \"List loaded skills\" },\n\t\t\t{ name: \"model\", description: \"Switch model (Ctrl+L)\" },\n\t\t\t{ name: \"quit\", description: \"Exit the CLI\" },\n\t\t\t{ name: \"exit\", description: \"Exit the CLI\" },\n\t\t];\n\n\t\tfor (const skill of skills) {\n\t\t\tcommands.push({\n\t\t\t\tname: `skill:${skill.name}`,\n\t\t\t\tdescription: skill.description,\n\t\t\t});\n\t\t}\n\n\t\t// Add prompt templates as slash commands\n\t\tfor (const prompt of prompts) {\n\t\t\tcommands.push({\n\t\t\t\tname: prompt.name,\n\t\t\t\tdescription: prompt.description,\n\t\t\t});\n\t\t}\n\n\t\treturn new CombinedAutocompleteProvider(commands, process.cwd(), fdPath ?? null);\n\t}\n\n\t// ========================================================================\n\t// Model Selection\n\t// ========================================================================\n\n\tprivate async handleModelSelect(): Promise<void> {\n\t\tconst latestModels = getLatestModels();\n\t\tconst modelOptions: { provider: string; modelId: string; label: string }[] = [];\n\t\tfor (const [provider, models] of Object.entries(latestModels)) {\n\t\t\tfor (const modelId of models) {\n\t\t\t\tmodelOptions.push({ provider, modelId, label: `${provider}/${modelId}` });\n\t\t\t}\n\t\t}\n\n\t\tconst items: SelectItem[] = modelOptions.map((m) => {\n\t\t\tconst current = m.provider === this.currentProvider && m.modelId === this.currentModelId;\n\t\t\treturn {\n\t\t\t\tvalue: `${m.provider}/${m.modelId}`,\n\t\t\t\tlabel: current ? `${m.label} (current)` : m.label,\n\t\t\t};\n\t\t});\n\n\t\tconst selected = await this.showSelectList(\"Switch model\", items);\n\t\tif (!selected) return;\n\n\t\tconst [newProvider, ...modelParts] = selected.split(\"/\");\n\t\tconst newModelId = modelParts.join(\"/\");\n\n\t\tif (newProvider === this.currentProvider && newModelId === this.currentModelId) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.showStatus(chalk.dim(`Switching to ${newProvider}/${newModelId}...`));\n\n\t\tif (!this.options.onModelChange) {\n\t\t\tthis.showStatus(chalk.yellow(\"Model switching is not available.\"));\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconst newAgent = await this.options.onModelChange(newProvider, newModelId);\n\t\t\t// Preserve conversation history\n\t\t\tnewAgent.setMessages([...this.agent.messages]);\n\t\t\tthis.agent = newAgent;\n\n\t\t\tthis.currentProvider = newProvider;\n\t\t\tthis.currentModelId = newModelId;\n\t\t\tthis.updateFooter();\n\n\t\t\t// Persist the choice for next startup\n\t\t\tthis.options.settingsManager?.setDefaults(newProvider, newModelId);\n\n\t\t\tthis.showStatus(chalk.green(`Switched to ${newProvider}/${newModelId}`));\n\t\t} catch (error) {\n\t\t\tconst msg = error instanceof Error ? error.message : String(error);\n\t\t\tthis.showStatus(chalk.red(`Failed to switch model: ${msg}`));\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Streaming\n\t// ========================================================================\n\n\tprivate async streamPrompt(prompt: string, images?: ClipboardImage[]): Promise<void> {\n\t\tthis.isStreaming = true;\n\t\tthis.updatePendingMessagesDisplay();\n\t\tconst { sessionManager } = this.options;\n\t\tconst messagesBefore = this.agent.messages.length;\n\n\t\t// Build image parts from clipboard images\n\t\tconst imageParts: ImagePart[] = (images ?? []).map((img) => ({\n\t\t\ttype: \"image\" as const,\n\t\t\timage: Buffer.from(img.bytes).toString(\"base64\"),\n\t\t\tmediaType: img.mimeType,\n\t\t}));\n\n\t\t// Start loading animation\n\t\tthis.startLoading();\n\n\t\tthis.streamingComponent = undefined;\n\t\tthis.streamingText = \"\";\n\t\tthis.hadToolResults = false;\n\n\t\tlet errorDisplayed = false;\n\t\tlet streamFailed = false;\n\t\ttry {\n\t\t\tconst result =\n\t\t\t\timageParts.length > 0\n\t\t\t\t\t? await this.agent.stream({\n\t\t\t\t\t\t\tmessages: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\trole: \"user\" as const,\n\t\t\t\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: prompt }, ...imageParts],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t})\n\t\t\t\t\t: await this.agent.stream({ prompt });\n\n\t\t\tfor await (const part of result.fullStream) {\n\t\t\t\tswitch (part.type) {\n\t\t\t\t\tcase \"text-delta\":\n\t\t\t\t\t\t// After tool results, or for the very first text part, start a new assistant message component\n\t\t\t\t\t\t// so each agent step gets its own message bubble\n\t\t\t\t\t\tif (this.hadToolResults || !this.streamingComponent) {\n\t\t\t\t\t\t\tthis.streamingComponent = new AssistantMessageComponent(getMarkdownTheme());\n\t\t\t\t\t\t\tthis.streamingText = \"\";\n\t\t\t\t\t\t\tthis.hadToolResults = false;\n\t\t\t\t\t\t\tthis.chatContainer.addChild(this.streamingComponent);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis.streamingText += part.text;\n\t\t\t\t\t\tthis.streamingComponent!.updateText(this.streamingText);\n\t\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase \"tool-call\": {\n\t\t\t\t\t\tconst args =\n\t\t\t\t\t\t\ttypeof part.input === \"object\" && part.input !== null\n\t\t\t\t\t\t\t\t? (part.input as Record<string, unknown>)\n\t\t\t\t\t\t\t\t: {};\n\t\t\t\t\t\tconst toolComponent = new ToolExecutionComponent(part.toolName, args);\n\n\t\t\t\t\t\tif (this.toolOutputExpanded) {\n\t\t\t\t\t\t\ttoolComponent.setExpanded(true);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthis.pendingTools.set(part.toolCallId, toolComponent);\n\t\t\t\t\t\tthis.chatContainer.addChild(toolComponent);\n\t\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase \"tool-result\": {\n\t\t\t\t\t\tconst toolComponent = this.pendingTools.get(part.toolCallId);\n\t\t\t\t\t\tif (toolComponent) {\n\t\t\t\t\t\t\tconst toolOutput = extractToolOutput(part.output);\n\t\t\t\t\t\t\ttoolComponent.updateResult(toolOutput, /* isError */ false, /* isPartial */ false);\n\t\t\t\t\t\t\tthis.pendingTools.delete(part.toolCallId);\n\t\t\t\t\t\t\tthis.hadToolResults = true;\n\t\t\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase \"error\": {\n\t\t\t\t\t\tconst errorMessage =\n\t\t\t\t\t\t\t(part.error as any)?.message ??\n\t\t\t\t\t\t\t(typeof part.error === \"object\" && part.error !== null\n\t\t\t\t\t\t\t\t? JSON.stringify(part.error)\n\t\t\t\t\t\t\t\t: String(part.error));\n\t\t\t\t\t\tif (this.streamingComponent) {\n\t\t\t\t\t\t\tthis.streamingComponent.setError(errorMessage);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.showStatus(chalk.red(`Error: ${errorMessage}`));\n\t\t\t\t\t\t}\n\t\t\t\t\t\terrorDisplayed = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (errorDisplayed) return;\n\n\t\t\t// Get final response and update messages\n\t\t\tconst response = await result.response;\n\t\t\tconst responseMessages = response.messages as ModelMessage[];\n\t\t\tthis.agent.setMessages([\n\t\t\t\t...this.agent.messages.slice(0, messagesBefore),\n\t\t\t\t...buildUserMessage(prompt, imageParts),\n\t\t\t\t...responseMessages,\n\t\t\t]);\n\n\t\t\t// Save to session\n\t\t\tif (sessionManager) {\n\t\t\t\tconst userMsg: ModelMessage = {\n\t\t\t\t\trole: \"user\",\n\t\t\t\t\tcontent: [{ type: \"text\", text: prompt }, ...imageParts],\n\t\t\t\t};\n\t\t\t\tsessionManager.appendMessage(userMsg);\n\t\t\t\tfor (const msg of responseMessages) {\n\t\t\t\t\tsessionManager.appendMessage(msg);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Update footer token stats\n\t\t\tthis.updateFooterTokens();\n\n\t\t\t// Check for auto-compaction after successful response\n\t\t\tawait this.checkAutoCompaction();\n\t\t} catch (error) {\n\t\t\tif (errorDisplayed) {\n\t\t\t\tstreamFailed = true;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tstreamFailed = true;\n\t\t\tif ((error as Error).name === \"AbortError\") {\n\t\t\t\tif (this.streamingComponent) {\n\t\t\t\t\tthis.streamingComponent.setAborted();\n\t\t\t\t} else {\n\t\t\t\t\tthis.showStatus(chalk.dim(\"[aborted]\"));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst msg =\n\t\t\t\t\terror instanceof Error\n\t\t\t\t\t\t? error.message\n\t\t\t\t\t\t: typeof error === \"object\" && error !== null\n\t\t\t\t\t\t\t? JSON.stringify(error)\n\t\t\t\t\t\t\t: String(error);\n\t\t\t\tif (this.streamingComponent) {\n\t\t\t\t\tthis.streamingComponent.setError(msg);\n\t\t\t\t} else {\n\t\t\t\t\tthis.showStatus(chalk.red(`Error: ${msg}`));\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.stopLoading();\n\t\t\tthis.streamingComponent = undefined;\n\t\t\tthis.streamingText = \"\";\n\t\t\tthis.hadToolResults = false;\n\t\t\tthis.pendingTools.clear();\n\t\t\tthis.isStreaming = false;\n\t\t\tthis.steeringMessages = [];\n\t\t\tif (streamFailed) {\n\t\t\t\tthis.followUpMessages = [];\n\t\t\t}\n\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\tthis.ui.requestRender();\n\t\t}\n\n\t\t// Process queued follow-ups (skipped if stream failed/aborted)\n\t\twhile (this.followUpMessages.length > 0) {\n\t\t\tconst next = this.followUpMessages.shift();\n\t\t\tif (!next) break;\n\n\t\t\tthis.updatePendingMessagesDisplay();\n\n\t\t\tthis.chatContainer.addChild(new UserMessageComponent(next, getMarkdownTheme()));\n\t\t\tthis.ui.requestRender();\n\t\t\tawait this.streamPrompt(next);\n\t\t}\n\t}\n\n\tprivate updatePendingMessagesDisplay(): void {\n\t\tthis.pendingMessagesContainer.clear();\n\n\t\t// If no agent is running, clear pending messages (they've been consumed)\n\t\tif (!this.isStreaming && this.followUpMessages.length === 0 && this.steeringMessages.length === 0) {\n\t\t\tthis.ui.requestRender();\n\t\t\treturn;\n\t\t}\n\n\t\tconst lines = formatPendingMessages(this.steeringMessages, this.followUpMessages);\n\n\t\tif (lines.length === 0) {\n\t\t\tthis.ui.requestRender();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.pendingMessagesContainer.addChild(new Spacer(1));\n\t\tthis.pendingMessagesContainer.addChild(new Text(lines.map((l) => chalk.dim(l)).join(\"\\n\"), 1, 0));\n\t\tthis.pendingMessagesContainer.addChild(new Text(chalk.dim(\"↳ Alt+Up to edit queued messages\"), 1, 0));\n\t\tthis.pendingMessagesContainer.addChild(new Spacer(1));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate clearAllQueues(): string[] {\n\t\tconst restored = [...this.steeringMessages, ...this.followUpMessages];\n\t\tthis.steeringMessages = [];\n\t\tthis.followUpMessages = [];\n\t\t// Force agent to drop its internal queue too if possible, but edge-pi agent doesn't expose that yet.\n\t\t// However, steering messages are consumed immediately by the agent loop so they might already be gone.\n\t\t// Follow-ups are managed here in the loop, so clearing this array stops them.\n\t\treturn restored;\n\t}\n\n\t// ========================================================================\n\t// Loading Animation\n\t// ========================================================================\n\n\tprivate startLoading(): void {\n\t\tthis.stopLoading();\n\t\tthis.loadingAnimation = new Loader(\n\t\t\tthis.ui,\n\t\t\t(s: string) => chalk.cyan(s),\n\t\t\t(s: string) => chalk.dim(s),\n\t\t\t\"Working...\",\n\t\t);\n\t\tthis.loadingAnimation.start();\n\t\tthis.pendingContainer.addChild(new Spacer(1));\n\t\tthis.pendingContainer.addChild(this.loadingAnimation);\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate stopLoading(): void {\n\t\tif (this.loadingAnimation) {\n\t\t\tthis.loadingAnimation.stop();\n\t\t\tthis.pendingContainer.clear();\n\t\t\tthis.loadingAnimation = undefined;\n\t\t\tthis.ui.requestRender();\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Tool Expansion\n\t// ========================================================================\n\n\tprivate toggleToolExpansion(): void {\n\t\tthis.toolOutputExpanded = !this.toolOutputExpanded;\n\n\t\t// Update all tool components and compaction components in the chat\n\t\tfor (const child of this.chatContainer.children) {\n\t\t\tif (child instanceof ToolExecutionComponent) {\n\t\t\t\tchild.setExpanded(this.toolOutputExpanded);\n\t\t\t} else if (child instanceof CompactionSummaryComponent) {\n\t\t\t\tchild.setExpanded(this.toolOutputExpanded);\n\t\t\t} else if (child instanceof BashExecutionComponent) {\n\t\t\t\tchild.setExpanded(this.toolOutputExpanded);\n\t\t\t}\n\t\t}\n\n\t\tthis.ui.requestRender();\n\t}\n\n\t// ========================================================================\n\t// Clipboard Image Paste\n\t// ========================================================================\n\n\tprivate handleClipboardImagePaste(): void {\n\t\ttry {\n\t\t\tconst image = readClipboardImage();\n\t\t\tif (!image) return;\n\t\t\tthis.pendingImages.push(image);\n\t\t\tconst ext = extensionForImageMimeType(image.mimeType) ?? \"image\";\n\t\t\tconst label = `[image ${this.pendingImages.length}: ${ext}]`;\n\t\t\tthis.editor.insertTextAtCursor(label);\n\t\t\tthis.ui.requestRender();\n\t\t} catch {\n\t\t\t// Silently ignore clipboard errors (may not have permission, etc.)\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Compaction\n\t// ========================================================================\n\n\t/**\n\t * Handle the /compact command.\n\t */\n\tprivate async handleCompactCommand(_customInstructions?: string): Promise<void> {\n\t\tconst messages = this.agent.messages;\n\t\tif (messages.length < 2) {\n\t\t\tthis.showStatus(chalk.yellow(\"Nothing to compact (not enough messages).\"));\n\t\t\treturn;\n\t\t}\n\n\t\tawait this.executeCompaction(false);\n\t}\n\n\t/**\n\t * Toggle auto-compaction on/off.\n\t */\n\tprivate toggleAutoCompaction(): void {\n\t\tthis.autoCompaction = !this.autoCompaction;\n\t\tthis.footer.setAutoCompaction(this.autoCompaction);\n\t\tthis.options.settingsManager?.setCompactionEnabled(this.autoCompaction);\n\t\tthis.showStatus(\n\t\t\tthis.autoCompaction ? chalk.green(\"Auto-compaction enabled\") : chalk.dim(\"Auto-compaction disabled\"),\n\t\t);\n\t\tthis.ui.requestRender();\n\t}\n\n\t/**\n\t * Check if auto-compaction should trigger after an agent response.\n\t */\n\tprivate async checkAutoCompaction(): Promise<void> {\n\t\tif (!this.autoCompaction) return;\n\n\t\tconst contextTokens = estimateContextTokens([...this.agent.messages]);\n\t\tif (!shouldCompact(contextTokens, this.contextWindow, this.compactionSettings)) return;\n\n\t\tawait this.executeCompaction(true);\n\t}\n\n\t/**\n\t * Execute compaction (used by both manual /compact and auto mode).\n\t */\n\tprivate async executeCompaction(isAuto: boolean): Promise<CompactionResult | undefined> {\n\t\tif (this.isCompacting) return undefined;\n\n\t\tconst { sessionManager } = this.options;\n\n\t\t// Build path entries from session if available, otherwise from agent messages\n\t\tconst pathEntries = sessionManager ? sessionManager.getBranch() : this.buildSessionEntriesFromMessages();\n\n\t\tif (pathEntries.length < 2) {\n\t\t\tif (!isAuto) {\n\t\t\t\tthis.showStatus(chalk.yellow(\"Nothing to compact (not enough messages).\"));\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\n\t\t// Prepare compaction\n\t\tconst preparation = prepareCompaction(pathEntries, this.compactionSettings);\n\t\tif (!preparation) {\n\t\t\tif (!isAuto) {\n\t\t\t\tthis.showStatus(chalk.yellow(\"Nothing to compact (already compacted or insufficient history).\"));\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (preparation.messagesToSummarize.length === 0) {\n\t\t\tif (!isAuto) {\n\t\t\t\tthis.showStatus(chalk.yellow(\"Nothing to compact (no messages to summarize).\"));\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\n\t\tthis.isCompacting = true;\n\t\tthis.compactionAbortController = new AbortController();\n\n\t\t// Show compaction indicator\n\t\tconst label = isAuto\n\t\t\t? \"Auto-compacting context... (Escape to cancel)\"\n\t\t\t: \"Compacting context... (Escape to cancel)\";\n\t\tconst compactingLoader = new Loader(\n\t\t\tthis.ui,\n\t\t\t(s: string) => chalk.cyan(s),\n\t\t\t(s: string) => chalk.dim(s),\n\t\t\tlabel,\n\t\t);\n\t\tcompactingLoader.start();\n\t\tthis.pendingContainer.clear();\n\t\tthis.pendingContainer.addChild(new Spacer(1));\n\t\tthis.pendingContainer.addChild(compactingLoader);\n\t\tthis.ui.requestRender();\n\n\t\tlet result: CompactionResult | undefined;\n\n\t\ttry {\n\t\t\t// We need a LanguageModel for summarization. Use the agent's model\n\t\t\t// by extracting it from the config. The model is accessible through\n\t\t\t// the onModelChange callback pattern, but for simplicity we create\n\t\t\t// a model via the same factory used at startup.\n\t\t\tconst { model } = await this.getCompactionModel();\n\n\t\t\tresult = await compact(preparation, model, this.compactionAbortController.signal);\n\n\t\t\t// Record compaction in session\n\t\t\tif (sessionManager) {\n\t\t\t\tsessionManager.appendCompaction(\n\t\t\t\t\tresult.summary,\n\t\t\t\t\tresult.firstKeptEntryId,\n\t\t\t\t\tresult.tokensBefore,\n\t\t\t\t\tresult.details,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Rebuild agent messages from the session context\n\t\t\tif (sessionManager) {\n\t\t\t\tconst context = sessionManager.buildSessionContext();\n\t\t\t\tthis.agent.setMessages(context.messages);\n\t\t\t}\n\n\t\t\t// Rebuild the chat UI\n\t\t\tthis.rebuildChatFromSession();\n\n\t\t\t// Add compaction summary component so user sees it\n\t\t\tconst summaryComponent = new CompactionSummaryComponent(result.tokensBefore, result.summary);\n\t\t\tif (this.toolOutputExpanded) {\n\t\t\t\tsummaryComponent.setExpanded(true);\n\t\t\t}\n\t\t\tthis.chatContainer.addChild(summaryComponent);\n\n\t\t\t// Update footer tokens\n\t\t\tthis.updateFooterTokens();\n\n\t\t\tif (this.options.verbose) {\n\t\t\t\tconst tokensAfter = estimateContextTokens([...this.agent.messages]);\n\t\t\t\tthis.showStatus(\n\t\t\t\t\tchalk.dim(\n\t\t\t\t\t\t`Compacted: ${result.tokensBefore.toLocaleString()} -> ${tokensAfter.toLocaleString()} tokens`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tif (\n\t\t\t\tthis.compactionAbortController.signal.aborted ||\n\t\t\t\tmessage === \"Compaction cancelled\" ||\n\t\t\t\t(error instanceof Error && error.name === \"AbortError\")\n\t\t\t) {\n\t\t\t\tthis.showStatus(chalk.dim(\"Compaction cancelled.\"));\n\t\t\t} else {\n\t\t\t\tthis.showStatus(chalk.red(`Compaction failed: ${message}`));\n\t\t\t}\n\t\t} finally {\n\t\t\tcompactingLoader.stop();\n\t\t\tthis.pendingContainer.clear();\n\t\t\tthis.isCompacting = false;\n\t\t\tthis.compactionAbortController = null;\n\t\t\tthis.ui.requestRender();\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Get the language model for compaction summarization.\n\t * Uses the same model creation path as the main agent.\n\t */\n\tprivate async getCompactionModel(): Promise<{ model: import(\"ai\").LanguageModel }> {\n\t\tconst { createModel } = await import(\"../../model-factory.js\");\n\t\treturn createModel({\n\t\t\tprovider: this.currentProvider,\n\t\t\tmodel: this.currentModelId,\n\t\t\tauthStorage: this.options.authStorage,\n\t\t});\n\t}\n\n\t/**\n\t * Build session entries from agent messages (when no session manager).\n\t * Creates synthetic SessionEntry objects for the compaction algorithm.\n\t */\n\tprivate buildSessionEntriesFromMessages(): import(\"edge-pi\").SessionEntry[] {\n\t\tconst messages = this.agent.messages;\n\t\tconst entries: import(\"edge-pi\").SessionEntry[] = [];\n\t\tlet parentId: string | null = null;\n\n\t\tfor (let i = 0; i < messages.length; i++) {\n\t\t\tconst id = `msg-${i}`;\n\t\t\tentries.push({\n\t\t\t\ttype: \"message\",\n\t\t\t\tid,\n\t\t\t\tparentId,\n\t\t\t\ttimestamp: new Date().toISOString(),\n\t\t\t\tmessage: messages[i],\n\t\t\t});\n\t\t\tparentId = id;\n\t\t}\n\n\t\treturn entries;\n\t}\n\n\t/**\n\t * Rebuild the chat UI from session context after compaction.\n\t */\n\tprivate rebuildChatFromSession(): void {\n\t\tthis.chatContainer.clear();\n\n\t\tconst messages = this.agent.messages;\n\t\tfor (const msg of messages) {\n\t\t\tif (msg.role === \"user\") {\n\t\t\t\t// Check if this is a compaction summary\n\t\t\t\tconst content = msg.content;\n\t\t\t\tif (Array.isArray(content) && content.length > 0) {\n\t\t\t\t\tconst textBlock = content[0] as { type: string; text?: string };\n\t\t\t\t\tif (textBlock.type === \"text\" && textBlock.text?.startsWith('<summary type=\"compaction\"')) {\n\t\t\t\t\t\t// Skip compaction summaries in rebuild (they are injected by buildSessionContext)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (textBlock.type === \"text\" && textBlock.text?.startsWith('<summary type=\"branch\"')) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst text = extractTextFromMessage(msg);\n\t\t\t\tif (text) {\n\t\t\t\t\tthis.chatContainer.addChild(new UserMessageComponent(text, getMarkdownTheme()));\n\t\t\t\t}\n\t\t\t} else if (msg.role === \"assistant\") {\n\t\t\t\tconst assistantMsg = msg as import(\"edge-pi\").AssistantModelMessage;\n\t\t\t\tconst textParts: string[] = [];\n\t\t\t\tfor (const block of assistantMsg.content) {\n\t\t\t\t\tconst b = block as {\n\t\t\t\t\t\ttype: string;\n\t\t\t\t\t\ttext?: string;\n\t\t\t\t\t\ttoolName?: string;\n\t\t\t\t\t\tinput?: unknown;\n\t\t\t\t\t\ttoolCallId?: string;\n\t\t\t\t\t};\n\t\t\t\t\tif (b.type === \"text\" && b.text) {\n\t\t\t\t\t\ttextParts.push(b.text);\n\t\t\t\t\t} else if (b.type === \"tool-call\" && b.toolName) {\n\t\t\t\t\t\tconst args =\n\t\t\t\t\t\t\ttypeof b.input === \"object\" && b.input !== null ? (b.input as Record<string, unknown>) : {};\n\t\t\t\t\t\tconst toolComp = new ToolExecutionComponent(b.toolName, args);\n\t\t\t\t\t\tif (this.toolOutputExpanded) {\n\t\t\t\t\t\t\ttoolComp.setExpanded(true);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Mark as completed (we don't have the result here, just show collapsed)\n\t\t\t\t\t\ttoolComp.updateResult({ text: \"(from history)\" }, false, false);\n\t\t\t\t\t\tthis.chatContainer.addChild(toolComp);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (textParts.length > 0) {\n\t\t\t\t\tconst comp = new AssistantMessageComponent(getMarkdownTheme());\n\t\t\t\t\tcomp.updateText(textParts.join(\"\"));\n\t\t\t\t\tthis.chatContainer.addChild(comp);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Skip tool messages in UI rebuild - they are consumed by tool-call components\n\t\t}\n\n\t\tthis.ui.requestRender();\n\t}\n\n\t// ========================================================================\n\t// Footer Token Tracking\n\t// ========================================================================\n\n\t/**\n\t * Update the footer with current token count information.\n\t */\n\tprivate updateFooterTokens(): void {\n\t\tconst contextTokens = estimateContextTokens([...this.agent.messages]);\n\t\tthis.footer.setTokenInfo(contextTokens, this.contextWindow);\n\t\tthis.footer.setAutoCompaction(this.autoCompaction);\n\t\tthis.ui?.requestRender();\n\t}\n\n\t/**\n\t * Check if the current provider is using an OAuth subscription credential.\n\t */\n\tprivate isSubscriptionProvider(): boolean {\n\t\tconst { authStorage } = this.options;\n\t\tif (!authStorage) return false;\n\t\tconst cred = authStorage.get(this.currentProvider);\n\t\treturn cred?.type === \"oauth\";\n\t}\n\n\t/**\n\t * Replace the footer component and update token info.\n\t */\n\tprivate updateFooter(): void {\n\t\tthis.footer = new FooterComponent(this.currentProvider, this.currentModelId);\n\t\tthis.footer.setSubscription(this.isSubscriptionProvider());\n\t\tthis.updateFooterTokens();\n\n\t\t// Replace footer in UI\n\t\tconst children = this.ui.children;\n\t\tchildren[children.length - 1] = this.footer;\n\t\tthis.ui.requestRender();\n\t}\n\n\t// ========================================================================\n\t// Startup Resource Display\n\t// ========================================================================\n\n\tprivate formatDisplayPath(p: string): string {\n\t\tconst home = process.env.HOME || process.env.USERPROFILE || \"\";\n\t\tif (home && p.startsWith(home)) {\n\t\t\treturn `~${p.slice(home.length)}`;\n\t\t}\n\t\treturn p;\n\t}\n\n\tprivate showLoadedResources(contextFiles: ContextFile[], skills: Skill[], prompts: PromptTemplate[]): void {\n\t\tconst sectionHeader = (name: string) => chalk.cyan(`[${name}]`);\n\n\t\tif (contextFiles.length > 0) {\n\t\t\tconst contextList = contextFiles.map((f) => chalk.dim(` ${this.formatDisplayPath(f.path)}`)).join(\"\\n\");\n\t\t\tthis.headerContainer.addChild(new Text(`${sectionHeader(\"Context\")}\\n${contextList}`, 0, 0));\n\t\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\t}\n\n\t\tif (skills.length > 0) {\n\t\t\tconst skillList = skills.map((s) => chalk.dim(` ${this.formatDisplayPath(s.filePath)}`)).join(\"\\n\");\n\t\t\tthis.headerContainer.addChild(new Text(`${sectionHeader(\"Skills\")}\\n${skillList}`, 0, 0));\n\t\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\t}\n\n\t\tif (prompts.length > 0) {\n\t\t\tconst promptList = prompts\n\t\t\t\t.map((p) => {\n\t\t\t\t\tconst sourceLabel = chalk.cyan(p.source);\n\t\t\t\t\treturn chalk.dim(` ${sourceLabel} /${p.name}`);\n\t\t\t\t})\n\t\t\t\t.join(\"\\n\");\n\t\t\tthis.headerContainer.addChild(new Text(`${sectionHeader(\"Prompts\")}\\n${promptList}`, 0, 0));\n\t\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Commands\n\t// ========================================================================\n\n\tprivate showHelp(): void {\n\t\tconst helpText = [\n\t\t\tchalk.bold(\"Commands:\"),\n\t\t\t\" !<command> Run inline bash and include output in context\",\n\t\t\t\" !!<command> Run inline bash but exclude output from context\",\n\t\t\t\" /resume Resume a previous session\",\n\t\t\t\" /compact [text] Compact the session context (optional instructions)\",\n\t\t\t\" /auto-compact Toggle automatic context compaction\",\n\t\t\t\" /model Switch model (Ctrl+L)\",\n\t\t\t\" /login Login to an OAuth provider\",\n\t\t\t\" /logout Logout from an OAuth provider\",\n\t\t\t\" /skills List loaded skills\",\n\t\t\t\" /skill:<name> Invoke a skill by name\",\n\t\t\t\" /quit, /exit Exit the CLI\",\n\t\t].join(\"\\n\");\n\n\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\tthis.chatContainer.addChild(new Text(helpText, 1, 0));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate showSkills(): void {\n\t\tconst { skills = [] } = this.options;\n\n\t\tif (skills.length === 0) {\n\t\t\tthis.showStatus(chalk.dim(\"No skills loaded.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst lines: string[] = [];\n\t\tfor (const skill of skills) {\n\t\t\tconst hidden = skill.disableModelInvocation ? chalk.dim(\" (hidden from model)\") : \"\";\n\t\t\tlines.push(` ${chalk.bold(skill.name)}${hidden}`);\n\t\t\tlines.push(chalk.dim(` ${skill.description}`));\n\t\t\tlines.push(chalk.dim(` ${skill.filePath}`));\n\t\t}\n\n\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\tthis.chatContainer.addChild(new Text(lines.join(\"\\n\"), 1, 0));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate async handleSkillInvocation(skillName: string): Promise<void> {\n\t\tconst { skills = [] } = this.options;\n\t\tconst skill = skills.find((s) => s.name === skillName);\n\n\t\tif (!skill) {\n\t\t\tthis.showStatus(chalk.red(`Skill \"${skillName}\" not found.`));\n\t\t\treturn;\n\t\t}\n\n\t\tconst skillPrompt = `Please read and follow the instructions in the skill file: ${skill.filePath}`;\n\t\tthis.chatContainer.addChild(new UserMessageComponent(skillPrompt, getMarkdownTheme()));\n\t\tthis.ui.requestRender();\n\t\tawait this.streamPrompt(skillPrompt);\n\t}\n\n\t// ========================================================================\n\t// OAuth Login/Logout\n\t// ========================================================================\n\n\tprivate async handleLogin(): Promise<void> {\n\t\tconst { authStorage } = this.options;\n\t\tif (!authStorage) {\n\t\t\tthis.showStatus(chalk.red(\"Auth storage not available.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst providers = authStorage.getProviders();\n\t\tif (providers.length === 0) {\n\t\t\tthis.showStatus(chalk.dim(\"No OAuth providers registered.\"));\n\t\t\treturn;\n\t\t}\n\n\t\t// Use SelectList overlay for provider selection\n\t\tconst items: SelectItem[] = providers.map((p) => {\n\t\t\tconst loggedIn = authStorage.get(p.id)?.type === \"oauth\" ? \" (logged in)\" : \"\";\n\t\t\treturn { value: p.id, label: `${p.name}${loggedIn}` };\n\t\t});\n\n\t\tconst selected = await this.showSelectList(\"Login to OAuth provider\", items);\n\t\tif (!selected) return;\n\n\t\tconst provider = providers.find((p) => p.id === selected);\n\t\tif (!provider) return;\n\n\t\tthis.showStatus(chalk.dim(`Logging in to ${provider.name}...`));\n\n\t\ttry {\n\t\t\tawait authStorage.login(provider.id, {\n\t\t\t\tonAuth: (info) => {\n\t\t\t\t\tconst lines = [chalk.bold(\"Open this URL in your browser:\"), chalk.cyan(info.url)];\n\t\t\t\t\tif (info.instructions) {\n\t\t\t\t\t\tlines.push(chalk.dim(info.instructions));\n\t\t\t\t\t}\n\t\t\t\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\t\t\t\tthis.chatContainer.addChild(new Text(lines.join(\"\\n\"), 1, 0));\n\t\t\t\t\tthis.ui.requestRender();\n\n\t\t\t\t\t// Try to open browser\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst { execSync } = require(\"node:child_process\") as typeof import(\"node:child_process\");\n\t\t\t\t\t\tconst platform = process.platform;\n\t\t\t\t\t\tif (platform === \"darwin\") {\n\t\t\t\t\t\t\texecSync(`open \"${info.url}\"`, { stdio: \"ignore\" });\n\t\t\t\t\t\t} else if (platform === \"linux\") {\n\t\t\t\t\t\t\texecSync(`xdg-open \"${info.url}\" 2>/dev/null || sensible-browser \"${info.url}\" 2>/dev/null`, {\n\t\t\t\t\t\t\t\tstdio: \"ignore\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t} else if (platform === \"win32\") {\n\t\t\t\t\t\t\texecSync(`start \"\" \"${info.url}\"`, { stdio: \"ignore\" });\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Silently fail - user can open manually\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tonPrompt: async (promptInfo) => {\n\t\t\t\t\t// Show prompt message and wait for user input\n\t\t\t\t\tthis.showStatus(chalk.dim(promptInfo.message));\n\t\t\t\t\tconst answer = await this.getUserInput();\n\t\t\t\t\treturn answer.trim();\n\t\t\t\t},\n\t\t\t\tonProgress: (message) => {\n\t\t\t\t\tthis.showStatus(chalk.dim(message));\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tthis.footer.setSubscription(this.isSubscriptionProvider());\n\t\t\tthis.ui.requestRender();\n\t\t\tthis.showStatus(chalk.green(`Logged in to ${provider.name}. Credentials saved.`));\n\t\t} catch (error) {\n\t\t\tconst msg = error instanceof Error ? error.message : String(error);\n\t\t\tif (msg !== \"Login cancelled\") {\n\t\t\t\tthis.showStatus(chalk.red(`Login failed: ${msg}`));\n\t\t\t} else {\n\t\t\t\tthis.showStatus(chalk.dim(\"Login cancelled.\"));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async handleLogout(): Promise<void> {\n\t\tconst { authStorage } = this.options;\n\t\tif (!authStorage) {\n\t\t\tthis.showStatus(chalk.red(\"Auth storage not available.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst loggedIn = authStorage\n\t\t\t.list()\n\t\t\t.filter((id) => authStorage.get(id)?.type === \"oauth\")\n\t\t\t.map((id) => {\n\t\t\t\tconst provider = authStorage.getProvider(id);\n\t\t\t\treturn { id, name: provider?.name ?? id };\n\t\t\t});\n\n\t\tif (loggedIn.length === 0) {\n\t\t\tthis.showStatus(chalk.dim(\"No OAuth providers logged in. Use /login first.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst items: SelectItem[] = loggedIn.map((p) => ({\n\t\t\tvalue: p.id,\n\t\t\tlabel: p.name,\n\t\t}));\n\n\t\tconst selected = await this.showSelectList(\"Logout from OAuth provider\", items);\n\t\tif (!selected) return;\n\n\t\tconst entry = loggedIn.find((p) => p.id === selected);\n\t\tif (!entry) return;\n\n\t\tauthStorage.logout(entry.id);\n\t\tthis.showStatus(chalk.green(`Logged out of ${entry.name}.`));\n\t}\n\n\t// ========================================================================\n\t// Resume Session\n\t// ========================================================================\n\n\t/**\n\t * List session files from the session directory, sorted by modification time (newest first).\n\t * Returns metadata for each session including the first user message as a preview.\n\t */\n\tprivate listAvailableSessions(): { path: string; mtime: number; preview: string; timestamp: string }[] {\n\t\tconst { sessionDir } = this.options;\n\t\tif (!sessionDir || !existsSync(sessionDir)) return [];\n\n\t\ttry {\n\t\t\tconst files = readdirSync(sessionDir)\n\t\t\t\t.filter((f: string) => f.endsWith(\".jsonl\"))\n\t\t\t\t.map((f: string) => {\n\t\t\t\t\tconst filePath = join(sessionDir, f);\n\t\t\t\t\tconst mtime = statSync(filePath).mtime.getTime();\n\t\t\t\t\treturn { name: f, path: filePath, mtime };\n\t\t\t\t})\n\t\t\t\t.sort((a, b) => b.mtime - a.mtime);\n\n\t\t\tconst sessions: { path: string; mtime: number; preview: string; timestamp: string }[] = [];\n\t\t\tfor (const file of files) {\n\t\t\t\t// Skip the current session file\n\t\t\t\tif (this.options.sessionManager?.getSessionFile() === file.path) continue;\n\n\t\t\t\tconst preview = this.getSessionPreview(file.path);\n\t\t\t\tconst timestamp = new Date(file.mtime).toLocaleString();\n\t\t\t\tsessions.push({ path: file.path, mtime: file.mtime, preview, timestamp });\n\t\t\t}\n\t\t\treturn sessions;\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Extract the first user message from a session file for preview.\n\t */\n\tprivate getSessionPreview(filePath: string): string {\n\t\ttry {\n\t\t\tconst content = readFileSync(filePath, \"utf-8\");\n\t\t\tconst lines = content.trim().split(\"\\n\");\n\t\t\tfor (const line of lines) {\n\t\t\t\tif (!line.trim()) continue;\n\t\t\t\ttry {\n\t\t\t\t\tconst entry = JSON.parse(line);\n\t\t\t\t\tif (entry.type === \"message\" && entry.message?.role === \"user\") {\n\t\t\t\t\t\tconst msg = entry.message;\n\t\t\t\t\t\tlet text = \"\";\n\t\t\t\t\t\tif (typeof msg.content === \"string\") {\n\t\t\t\t\t\t\ttext = msg.content;\n\t\t\t\t\t\t} else if (Array.isArray(msg.content)) {\n\t\t\t\t\t\t\tfor (const block of msg.content) {\n\t\t\t\t\t\t\t\tif (block.type === \"text\" && block.text) {\n\t\t\t\t\t\t\t\t\ttext = block.text;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Truncate and clean up for display\n\t\t\t\t\t\ttext = text.replace(/\\n/g, \" \").trim();\n\t\t\t\t\t\tif (text.length > 80) {\n\t\t\t\t\t\t\ttext = `${text.slice(0, 77)}...`;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn text || \"(empty message)\";\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Skip malformed lines\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn \"(no messages)\";\n\t\t} catch {\n\t\t\treturn \"(unreadable)\";\n\t\t}\n\t}\n\n\t/**\n\t * Format a relative time string (e.g. \"2 hours ago\", \"3 days ago\").\n\t */\n\tprivate formatRelativeTime(mtime: number): string {\n\t\tconst now = Date.now();\n\t\tconst diffMs = now - mtime;\n\t\tconst diffSec = Math.floor(diffMs / 1000);\n\t\tconst diffMin = Math.floor(diffSec / 60);\n\t\tconst diffHour = Math.floor(diffMin / 60);\n\t\tconst diffDay = Math.floor(diffHour / 24);\n\n\t\tif (diffMin < 1) return \"just now\";\n\t\tif (diffMin < 60) return `${diffMin}m ago`;\n\t\tif (diffHour < 24) return `${diffHour}h ago`;\n\t\tif (diffDay < 30) return `${diffDay}d ago`;\n\t\treturn new Date(mtime).toLocaleDateString();\n\t}\n\n\t/**\n\t * Handle the /resume command: show a list of previous sessions and load the selected one.\n\t */\n\tprivate async handleResume(): Promise<void> {\n\t\tconst sessions = this.listAvailableSessions();\n\t\tif (sessions.length === 0) {\n\t\t\tthis.showStatus(chalk.yellow(\"No previous sessions found.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst items: SelectItem[] = sessions.map((s) => ({\n\t\t\tvalue: s.path,\n\t\t\tlabel: `${chalk.dim(this.formatRelativeTime(s.mtime))} ${s.preview}`,\n\t\t}));\n\n\t\tconst selected = await this.showSelectList(\"Resume session\", items);\n\t\tif (!selected) return;\n\n\t\tconst session = sessions.find((s) => s.path === selected);\n\t\tif (!session) return;\n\n\t\ttry {\n\t\t\t// Open the selected session\n\t\t\tconst sessionDir = this.options.sessionDir!;\n\t\t\tconst newSessionManager = SessionManagerClass.open(selected, sessionDir);\n\n\t\t\t// Rebuild agent messages from session context\n\t\t\tconst context = newSessionManager.buildSessionContext();\n\t\t\tthis.agent.setMessages(context.messages);\n\n\t\t\t// Update session manager reference\n\t\t\tthis.options.sessionManager = newSessionManager;\n\n\t\t\t// Rebuild the chat UI\n\t\t\tthis.chatContainer.clear();\n\t\t\tthis.rebuildChatFromSession();\n\n\t\t\t// Update footer tokens\n\t\t\tthis.updateFooterTokens();\n\n\t\t\tconst msgCount = context.messages.length;\n\t\t\tthis.showStatus(chalk.green(`Resumed session (${msgCount} messages)`));\n\t\t} catch (error) {\n\t\t\tconst msg = error instanceof Error ? error.message : String(error);\n\t\t\tthis.showStatus(chalk.red(`Failed to resume session: ${msg}`));\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Select List (overlay pattern from pi-coding-agent)\n\t// ========================================================================\n\n\tprivate showSelectList(title: string, items: SelectItem[]): Promise<string | null> {\n\t\treturn new Promise((resolve) => {\n\t\t\tconst container = new Container();\n\n\t\t\tcontainer.addChild(new Spacer(1));\n\t\t\tcontainer.addChild(new Text(chalk.bold.cyan(title), 1, 0));\n\n\t\t\tconst selectList = new SelectList(items, Math.min(items.length, 10), getSelectListTheme());\n\t\t\tselectList.onSelect = (item) => {\n\t\t\t\t// Restore normal UI\n\t\t\t\tthis.pendingContainer.clear();\n\t\t\t\tthis.editorContainer.addChild(this.editor);\n\t\t\t\tthis.ui.setFocus(this.editor);\n\t\t\t\tthis.ui.requestRender();\n\t\t\t\tresolve(item.value);\n\t\t\t};\n\t\t\tselectList.onCancel = () => {\n\t\t\t\tthis.pendingContainer.clear();\n\t\t\t\tthis.editorContainer.addChild(this.editor);\n\t\t\t\tthis.ui.setFocus(this.editor);\n\t\t\t\tthis.ui.requestRender();\n\t\t\t\tresolve(null);\n\t\t\t};\n\t\t\tcontainer.addChild(selectList);\n\t\t\tcontainer.addChild(new Text(chalk.dim(\"↑↓ navigate • enter select • esc cancel\"), 1, 0));\n\t\t\tcontainer.addChild(new Spacer(1));\n\n\t\t\t// Replace editor area with select list\n\t\t\tthis.editorContainer.clear();\n\t\t\tthis.pendingContainer.clear();\n\t\t\tthis.pendingContainer.addChild(container);\n\t\t\tthis.ui.setFocus(selectList);\n\t\t\tthis.ui.requestRender();\n\t\t});\n\t}\n\n\t// ========================================================================\n\t// Status & Utilities\n\t// ========================================================================\n\n\tprivate showStatus(text: string): void {\n\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\tthis.chatContainer.addChild(new Text(text, 1, 0));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate updateEditorBorderColor(): void {\n\t\tthis.editorTheme.borderColor = this.isBashMode ? (s: string) => chalk.yellow(s) : (s: string) => chalk.gray(s);\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate shutdown(): void {\n\t\tthis.ui.stop();\n\t\tconsole.log(chalk.dim(\"\\nGoodbye.\"));\n\t\tprocess.exit(0);\n\t}\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction buildUserMessage(text: string, imageParts?: ImagePart[]): ModelMessage[] {\n\tconst content: Array<{ type: \"text\"; text: string } | ImagePart> = [{ type: \"text\" as const, text }];\n\tif (imageParts && imageParts.length > 0) {\n\t\tcontent.push(...imageParts);\n\t}\n\treturn [{ role: \"user\" as const, content }];\n}\n\nfunction extractTextFromMessage(msg: ModelMessage): string {\n\tif (msg.role === \"user\") {\n\t\tconst content = (msg as import(\"edge-pi\").UserModelMessage).content;\n\t\tif (typeof content === \"string\") return content;\n\t\tif (Array.isArray(content)) {\n\t\t\treturn content\n\t\t\t\t.filter((c): c is { type: \"text\"; text: string } => (c as { type: string }).type === \"text\")\n\t\t\t\t.map((c) => c.text)\n\t\t\t\t.join(\"\");\n\t\t}\n\t}\n\treturn \"\";\n}\n"]}
1
+ {"version":3,"file":"interactive-mode.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/interactive-mode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAqBH,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAgB,cAAc,EAAE,MAAM,SAAS,CAAC;AAW5F,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AA8B7C,MAAM,WAAW,sBAAsB;IACtC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;IACjB,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC;IAC7B,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,sFAAsF;IACtF,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6FAA6F;IAC7F,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IAC5E,2DAA2D;IAC3D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,iEAAiE;IACjE,aAAa,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAG3G","sourcesContent":["/**\n * Interactive mode using @mariozechner/pi-tui.\n *\n * Replaces the old readline-based REPL with a proper TUI that matches\n * the UX patterns from @mariozechner/pi-coding-agent:\n * - Editor component for input with submit/escape handling\n * - Markdown rendering for assistant responses\n * - Tool execution components with collapsible output\n * - Footer with model/provider info and token stats\n * - Container-based layout (header → chat → pending → editor → footer)\n * - Context compaction (manual /compact + auto mode)\n */\n\nimport { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n\tCombinedAutocompleteProvider,\n\tContainer,\n\tEditor,\n\tKey,\n\tLoader,\n\tmatchesKey,\n\tProcessTerminal,\n\ttype SelectItem,\n\tSelectList,\n\ttype SlashCommand,\n\tSpacer,\n\tText,\n\tTUI,\n} from \"@mariozechner/pi-tui\";\nimport type { ImagePart } from \"ai\";\nimport chalk from \"chalk\";\nimport type { CodingAgent, CodingAgentConfig, ModelMessage, SessionManager } from \"edge-pi\";\nimport {\n\ttype CompactionResult,\n\ttype CompactionSettings,\n\tcompact,\n\tDEFAULT_COMPACTION_SETTINGS,\n\testimateContextTokens,\n\tprepareCompaction,\n\tSessionManager as SessionManagerClass,\n\tshouldCompact,\n} from \"edge-pi\";\nimport type { AuthStorage } from \"../../auth/auth-storage.js\";\nimport type { ContextFile } from \"../../context.js\";\nimport { getLatestModels } from \"../../model-factory.js\";\nimport type { PromptTemplate } from \"../../prompts.js\";\nimport { expandPromptTemplate } from \"../../prompts.js\";\nimport type { SettingsManager } from \"../../settings.js\";\nimport type { Skill } from \"../../skills.js\";\nimport { executeBashCommand } from \"../../utils/bash-executor.js\";\nimport { type ClipboardImage, extensionForImageMimeType, readClipboardImage } from \"../../utils/clipboard-image.js\";\nimport { formatPendingMessages, parseBashInput } from \"./bash-helpers.js\";\nimport { AssistantMessageComponent } from \"./components/assistant-message.js\";\nimport { BashExecutionComponent } from \"./components/bash-execution.js\";\nimport { CompactionSummaryComponent } from \"./components/compaction-summary.js\";\nimport { FooterComponent } from \"./components/footer.js\";\nimport { ToolExecutionComponent, type ToolOutput } from \"./components/tool-execution.js\";\nimport { UserMessageComponent } from \"./components/user-message.js\";\nimport { getEditorTheme, getMarkdownTheme, getSelectListTheme } from \"./theme.js\";\n\n/** Default context window size (used when model doesn't report one). */\nconst DEFAULT_CONTEXT_WINDOW = 200_000;\n\n/** Extract display-friendly output from a tool result. Handles both plain strings and structured objects with text/image fields. */\nfunction extractToolOutput(output: unknown): ToolOutput {\n\tif (typeof output === \"string\") {\n\t\treturn { text: output };\n\t}\n\tif (typeof output === \"object\" && output !== null && \"text\" in output && typeof (output as any).text === \"string\") {\n\t\tconst obj = output as any;\n\t\treturn {\n\t\t\ttext: obj.text,\n\t\t\t...(obj.image && { image: obj.image }),\n\t\t};\n\t}\n\treturn { text: JSON.stringify(output) };\n}\n\nexport interface InteractiveModeOptions {\n\tinitialMessage?: string;\n\tinitialMessages?: string[];\n\tsessionManager?: SessionManager;\n\tskills?: Skill[];\n\tcontextFiles?: ContextFile[];\n\tprompts?: PromptTemplate[];\n\tverbose?: boolean;\n\tprovider: string;\n\tmodelId: string;\n\tauthStorage?: AuthStorage;\n\t/** Settings manager for persisting user preferences (provider, model, compaction). */\n\tsettingsManager?: SettingsManager;\n\t/** Path to the `fd` binary for @ file autocomplete, or undefined if unavailable. */\n\tfdPath?: string;\n\t/** Called when the user switches model via Ctrl+L. Returns a new agent for the new model. */\n\tonModelChange?: (provider: string, modelId: string) => Promise<CodingAgent>;\n\t/** Context window size for the model. Defaults to 200k. */\n\tcontextWindow?: number;\n\t/** Directory where session files are stored. Required for /resume. */\n\tsessionDir?: string;\n\t/** Agent config used to recreate agents when resuming sessions. */\n\tagentConfig?: CodingAgentConfig;\n\t/** When true, show the session picker immediately on startup. */\n\tresumeOnStart?: boolean;\n}\n\n/**\n * Run the interactive TUI mode with streaming output.\n */\nexport async function runInteractiveMode(agent: CodingAgent, options: InteractiveModeOptions): Promise<void> {\n\tconst mode = new InteractiveMode(agent, options);\n\tawait mode.run();\n}\n\n// ============================================================================\n// InteractiveMode class\n// ============================================================================\n\nclass InteractiveMode {\n\tprivate agent: CodingAgent;\n\tprivate options: InteractiveModeOptions;\n\tprivate currentProvider: string;\n\tprivate currentModelId: string;\n\n\tprivate ui!: TUI;\n\tprivate headerContainer!: Container;\n\tprivate chatContainer!: Container;\n\tprivate pendingContainer!: Container;\n\tprivate pendingMessagesContainer!: Container;\n\tprivate editorContainer!: Container;\n\tprivate editor!: Editor;\n\tprivate editorTheme!: import(\"@mariozechner/pi-tui\").EditorTheme;\n\n\t// Message queues\n\tprivate steeringMessages: string[] = [];\n\tprivate followUpMessages: string[] = [];\n\tprivate isStreaming = false;\n\n\t// Inline bash state\n\tprivate isBashMode = false;\n\tprivate isBashRunning = false;\n\tprivate bashAbortController: AbortController | null = null;\n\tprivate bashComponent: import(\"./components/bash-execution.js\").BashExecutionComponent | undefined = undefined;\n\tprivate footer!: FooterComponent;\n\n\t// Loading animation during agent processing\n\tprivate loadingAnimation: Loader | undefined = undefined;\n\n\t// Streaming state\n\tprivate streamingComponent: AssistantMessageComponent | undefined = undefined;\n\tprivate streamingText = \"\";\n\tprivate hadToolResults = false;\n\n\t// Tool execution tracking: toolCallId → component\n\tprivate pendingTools = new Map<string, ToolExecutionComponent>();\n\n\t// Tool output expansion state\n\tprivate toolOutputExpanded = false;\n\n\t// Callback for resolving user input promise\n\tprivate onInputCallback?: (text: string) => void;\n\n\t// Pending clipboard images to attach to the next message\n\tprivate pendingImages: ClipboardImage[] = [];\n\n\t// Compaction state\n\tprivate contextWindow: number;\n\tprivate compactionSettings: CompactionSettings;\n\tprivate autoCompaction = true;\n\tprivate isCompacting = false;\n\tprivate compactionAbortController: AbortController | null = null;\n\n\tconstructor(agent: CodingAgent, options: InteractiveModeOptions) {\n\t\tthis.agent = agent;\n\t\tthis.options = options;\n\t\tthis.currentProvider = options.provider;\n\t\tthis.currentModelId = options.modelId;\n\t\tthis.contextWindow = options.contextWindow ?? DEFAULT_CONTEXT_WINDOW;\n\n\t\t// Initialize compaction settings from persisted settings if available\n\t\tconst savedCompaction = options.settingsManager?.getCompaction();\n\t\tthis.compactionSettings = {\n\t\t\t...DEFAULT_COMPACTION_SETTINGS,\n\t\t\t...(savedCompaction?.reserveTokens !== undefined && { reserveTokens: savedCompaction.reserveTokens }),\n\t\t\t...(savedCompaction?.keepRecentTokens !== undefined && { keepRecentTokens: savedCompaction.keepRecentTokens }),\n\t\t};\n\t\tthis.autoCompaction = options.settingsManager?.getCompactionEnabled() ?? true;\n\t}\n\n\tasync run(): Promise<void> {\n\t\tthis.initUI();\n\t\tthis.updateFooterTokens();\n\n\t\t// Show session picker immediately if --resume was passed\n\t\tif (this.options.resumeOnStart) {\n\t\t\tawait this.handleResume();\n\t\t}\n\n\t\t// Process initial messages\n\t\tconst { initialMessage, initialMessages = [] } = this.options;\n\n\t\tconst allInitial: string[] = [];\n\t\tif (initialMessage) allInitial.push(initialMessage);\n\t\tallInitial.push(...initialMessages);\n\n\t\tfor (const msg of allInitial) {\n\t\t\tthis.chatContainer.addChild(new UserMessageComponent(msg, getMarkdownTheme()));\n\t\t\tthis.ui.requestRender();\n\t\t\tawait this.streamPrompt(msg);\n\t\t}\n\n\t\t// Main interactive loop\n\t\twhile (true) {\n\t\t\tconst userInput = await this.getUserInput();\n\t\t\tawait this.handleUserInput(userInput);\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// UI Setup\n\t// ========================================================================\n\n\tprivate initUI(): void {\n\t\tconst { provider, modelId, skills = [], contextFiles = [], prompts = [], verbose, sessionManager } = this.options;\n\n\t\tthis.ui = new TUI(new ProcessTerminal());\n\n\t\t// Header\n\t\tthis.headerContainer = new Container();\n\t\tconst logo = chalk.bold(\"epi\") + chalk.dim(` - ${provider}/${modelId}`);\n\n\t\tconst hints = [\n\t\t\t`${chalk.dim(\"Escape\")} to abort`,\n\t\t\t`${chalk.dim(\"!\")} inline bash`,\n\t\t\t`${chalk.dim(\"Alt+Enter\")} follow-up while streaming`,\n\t\t\t`${chalk.dim(\"Ctrl+C\")} to exit`,\n\t\t\t`${chalk.dim(\"Ctrl+E\")} to expand tools`,\n\t\t\t`${chalk.dim(\"Ctrl+L\")} to switch model`,\n\t\t\t`${chalk.dim(\"Ctrl+V\")} to paste image`,\n\t\t\t`${chalk.dim(\"↑/↓\")} to browse history`,\n\t\t\t`${chalk.dim(\"@\")} for file references`,\n\t\t\t`${chalk.dim(\"/\")} for commands`,\n\t\t].join(\"\\n\");\n\n\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\tthis.headerContainer.addChild(new Text(`${logo}\\n${hints}`, 1, 0));\n\t\tthis.headerContainer.addChild(new Spacer(1));\n\n\t\tif (verbose && sessionManager?.getSessionFile()) {\n\t\t\tthis.headerContainer.addChild(new Text(chalk.dim(`Session: ${sessionManager.getSessionFile()}`), 1, 0));\n\t\t}\n\n\t\t// Show loaded context, skills, and prompts at startup\n\t\tthis.showLoadedResources(contextFiles, skills, prompts);\n\n\t\t// Chat area\n\t\tthis.chatContainer = new Container();\n\n\t\t// Pending messages (loading animations, status)\n\t\tthis.pendingContainer = new Container();\n\n\t\t// Pending steering/follow-up messages\n\t\tthis.pendingMessagesContainer = new Container();\n\n\t\t// Editor with slash command autocomplete\n\t\tthis.editorTheme = getEditorTheme();\n\t\tthis.editor = new Editor(this.ui, this.editorTheme);\n\t\tthis.editor.setAutocompleteProvider(this.buildAutocompleteProvider());\n\t\tthis.editorContainer = new Container();\n\t\tthis.editorContainer.addChild(this.editor);\n\n\t\t// Footer\n\t\tthis.footer = new FooterComponent(this.currentProvider, this.currentModelId);\n\t\tthis.footer.setAutoCompaction(this.autoCompaction);\n\t\tthis.footer.setSubscription(this.isSubscriptionProvider());\n\n\t\t// Assemble layout\n\t\tthis.ui.addChild(this.headerContainer);\n\t\tthis.ui.addChild(this.chatContainer);\n\t\tthis.ui.addChild(this.pendingMessagesContainer);\n\t\tthis.ui.addChild(this.pendingContainer);\n\t\tthis.ui.addChild(this.editorContainer);\n\t\tthis.ui.addChild(this.footer);\n\n\t\tthis.ui.setFocus(this.editor);\n\t\tthis.setupKeyHandlers();\n\n\t\tthis.ui.start();\n\t}\n\n\t// ========================================================================\n\t// Key Handlers\n\t// ========================================================================\n\n\tprivate setupKeyHandlers(): void {\n\t\tthis.editor.onChange = (text: string) => {\n\t\t\tconst wasBashMode = this.isBashMode;\n\t\t\tthis.isBashMode = text.trimStart().startsWith(\"!\");\n\t\t\tif (wasBashMode !== this.isBashMode) {\n\t\t\t\tthis.updateEditorBorderColor();\n\t\t\t}\n\t\t};\n\t\tthis.editor.onSubmit = (text: string) => {\n\t\t\ttext = text.trim();\n\t\t\tif (!text) return;\n\n\t\t\t// If agent is streaming, Enter becomes a steering message\n\t\t\tif (this.isStreaming) {\n\t\t\t\tthis.agent.steer({ role: \"user\", content: [{ type: \"text\", text }] });\n\t\t\t\tthis.steeringMessages.push(text);\n\t\t\t\tthis.editor.setText(\"\");\n\t\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.editor.addToHistory(text);\n\t\t\tthis.editor.setText(\"\");\n\n\t\t\tif (this.onInputCallback) {\n\t\t\t\tthis.onInputCallback(text);\n\t\t\t}\n\t\t};\n\n\t\tconst origHandleInput = this.editor.handleInput.bind(this.editor);\n\t\tthis.editor.handleInput = (data: string) => {\n\t\t\t// Escape: abort if agent is running or compacting\n\t\t\tif (matchesKey(data, Key.escape)) {\n\t\t\t\tif (this.isBashRunning && this.bashAbortController) {\n\t\t\t\t\tthis.bashAbortController.abort();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (this.isCompacting && this.compactionAbortController) {\n\t\t\t\t\tthis.compactionAbortController.abort();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (this.loadingAnimation) {\n\t\t\t\t\tthis.agent.abort();\n\t\t\t\t\tthis.stopLoading();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Ctrl+C: exit\n\t\t\tif (matchesKey(data, Key.ctrl(\"c\"))) {\n\t\t\t\tthis.shutdown();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Ctrl+D: exit if editor is empty\n\t\t\tif (matchesKey(data, Key.ctrl(\"d\"))) {\n\t\t\t\tif (this.editor.getText().length === 0) {\n\t\t\t\t\tthis.shutdown();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Ctrl+E: toggle tool output expansion\n\t\t\tif (matchesKey(data, Key.ctrl(\"e\"))) {\n\t\t\t\tthis.toggleToolExpansion();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Ctrl+L: select model\n\t\t\tif (matchesKey(data, Key.ctrl(\"l\"))) {\n\t\t\t\tthis.handleModelSelect();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Ctrl+V: paste image from clipboard\n\t\t\tif (matchesKey(data, Key.ctrl(\"v\"))) {\n\t\t\t\tthis.handleClipboardImagePaste();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Alt+Enter (Option+Enter on Mac): follow-up while streaming (or submit normally when idle)\n\t\t\tif (matchesKey(data, Key.alt(\"enter\"))) {\n\t\t\t\tconst text = this.editor.getText().trim();\n\t\t\t\tif (!text) return;\n\n\t\t\t\tif (this.isStreaming) {\n\t\t\t\t\tthis.followUpMessages.push(text);\n\t\t\t\t\tthis.editor.setText(\"\");\n\t\t\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Not streaming: treat like regular submit\n\t\t\t\tthis.editor.onSubmit?.(text);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Alt+Up (Option+Up on Mac): dequeue all queued messages back into the editor\n\t\t\tif (matchesKey(data, Key.alt(\"up\"))) {\n\t\t\t\tconst restored = this.clearAllQueues();\n\t\t\t\tif (restored.length > 0) {\n\t\t\t\t\tthis.editor.setText(restored.join(\"\\n\\n\"));\n\t\t\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\torigHandleInput(data);\n\t\t};\n\t}\n\n\t// ========================================================================\n\t// User Input\n\t// ========================================================================\n\n\tprivate getUserInput(): Promise<string> {\n\t\treturn new Promise((resolve) => {\n\t\t\tthis.onInputCallback = (text: string) => {\n\t\t\t\tthis.onInputCallback = undefined;\n\t\t\t\tresolve(text);\n\t\t\t};\n\t\t});\n\t}\n\n\tprivate async handleUserInput(input: string): Promise<void> {\n\t\t// Handle commands\n\t\tif (input === \"/help\") {\n\t\t\tthis.showHelp();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/skills\") {\n\t\t\tthis.showSkills();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/quit\" || input === \"/exit\") {\n\t\t\tthis.shutdown();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/model\") {\n\t\t\tawait this.handleModelSelect();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/login\") {\n\t\t\tawait this.handleLogin();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/logout\") {\n\t\t\tawait this.handleLogout();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/compact\" || input.startsWith(\"/compact \")) {\n\t\t\tconst customInstructions = input.startsWith(\"/compact \") ? input.slice(9).trim() : undefined;\n\t\t\tawait this.handleCompactCommand(customInstructions);\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/auto-compact\") {\n\t\t\tthis.toggleAutoCompaction();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/resume\") {\n\t\t\tawait this.handleResume();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input.startsWith(\"/skill:\")) {\n\t\t\tconst skillName = input.slice(\"/skill:\".length).trim();\n\t\t\tawait this.handleSkillInvocation(skillName);\n\t\t\treturn;\n\t\t}\n\n\t\t// Handle bash commands (! for normal, !! for excluded from context)\n\t\tconst bashParsed = parseBashInput(input);\n\t\tif (bashParsed) {\n\t\t\tif (this.isBashRunning) {\n\t\t\t\tthis.showStatus(chalk.yellow(\"A bash command is already running. Press Escape to cancel it first.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait this.handleBashCommand(bashParsed.command, bashParsed.excludeFromContext);\n\t\t\tthis.isBashMode = false;\n\t\t\tthis.updateEditorBorderColor();\n\t\t\treturn;\n\t\t}\n\n\t\t// Try expanding prompt templates\n\t\tconst { prompts = [] } = this.options;\n\t\tconst expanded = expandPromptTemplate(input, prompts);\n\n\t\t// Capture and clear pending images\n\t\tconst images = this.pendingImages.length > 0 ? [...this.pendingImages] : undefined;\n\t\tthis.pendingImages = [];\n\n\t\t// Regular message (use expanded text if a prompt template was matched)\n\t\tconst imageLabel = images ? chalk.dim(` (${images.length} image${images.length > 1 ? \"s\" : \"\"})`) : \"\";\n\t\tthis.chatContainer.addChild(new UserMessageComponent(`${expanded}${imageLabel}`, getMarkdownTheme()));\n\t\tthis.ui.requestRender();\n\t\tawait this.streamPrompt(expanded, images);\n\t}\n\n\tprivate async handleBashCommand(command: string, excludeFromContext: boolean): Promise<void> {\n\t\tconst { sessionManager } = this.options;\n\n\t\tthis.bashAbortController = new AbortController();\n\t\tthis.isBashRunning = true;\n\n\t\tthis.bashComponent = new BashExecutionComponent(command, this.ui, excludeFromContext);\n\t\tif (this.toolOutputExpanded) {\n\t\t\tthis.bashComponent.setExpanded(true);\n\t\t}\n\t\tthis.chatContainer.addChild(this.bashComponent);\n\t\tthis.ui.requestRender();\n\n\t\ttry {\n\t\t\tconst result = await executeBashCommand(command, {\n\t\t\t\tsignal: this.bashAbortController.signal,\n\t\t\t\tonChunk: (chunk) => {\n\t\t\t\t\tthis.bashComponent?.appendOutput(chunk);\n\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tthis.bashComponent.setComplete(result.exitCode, result.cancelled, result.truncated, result.fullOutputPath);\n\t\t\tthis.ui.requestRender();\n\n\t\t\tif (!excludeFromContext) {\n\t\t\t\tconst msgText = `Ran \\`${command}\\`\\n\\n\\`\\`\\`\\n${result.output.trimEnd()}\\n\\`\\`\\``;\n\t\t\t\tconst userMsg: ModelMessage = { role: \"user\", content: [{ type: \"text\", text: msgText }] };\n\t\t\t\tthis.agent.setMessages([...this.agent.messages, userMsg]);\n\t\t\t\tsessionManager?.appendMessage(userMsg);\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.isBashRunning = false;\n\t\t\tthis.bashAbortController = null;\n\t\t\tthis.bashComponent = undefined;\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Autocomplete\n\t// ========================================================================\n\n\tprivate buildAutocompleteProvider(): CombinedAutocompleteProvider {\n\t\tconst { skills = [], prompts = [], fdPath } = this.options;\n\n\t\tconst commands: SlashCommand[] = [\n\t\t\t{ name: \"help\", description: \"Show available commands\" },\n\t\t\t{ name: \"resume\", description: \"Resume a previous session\" },\n\t\t\t{ name: \"compact\", description: \"Manually compact the session context\" },\n\t\t\t{ name: \"auto-compact\", description: \"Toggle automatic context compaction\" },\n\t\t\t{ name: \"login\", description: \"Login to an OAuth provider\" },\n\t\t\t{ name: \"logout\", description: \"Logout from an OAuth provider\" },\n\t\t\t{ name: \"skills\", description: \"List loaded skills\" },\n\t\t\t{ name: \"model\", description: \"Switch model (Ctrl+L)\" },\n\t\t\t{ name: \"quit\", description: \"Exit the CLI\" },\n\t\t\t{ name: \"exit\", description: \"Exit the CLI\" },\n\t\t];\n\n\t\tfor (const skill of skills) {\n\t\t\tcommands.push({\n\t\t\t\tname: `skill:${skill.name}`,\n\t\t\t\tdescription: skill.description,\n\t\t\t});\n\t\t}\n\n\t\t// Add prompt templates as slash commands\n\t\tfor (const prompt of prompts) {\n\t\t\tcommands.push({\n\t\t\t\tname: prompt.name,\n\t\t\t\tdescription: prompt.description,\n\t\t\t});\n\t\t}\n\n\t\treturn new CombinedAutocompleteProvider(commands, process.cwd(), fdPath ?? null);\n\t}\n\n\t// ========================================================================\n\t// Model Selection\n\t// ========================================================================\n\n\tprivate async handleModelSelect(): Promise<void> {\n\t\tconst latestModels = getLatestModels();\n\t\tconst modelOptions: { provider: string; modelId: string; label: string }[] = [];\n\t\tfor (const [provider, models] of Object.entries(latestModels)) {\n\t\t\tfor (const modelId of models) {\n\t\t\t\tmodelOptions.push({ provider, modelId, label: `${provider}/${modelId}` });\n\t\t\t}\n\t\t}\n\n\t\tconst items: SelectItem[] = modelOptions.map((m) => {\n\t\t\tconst current = m.provider === this.currentProvider && m.modelId === this.currentModelId;\n\t\t\treturn {\n\t\t\t\tvalue: `${m.provider}/${m.modelId}`,\n\t\t\t\tlabel: current ? `${m.label} (current)` : m.label,\n\t\t\t};\n\t\t});\n\n\t\tconst selected = await this.showSelectList(\"Switch model\", items);\n\t\tif (!selected) return;\n\n\t\tconst [newProvider, ...modelParts] = selected.split(\"/\");\n\t\tconst newModelId = modelParts.join(\"/\");\n\n\t\tif (newProvider === this.currentProvider && newModelId === this.currentModelId) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.showStatus(chalk.dim(`Switching to ${newProvider}/${newModelId}...`));\n\n\t\tif (!this.options.onModelChange) {\n\t\t\tthis.showStatus(chalk.yellow(\"Model switching is not available.\"));\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconst newAgent = await this.options.onModelChange(newProvider, newModelId);\n\t\t\t// Preserve conversation history\n\t\t\tnewAgent.setMessages([...this.agent.messages]);\n\t\t\tthis.agent = newAgent;\n\n\t\t\tthis.currentProvider = newProvider;\n\t\t\tthis.currentModelId = newModelId;\n\t\t\tthis.updateFooter();\n\n\t\t\t// Persist the choice for next startup\n\t\t\tthis.options.settingsManager?.setDefaults(newProvider, newModelId);\n\n\t\t\tthis.showStatus(chalk.green(`Switched to ${newProvider}/${newModelId}`));\n\t\t} catch (error) {\n\t\t\tconst msg = error instanceof Error ? error.message : String(error);\n\t\t\tthis.showStatus(chalk.red(`Failed to switch model: ${msg}`));\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Streaming\n\t// ========================================================================\n\n\tprivate async streamPrompt(prompt: string, images?: ClipboardImage[]): Promise<void> {\n\t\tthis.isStreaming = true;\n\t\tthis.updatePendingMessagesDisplay();\n\t\tconst { sessionManager } = this.options;\n\t\tconst messagesBefore = this.agent.messages.length;\n\n\t\t// Build image parts from clipboard images\n\t\tconst imageParts: ImagePart[] = (images ?? []).map((img) => ({\n\t\t\ttype: \"image\" as const,\n\t\t\timage: Buffer.from(img.bytes).toString(\"base64\"),\n\t\t\tmediaType: img.mimeType,\n\t\t}));\n\n\t\t// Start loading animation\n\t\tthis.startLoading();\n\n\t\tthis.streamingComponent = undefined;\n\t\tthis.streamingText = \"\";\n\t\tthis.hadToolResults = false;\n\n\t\tlet errorDisplayed = false;\n\t\tlet streamFailed = false;\n\t\ttry {\n\t\t\tconst result =\n\t\t\t\timageParts.length > 0\n\t\t\t\t\t? await this.agent.stream({\n\t\t\t\t\t\t\tmessages: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\trole: \"user\" as const,\n\t\t\t\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: prompt }, ...imageParts],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t})\n\t\t\t\t\t: await this.agent.stream({ prompt });\n\n\t\t\tfor await (const part of result.fullStream) {\n\t\t\t\tswitch (part.type) {\n\t\t\t\t\tcase \"text-delta\":\n\t\t\t\t\t\t// After tool results, or for the very first text part, start a new assistant message component\n\t\t\t\t\t\t// so each agent step gets its own message bubble\n\t\t\t\t\t\tif (this.hadToolResults || !this.streamingComponent) {\n\t\t\t\t\t\t\tthis.streamingComponent = new AssistantMessageComponent(getMarkdownTheme());\n\t\t\t\t\t\t\tthis.streamingText = \"\";\n\t\t\t\t\t\t\tthis.hadToolResults = false;\n\t\t\t\t\t\t\tthis.chatContainer.addChild(this.streamingComponent);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis.streamingText += part.text;\n\t\t\t\t\t\tthis.streamingComponent!.updateText(this.streamingText);\n\t\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase \"tool-call\": {\n\t\t\t\t\t\tconst args =\n\t\t\t\t\t\t\ttypeof part.input === \"object\" && part.input !== null\n\t\t\t\t\t\t\t\t? (part.input as Record<string, unknown>)\n\t\t\t\t\t\t\t\t: {};\n\t\t\t\t\t\tconst toolComponent = new ToolExecutionComponent(part.toolName, args);\n\n\t\t\t\t\t\tif (this.toolOutputExpanded) {\n\t\t\t\t\t\t\ttoolComponent.setExpanded(true);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthis.pendingTools.set(part.toolCallId, toolComponent);\n\t\t\t\t\t\tthis.chatContainer.addChild(toolComponent);\n\t\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase \"tool-result\": {\n\t\t\t\t\t\tconst toolComponent = this.pendingTools.get(part.toolCallId);\n\t\t\t\t\t\tif (toolComponent) {\n\t\t\t\t\t\t\tconst toolOutput = extractToolOutput(part.output);\n\t\t\t\t\t\t\ttoolComponent.updateResult(toolOutput, /* isError */ false, /* isPartial */ false);\n\t\t\t\t\t\t\tthis.pendingTools.delete(part.toolCallId);\n\t\t\t\t\t\t\tthis.hadToolResults = true;\n\t\t\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase \"error\": {\n\t\t\t\t\t\tconst errorMessage =\n\t\t\t\t\t\t\t(part.error as any)?.message ??\n\t\t\t\t\t\t\t(typeof part.error === \"object\" && part.error !== null\n\t\t\t\t\t\t\t\t? JSON.stringify(part.error)\n\t\t\t\t\t\t\t\t: String(part.error));\n\t\t\t\t\t\tif (this.streamingComponent) {\n\t\t\t\t\t\t\tthis.streamingComponent.setError(errorMessage);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.showStatus(chalk.red(`Error: ${errorMessage}`));\n\t\t\t\t\t\t}\n\t\t\t\t\t\terrorDisplayed = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (errorDisplayed) return;\n\n\t\t\t// Get final response and update messages\n\t\t\tconst response = await result.response;\n\t\t\tconst responseMessages = response.messages as ModelMessage[];\n\t\t\tthis.agent.setMessages([\n\t\t\t\t...this.agent.messages.slice(0, messagesBefore),\n\t\t\t\t...buildUserMessage(prompt, imageParts),\n\t\t\t\t...responseMessages,\n\t\t\t]);\n\n\t\t\t// Save to session\n\t\t\tif (sessionManager) {\n\t\t\t\tconst userMsg: ModelMessage = {\n\t\t\t\t\trole: \"user\",\n\t\t\t\t\tcontent: [{ type: \"text\", text: prompt }, ...imageParts],\n\t\t\t\t};\n\t\t\t\tsessionManager.appendMessage(userMsg);\n\t\t\t\tfor (const msg of responseMessages) {\n\t\t\t\t\tsessionManager.appendMessage(msg);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Update footer token stats\n\t\t\tthis.updateFooterTokens();\n\n\t\t\t// Check for auto-compaction after successful response\n\t\t\tawait this.checkAutoCompaction();\n\t\t} catch (error) {\n\t\t\tif (errorDisplayed) {\n\t\t\t\tstreamFailed = true;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tstreamFailed = true;\n\t\t\tif ((error as Error).name === \"AbortError\") {\n\t\t\t\tif (this.streamingComponent) {\n\t\t\t\t\tthis.streamingComponent.setAborted();\n\t\t\t\t} else {\n\t\t\t\t\tthis.showStatus(chalk.dim(\"[aborted]\"));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst msg =\n\t\t\t\t\terror instanceof Error\n\t\t\t\t\t\t? error.message\n\t\t\t\t\t\t: typeof error === \"object\" && error !== null\n\t\t\t\t\t\t\t? JSON.stringify(error)\n\t\t\t\t\t\t\t: String(error);\n\t\t\t\tif (this.streamingComponent) {\n\t\t\t\t\tthis.streamingComponent.setError(msg);\n\t\t\t\t} else {\n\t\t\t\t\tthis.showStatus(chalk.red(`Error: ${msg}`));\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.stopLoading();\n\t\t\tthis.streamingComponent = undefined;\n\t\t\tthis.streamingText = \"\";\n\t\t\tthis.hadToolResults = false;\n\t\t\tthis.pendingTools.clear();\n\t\t\tthis.isStreaming = false;\n\t\t\tthis.steeringMessages = [];\n\t\t\tif (streamFailed) {\n\t\t\t\tthis.followUpMessages = [];\n\t\t\t}\n\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\tthis.ui.requestRender();\n\t\t}\n\n\t\t// Process queued follow-ups (skipped if stream failed/aborted)\n\t\twhile (this.followUpMessages.length > 0) {\n\t\t\tconst next = this.followUpMessages.shift();\n\t\t\tif (!next) break;\n\n\t\t\tthis.updatePendingMessagesDisplay();\n\n\t\t\tthis.chatContainer.addChild(new UserMessageComponent(next, getMarkdownTheme()));\n\t\t\tthis.ui.requestRender();\n\t\t\tawait this.streamPrompt(next);\n\t\t}\n\t}\n\n\tprivate updatePendingMessagesDisplay(): void {\n\t\tthis.pendingMessagesContainer.clear();\n\n\t\t// If no agent is running, clear pending messages (they've been consumed)\n\t\tif (!this.isStreaming && this.followUpMessages.length === 0 && this.steeringMessages.length === 0) {\n\t\t\tthis.ui.requestRender();\n\t\t\treturn;\n\t\t}\n\n\t\tconst lines = formatPendingMessages(this.steeringMessages, this.followUpMessages);\n\n\t\tif (lines.length === 0) {\n\t\t\tthis.ui.requestRender();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.pendingMessagesContainer.addChild(new Spacer(1));\n\t\tthis.pendingMessagesContainer.addChild(new Text(lines.map((l) => chalk.dim(l)).join(\"\\n\"), 1, 0));\n\t\tthis.pendingMessagesContainer.addChild(new Text(chalk.dim(\"↳ Alt+Up to edit queued messages\"), 1, 0));\n\t\tthis.pendingMessagesContainer.addChild(new Spacer(1));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate clearAllQueues(): string[] {\n\t\tconst restored = [...this.steeringMessages, ...this.followUpMessages];\n\t\tthis.steeringMessages = [];\n\t\tthis.followUpMessages = [];\n\t\treturn restored;\n\t}\n\n\t// ========================================================================\n\t// Loading Animation\n\t// ========================================================================\n\n\tprivate startLoading(): void {\n\t\tthis.stopLoading();\n\t\tthis.loadingAnimation = new Loader(\n\t\t\tthis.ui,\n\t\t\t(s: string) => chalk.cyan(s),\n\t\t\t(s: string) => chalk.dim(s),\n\t\t\t\"Working...\",\n\t\t);\n\t\tthis.loadingAnimation.start();\n\t\tthis.pendingContainer.addChild(new Spacer(1));\n\t\tthis.pendingContainer.addChild(this.loadingAnimation);\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate stopLoading(): void {\n\t\tif (this.loadingAnimation) {\n\t\t\tthis.loadingAnimation.stop();\n\t\t\tthis.pendingContainer.clear();\n\t\t\tthis.loadingAnimation = undefined;\n\t\t\tthis.ui.requestRender();\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Tool Expansion\n\t// ========================================================================\n\n\tprivate toggleToolExpansion(): void {\n\t\tthis.toolOutputExpanded = !this.toolOutputExpanded;\n\n\t\t// Update all tool components and compaction components in the chat\n\t\tfor (const child of this.chatContainer.children) {\n\t\t\tif (child instanceof ToolExecutionComponent) {\n\t\t\t\tchild.setExpanded(this.toolOutputExpanded);\n\t\t\t} else if (child instanceof CompactionSummaryComponent) {\n\t\t\t\tchild.setExpanded(this.toolOutputExpanded);\n\t\t\t} else if (child instanceof BashExecutionComponent) {\n\t\t\t\tchild.setExpanded(this.toolOutputExpanded);\n\t\t\t}\n\t\t}\n\n\t\tthis.ui.requestRender();\n\t}\n\n\t// ========================================================================\n\t// Clipboard Image Paste\n\t// ========================================================================\n\n\tprivate handleClipboardImagePaste(): void {\n\t\ttry {\n\t\t\tconst image = readClipboardImage();\n\t\t\tif (!image) return;\n\t\t\tthis.pendingImages.push(image);\n\t\t\tconst ext = extensionForImageMimeType(image.mimeType) ?? \"image\";\n\t\t\tconst label = `[image ${this.pendingImages.length}: ${ext}]`;\n\t\t\tthis.editor.insertTextAtCursor(label);\n\t\t\tthis.ui.requestRender();\n\t\t} catch {\n\t\t\t// Silently ignore clipboard errors (may not have permission, etc.)\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Compaction\n\t// ========================================================================\n\n\t/**\n\t * Handle the /compact command.\n\t */\n\tprivate async handleCompactCommand(_customInstructions?: string): Promise<void> {\n\t\tconst messages = this.agent.messages;\n\t\tif (messages.length < 2) {\n\t\t\tthis.showStatus(chalk.yellow(\"Nothing to compact (not enough messages).\"));\n\t\t\treturn;\n\t\t}\n\n\t\tawait this.executeCompaction(false);\n\t}\n\n\t/**\n\t * Toggle auto-compaction on/off.\n\t */\n\tprivate toggleAutoCompaction(): void {\n\t\tthis.autoCompaction = !this.autoCompaction;\n\t\tthis.footer.setAutoCompaction(this.autoCompaction);\n\t\tthis.options.settingsManager?.setCompactionEnabled(this.autoCompaction);\n\t\tthis.showStatus(\n\t\t\tthis.autoCompaction ? chalk.green(\"Auto-compaction enabled\") : chalk.dim(\"Auto-compaction disabled\"),\n\t\t);\n\t\tthis.ui.requestRender();\n\t}\n\n\t/**\n\t * Check if auto-compaction should trigger after an agent response.\n\t */\n\tprivate async checkAutoCompaction(): Promise<void> {\n\t\tif (!this.autoCompaction) return;\n\n\t\tconst contextTokens = estimateContextTokens([...this.agent.messages]);\n\t\tif (!shouldCompact(contextTokens, this.contextWindow, this.compactionSettings)) return;\n\n\t\tawait this.executeCompaction(true);\n\t}\n\n\t/**\n\t * Execute compaction (used by both manual /compact and auto mode).\n\t */\n\tprivate async executeCompaction(isAuto: boolean): Promise<CompactionResult | undefined> {\n\t\tif (this.isCompacting) return undefined;\n\n\t\tconst { sessionManager } = this.options;\n\n\t\t// Build path entries from session if available, otherwise from agent messages\n\t\tconst pathEntries = sessionManager ? sessionManager.getBranch() : this.buildSessionEntriesFromMessages();\n\n\t\tif (pathEntries.length < 2) {\n\t\t\tif (!isAuto) {\n\t\t\t\tthis.showStatus(chalk.yellow(\"Nothing to compact (not enough messages).\"));\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\n\t\t// Prepare compaction\n\t\tconst preparation = prepareCompaction(pathEntries, this.compactionSettings);\n\t\tif (!preparation) {\n\t\t\tif (!isAuto) {\n\t\t\t\tthis.showStatus(chalk.yellow(\"Nothing to compact (already compacted or insufficient history).\"));\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (preparation.messagesToSummarize.length === 0) {\n\t\t\tif (!isAuto) {\n\t\t\t\tthis.showStatus(chalk.yellow(\"Nothing to compact (no messages to summarize).\"));\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\n\t\tthis.isCompacting = true;\n\t\tthis.compactionAbortController = new AbortController();\n\n\t\t// Show compaction indicator\n\t\tconst label = isAuto\n\t\t\t? \"Auto-compacting context... (Escape to cancel)\"\n\t\t\t: \"Compacting context... (Escape to cancel)\";\n\t\tconst compactingLoader = new Loader(\n\t\t\tthis.ui,\n\t\t\t(s: string) => chalk.cyan(s),\n\t\t\t(s: string) => chalk.dim(s),\n\t\t\tlabel,\n\t\t);\n\t\tcompactingLoader.start();\n\t\tthis.pendingContainer.clear();\n\t\tthis.pendingContainer.addChild(new Spacer(1));\n\t\tthis.pendingContainer.addChild(compactingLoader);\n\t\tthis.ui.requestRender();\n\n\t\tlet result: CompactionResult | undefined;\n\n\t\ttry {\n\t\t\t// We need a LanguageModel for summarization. Use the agent's model\n\t\t\t// by extracting it from the config. The model is accessible through\n\t\t\t// the onModelChange callback pattern, but for simplicity we create\n\t\t\t// a model via the same factory used at startup.\n\t\t\tconst { model } = await this.getCompactionModel();\n\n\t\t\tresult = await compact(preparation, model, this.compactionAbortController.signal);\n\n\t\t\t// Record compaction in session\n\t\t\tif (sessionManager) {\n\t\t\t\tsessionManager.appendCompaction(\n\t\t\t\t\tresult.summary,\n\t\t\t\t\tresult.firstKeptEntryId,\n\t\t\t\t\tresult.tokensBefore,\n\t\t\t\t\tresult.details,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Rebuild agent messages from the session context\n\t\t\tif (sessionManager) {\n\t\t\t\tconst context = sessionManager.buildSessionContext();\n\t\t\t\tthis.agent.setMessages(context.messages);\n\t\t\t}\n\n\t\t\t// Rebuild the chat UI\n\t\t\tthis.rebuildChatFromSession();\n\n\t\t\t// Add compaction summary component so user sees it\n\t\t\tconst summaryComponent = new CompactionSummaryComponent(result.tokensBefore, result.summary);\n\t\t\tif (this.toolOutputExpanded) {\n\t\t\t\tsummaryComponent.setExpanded(true);\n\t\t\t}\n\t\t\tthis.chatContainer.addChild(summaryComponent);\n\n\t\t\t// Update footer tokens\n\t\t\tthis.updateFooterTokens();\n\n\t\t\tif (this.options.verbose) {\n\t\t\t\tconst tokensAfter = estimateContextTokens([...this.agent.messages]);\n\t\t\t\tthis.showStatus(\n\t\t\t\t\tchalk.dim(\n\t\t\t\t\t\t`Compacted: ${result.tokensBefore.toLocaleString()} -> ${tokensAfter.toLocaleString()} tokens`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tif (\n\t\t\t\tthis.compactionAbortController.signal.aborted ||\n\t\t\t\tmessage === \"Compaction cancelled\" ||\n\t\t\t\t(error instanceof Error && error.name === \"AbortError\")\n\t\t\t) {\n\t\t\t\tthis.showStatus(chalk.dim(\"Compaction cancelled.\"));\n\t\t\t} else {\n\t\t\t\tthis.showStatus(chalk.red(`Compaction failed: ${message}`));\n\t\t\t}\n\t\t} finally {\n\t\t\tcompactingLoader.stop();\n\t\t\tthis.pendingContainer.clear();\n\t\t\tthis.isCompacting = false;\n\t\t\tthis.compactionAbortController = null;\n\t\t\tthis.ui.requestRender();\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Get the language model for compaction summarization.\n\t * Uses the same model creation path as the main agent.\n\t */\n\tprivate async getCompactionModel(): Promise<{ model: import(\"ai\").LanguageModel }> {\n\t\tconst { createModel } = await import(\"../../model-factory.js\");\n\t\treturn createModel({\n\t\t\tprovider: this.currentProvider,\n\t\t\tmodel: this.currentModelId,\n\t\t\tauthStorage: this.options.authStorage,\n\t\t});\n\t}\n\n\t/**\n\t * Build session entries from agent messages (when no session manager).\n\t * Creates synthetic SessionEntry objects for the compaction algorithm.\n\t */\n\tprivate buildSessionEntriesFromMessages(): import(\"edge-pi\").SessionEntry[] {\n\t\tconst messages = this.agent.messages;\n\t\tconst entries: import(\"edge-pi\").SessionEntry[] = [];\n\t\tlet parentId: string | null = null;\n\n\t\tfor (let i = 0; i < messages.length; i++) {\n\t\t\tconst id = `msg-${i}`;\n\t\t\tentries.push({\n\t\t\t\ttype: \"message\",\n\t\t\t\tid,\n\t\t\t\tparentId,\n\t\t\t\ttimestamp: new Date().toISOString(),\n\t\t\t\tmessage: messages[i],\n\t\t\t});\n\t\t\tparentId = id;\n\t\t}\n\n\t\treturn entries;\n\t}\n\n\t/**\n\t * Rebuild the chat UI from session context after compaction.\n\t */\n\tprivate rebuildChatFromSession(): void {\n\t\tthis.chatContainer.clear();\n\n\t\tconst messages = this.agent.messages;\n\t\tfor (const msg of messages) {\n\t\t\tif (msg.role === \"user\") {\n\t\t\t\t// Check if this is a compaction summary\n\t\t\t\tconst content = msg.content;\n\t\t\t\tif (Array.isArray(content) && content.length > 0) {\n\t\t\t\t\tconst textBlock = content[0] as { type: string; text?: string };\n\t\t\t\t\tif (textBlock.type === \"text\" && textBlock.text?.startsWith('<summary type=\"compaction\"')) {\n\t\t\t\t\t\t// Skip compaction summaries in rebuild (they are injected by buildSessionContext)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (textBlock.type === \"text\" && textBlock.text?.startsWith('<summary type=\"branch\"')) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst text = extractTextFromMessage(msg);\n\t\t\t\tif (text) {\n\t\t\t\t\tthis.chatContainer.addChild(new UserMessageComponent(text, getMarkdownTheme()));\n\t\t\t\t}\n\t\t\t} else if (msg.role === \"assistant\") {\n\t\t\t\tconst assistantMsg = msg as import(\"edge-pi\").AssistantModelMessage;\n\t\t\t\tconst textParts: string[] = [];\n\t\t\t\tfor (const block of assistantMsg.content) {\n\t\t\t\t\tconst b = block as {\n\t\t\t\t\t\ttype: string;\n\t\t\t\t\t\ttext?: string;\n\t\t\t\t\t\ttoolName?: string;\n\t\t\t\t\t\tinput?: unknown;\n\t\t\t\t\t\ttoolCallId?: string;\n\t\t\t\t\t};\n\t\t\t\t\tif (b.type === \"text\" && b.text) {\n\t\t\t\t\t\ttextParts.push(b.text);\n\t\t\t\t\t} else if (b.type === \"tool-call\" && b.toolName) {\n\t\t\t\t\t\tconst args =\n\t\t\t\t\t\t\ttypeof b.input === \"object\" && b.input !== null ? (b.input as Record<string, unknown>) : {};\n\t\t\t\t\t\tconst toolComp = new ToolExecutionComponent(b.toolName, args);\n\t\t\t\t\t\tif (this.toolOutputExpanded) {\n\t\t\t\t\t\t\ttoolComp.setExpanded(true);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Mark as completed (we don't have the result here, just show collapsed)\n\t\t\t\t\t\ttoolComp.updateResult({ text: \"(from history)\" }, false, false);\n\t\t\t\t\t\tthis.chatContainer.addChild(toolComp);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (textParts.length > 0) {\n\t\t\t\t\tconst comp = new AssistantMessageComponent(getMarkdownTheme());\n\t\t\t\t\tcomp.updateText(textParts.join(\"\"));\n\t\t\t\t\tthis.chatContainer.addChild(comp);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Skip tool messages in UI rebuild - they are consumed by tool-call components\n\t\t}\n\n\t\tthis.ui.requestRender();\n\t}\n\n\t// ========================================================================\n\t// Footer Token Tracking\n\t// ========================================================================\n\n\t/**\n\t * Update the footer with current token count information.\n\t */\n\tprivate updateFooterTokens(): void {\n\t\tconst contextTokens = estimateContextTokens([...this.agent.messages]);\n\t\tthis.footer.setTokenInfo(contextTokens, this.contextWindow);\n\t\tthis.footer.setAutoCompaction(this.autoCompaction);\n\t\tthis.ui?.requestRender();\n\t}\n\n\t/**\n\t * Check if the current provider is using an OAuth subscription credential.\n\t */\n\tprivate isSubscriptionProvider(): boolean {\n\t\tconst { authStorage } = this.options;\n\t\tif (!authStorage) return false;\n\t\tconst cred = authStorage.get(this.currentProvider);\n\t\treturn cred?.type === \"oauth\";\n\t}\n\n\t/**\n\t * Replace the footer component and update token info.\n\t */\n\tprivate updateFooter(): void {\n\t\tthis.footer = new FooterComponent(this.currentProvider, this.currentModelId);\n\t\tthis.footer.setSubscription(this.isSubscriptionProvider());\n\t\tthis.updateFooterTokens();\n\n\t\t// Replace footer in UI\n\t\tconst children = this.ui.children;\n\t\tchildren[children.length - 1] = this.footer;\n\t\tthis.ui.requestRender();\n\t}\n\n\t// ========================================================================\n\t// Startup Resource Display\n\t// ========================================================================\n\n\tprivate formatDisplayPath(p: string): string {\n\t\tconst home = process.env.HOME || process.env.USERPROFILE || \"\";\n\t\tif (home && p.startsWith(home)) {\n\t\t\treturn `~${p.slice(home.length)}`;\n\t\t}\n\t\treturn p;\n\t}\n\n\tprivate showLoadedResources(contextFiles: ContextFile[], skills: Skill[], prompts: PromptTemplate[]): void {\n\t\tconst sectionHeader = (name: string) => chalk.cyan(`[${name}]`);\n\n\t\tif (contextFiles.length > 0) {\n\t\t\tconst contextList = contextFiles.map((f) => chalk.dim(` ${this.formatDisplayPath(f.path)}`)).join(\"\\n\");\n\t\t\tthis.headerContainer.addChild(new Text(`${sectionHeader(\"Context\")}\\n${contextList}`, 0, 0));\n\t\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\t}\n\n\t\tif (skills.length > 0) {\n\t\t\tconst skillList = skills.map((s) => chalk.dim(` ${this.formatDisplayPath(s.filePath)}`)).join(\"\\n\");\n\t\t\tthis.headerContainer.addChild(new Text(`${sectionHeader(\"Skills\")}\\n${skillList}`, 0, 0));\n\t\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\t}\n\n\t\tif (prompts.length > 0) {\n\t\t\tconst promptList = prompts\n\t\t\t\t.map((p) => {\n\t\t\t\t\tconst sourceLabel = chalk.cyan(p.source);\n\t\t\t\t\treturn chalk.dim(` ${sourceLabel} /${p.name}`);\n\t\t\t\t})\n\t\t\t\t.join(\"\\n\");\n\t\t\tthis.headerContainer.addChild(new Text(`${sectionHeader(\"Prompts\")}\\n${promptList}`, 0, 0));\n\t\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Commands\n\t// ========================================================================\n\n\tprivate showHelp(): void {\n\t\tconst helpText = [\n\t\t\tchalk.bold(\"Commands:\"),\n\t\t\t\" !<command> Run inline bash and include output in context\",\n\t\t\t\" !!<command> Run inline bash but exclude output from context\",\n\t\t\t\" /resume Resume a previous session\",\n\t\t\t\" /compact [text] Compact the session context (optional instructions)\",\n\t\t\t\" /auto-compact Toggle automatic context compaction\",\n\t\t\t\" /model Switch model (Ctrl+L)\",\n\t\t\t\" /login Login to an OAuth provider\",\n\t\t\t\" /logout Logout from an OAuth provider\",\n\t\t\t\" /skills List loaded skills\",\n\t\t\t\" /skill:<name> Invoke a skill by name\",\n\t\t\t\" /quit, /exit Exit the CLI\",\n\t\t].join(\"\\n\");\n\n\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\tthis.chatContainer.addChild(new Text(helpText, 1, 0));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate showSkills(): void {\n\t\tconst { skills = [] } = this.options;\n\n\t\tif (skills.length === 0) {\n\t\t\tthis.showStatus(chalk.dim(\"No skills loaded.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst lines: string[] = [];\n\t\tfor (const skill of skills) {\n\t\t\tconst hidden = skill.disableModelInvocation ? chalk.dim(\" (hidden from model)\") : \"\";\n\t\t\tlines.push(` ${chalk.bold(skill.name)}${hidden}`);\n\t\t\tlines.push(chalk.dim(` ${skill.description}`));\n\t\t\tlines.push(chalk.dim(` ${skill.filePath}`));\n\t\t}\n\n\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\tthis.chatContainer.addChild(new Text(lines.join(\"\\n\"), 1, 0));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate async handleSkillInvocation(skillName: string): Promise<void> {\n\t\tconst { skills = [] } = this.options;\n\t\tconst skill = skills.find((s) => s.name === skillName);\n\n\t\tif (!skill) {\n\t\t\tthis.showStatus(chalk.red(`Skill \"${skillName}\" not found.`));\n\t\t\treturn;\n\t\t}\n\n\t\tconst skillPrompt = `Please read and follow the instructions in the skill file: ${skill.filePath}`;\n\t\tthis.chatContainer.addChild(new UserMessageComponent(skillPrompt, getMarkdownTheme()));\n\t\tthis.ui.requestRender();\n\t\tawait this.streamPrompt(skillPrompt);\n\t}\n\n\t// ========================================================================\n\t// OAuth Login/Logout\n\t// ========================================================================\n\n\tprivate async handleLogin(): Promise<void> {\n\t\tconst { authStorage } = this.options;\n\t\tif (!authStorage) {\n\t\t\tthis.showStatus(chalk.red(\"Auth storage not available.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst providers = authStorage.getProviders();\n\t\tif (providers.length === 0) {\n\t\t\tthis.showStatus(chalk.dim(\"No OAuth providers registered.\"));\n\t\t\treturn;\n\t\t}\n\n\t\t// Use SelectList overlay for provider selection\n\t\tconst items: SelectItem[] = providers.map((p) => {\n\t\t\tconst loggedIn = authStorage.get(p.id)?.type === \"oauth\" ? \" (logged in)\" : \"\";\n\t\t\treturn { value: p.id, label: `${p.name}${loggedIn}` };\n\t\t});\n\n\t\tconst selected = await this.showSelectList(\"Login to OAuth provider\", items);\n\t\tif (!selected) return;\n\n\t\tconst provider = providers.find((p) => p.id === selected);\n\t\tif (!provider) return;\n\n\t\tthis.showStatus(chalk.dim(`Logging in to ${provider.name}...`));\n\n\t\ttry {\n\t\t\tawait authStorage.login(provider.id, {\n\t\t\t\tonAuth: (info) => {\n\t\t\t\t\tconst lines = [chalk.bold(\"Open this URL in your browser:\"), chalk.cyan(info.url)];\n\t\t\t\t\tif (info.instructions) {\n\t\t\t\t\t\tlines.push(chalk.dim(info.instructions));\n\t\t\t\t\t}\n\t\t\t\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\t\t\t\tthis.chatContainer.addChild(new Text(lines.join(\"\\n\"), 1, 0));\n\t\t\t\t\tthis.ui.requestRender();\n\n\t\t\t\t\t// Try to open browser\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst { execSync } = require(\"node:child_process\") as typeof import(\"node:child_process\");\n\t\t\t\t\t\tconst platform = process.platform;\n\t\t\t\t\t\tif (platform === \"darwin\") {\n\t\t\t\t\t\t\texecSync(`open \"${info.url}\"`, { stdio: \"ignore\" });\n\t\t\t\t\t\t} else if (platform === \"linux\") {\n\t\t\t\t\t\t\texecSync(`xdg-open \"${info.url}\" 2>/dev/null || sensible-browser \"${info.url}\" 2>/dev/null`, {\n\t\t\t\t\t\t\t\tstdio: \"ignore\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t} else if (platform === \"win32\") {\n\t\t\t\t\t\t\texecSync(`start \"\" \"${info.url}\"`, { stdio: \"ignore\" });\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Silently fail - user can open manually\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tonPrompt: async (promptInfo) => {\n\t\t\t\t\t// Show prompt message and wait for user input\n\t\t\t\t\tthis.showStatus(chalk.dim(promptInfo.message));\n\t\t\t\t\tconst answer = await this.getUserInput();\n\t\t\t\t\treturn answer.trim();\n\t\t\t\t},\n\t\t\t\tonProgress: (message) => {\n\t\t\t\t\tthis.showStatus(chalk.dim(message));\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tthis.footer.setSubscription(this.isSubscriptionProvider());\n\t\t\tthis.ui.requestRender();\n\t\t\tthis.showStatus(chalk.green(`Logged in to ${provider.name}. Credentials saved.`));\n\t\t} catch (error) {\n\t\t\tconst msg = error instanceof Error ? error.message : String(error);\n\t\t\tif (msg !== \"Login cancelled\") {\n\t\t\t\tthis.showStatus(chalk.red(`Login failed: ${msg}`));\n\t\t\t} else {\n\t\t\t\tthis.showStatus(chalk.dim(\"Login cancelled.\"));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async handleLogout(): Promise<void> {\n\t\tconst { authStorage } = this.options;\n\t\tif (!authStorage) {\n\t\t\tthis.showStatus(chalk.red(\"Auth storage not available.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst loggedIn = authStorage\n\t\t\t.list()\n\t\t\t.filter((id) => authStorage.get(id)?.type === \"oauth\")\n\t\t\t.map((id) => {\n\t\t\t\tconst provider = authStorage.getProvider(id);\n\t\t\t\treturn { id, name: provider?.name ?? id };\n\t\t\t});\n\n\t\tif (loggedIn.length === 0) {\n\t\t\tthis.showStatus(chalk.dim(\"No OAuth providers logged in. Use /login first.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst items: SelectItem[] = loggedIn.map((p) => ({\n\t\t\tvalue: p.id,\n\t\t\tlabel: p.name,\n\t\t}));\n\n\t\tconst selected = await this.showSelectList(\"Logout from OAuth provider\", items);\n\t\tif (!selected) return;\n\n\t\tconst entry = loggedIn.find((p) => p.id === selected);\n\t\tif (!entry) return;\n\n\t\tauthStorage.logout(entry.id);\n\t\tthis.showStatus(chalk.green(`Logged out of ${entry.name}.`));\n\t}\n\n\t// ========================================================================\n\t// Resume Session\n\t// ========================================================================\n\n\t/**\n\t * List session files from the session directory, sorted by modification time (newest first).\n\t * Returns metadata for each session including the first user message as a preview.\n\t */\n\tprivate listAvailableSessions(): { path: string; mtime: number; preview: string; timestamp: string }[] {\n\t\tconst { sessionDir } = this.options;\n\t\tif (!sessionDir || !existsSync(sessionDir)) return [];\n\n\t\ttry {\n\t\t\tconst files = readdirSync(sessionDir)\n\t\t\t\t.filter((f: string) => f.endsWith(\".jsonl\"))\n\t\t\t\t.map((f: string) => {\n\t\t\t\t\tconst filePath = join(sessionDir, f);\n\t\t\t\t\tconst mtime = statSync(filePath).mtime.getTime();\n\t\t\t\t\treturn { name: f, path: filePath, mtime };\n\t\t\t\t})\n\t\t\t\t.sort((a, b) => b.mtime - a.mtime);\n\n\t\t\tconst sessions: { path: string; mtime: number; preview: string; timestamp: string }[] = [];\n\t\t\tfor (const file of files) {\n\t\t\t\t// Skip the current session file\n\t\t\t\tif (this.options.sessionManager?.getSessionFile() === file.path) continue;\n\n\t\t\t\tconst preview = this.getSessionPreview(file.path);\n\t\t\t\tconst timestamp = new Date(file.mtime).toLocaleString();\n\t\t\t\tsessions.push({ path: file.path, mtime: file.mtime, preview, timestamp });\n\t\t\t}\n\t\t\treturn sessions;\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Extract the first user message from a session file for preview.\n\t */\n\tprivate getSessionPreview(filePath: string): string {\n\t\ttry {\n\t\t\tconst content = readFileSync(filePath, \"utf-8\");\n\t\t\tconst lines = content.trim().split(\"\\n\");\n\t\t\tfor (const line of lines) {\n\t\t\t\tif (!line.trim()) continue;\n\t\t\t\ttry {\n\t\t\t\t\tconst entry = JSON.parse(line);\n\t\t\t\t\tif (entry.type === \"message\" && entry.message?.role === \"user\") {\n\t\t\t\t\t\tconst msg = entry.message;\n\t\t\t\t\t\tlet text = \"\";\n\t\t\t\t\t\tif (typeof msg.content === \"string\") {\n\t\t\t\t\t\t\ttext = msg.content;\n\t\t\t\t\t\t} else if (Array.isArray(msg.content)) {\n\t\t\t\t\t\t\tfor (const block of msg.content) {\n\t\t\t\t\t\t\t\tif (block.type === \"text\" && block.text) {\n\t\t\t\t\t\t\t\t\ttext = block.text;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Truncate and clean up for display\n\t\t\t\t\t\ttext = text.replace(/\\n/g, \" \").trim();\n\t\t\t\t\t\tif (text.length > 80) {\n\t\t\t\t\t\t\ttext = `${text.slice(0, 77)}...`;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn text || \"(empty message)\";\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Skip malformed lines\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn \"(no messages)\";\n\t\t} catch {\n\t\t\treturn \"(unreadable)\";\n\t\t}\n\t}\n\n\t/**\n\t * Format a relative time string (e.g. \"2 hours ago\", \"3 days ago\").\n\t */\n\tprivate formatRelativeTime(mtime: number): string {\n\t\tconst now = Date.now();\n\t\tconst diffMs = now - mtime;\n\t\tconst diffSec = Math.floor(diffMs / 1000);\n\t\tconst diffMin = Math.floor(diffSec / 60);\n\t\tconst diffHour = Math.floor(diffMin / 60);\n\t\tconst diffDay = Math.floor(diffHour / 24);\n\n\t\tif (diffMin < 1) return \"just now\";\n\t\tif (diffMin < 60) return `${diffMin}m ago`;\n\t\tif (diffHour < 24) return `${diffHour}h ago`;\n\t\tif (diffDay < 30) return `${diffDay}d ago`;\n\t\treturn new Date(mtime).toLocaleDateString();\n\t}\n\n\t/**\n\t * Handle the /resume command: show a list of previous sessions and load the selected one.\n\t */\n\tprivate async handleResume(): Promise<void> {\n\t\tconst sessions = this.listAvailableSessions();\n\t\tif (sessions.length === 0) {\n\t\t\tthis.showStatus(chalk.yellow(\"No previous sessions found.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst items: SelectItem[] = sessions.map((s) => ({\n\t\t\tvalue: s.path,\n\t\t\tlabel: `${chalk.dim(this.formatRelativeTime(s.mtime))} ${s.preview}`,\n\t\t}));\n\n\t\tconst selected = await this.showSelectList(\"Resume session\", items);\n\t\tif (!selected) return;\n\n\t\tconst session = sessions.find((s) => s.path === selected);\n\t\tif (!session) return;\n\n\t\ttry {\n\t\t\t// Open the selected session\n\t\t\tconst sessionDir = this.options.sessionDir!;\n\t\t\tconst newSessionManager = SessionManagerClass.open(selected, sessionDir);\n\n\t\t\t// Rebuild agent messages from session context\n\t\t\tconst context = newSessionManager.buildSessionContext();\n\t\t\tthis.agent.setMessages(context.messages);\n\n\t\t\t// Update session manager reference\n\t\t\tthis.options.sessionManager = newSessionManager;\n\n\t\t\t// Rebuild the chat UI\n\t\t\tthis.chatContainer.clear();\n\t\t\tthis.rebuildChatFromSession();\n\n\t\t\t// Update footer tokens\n\t\t\tthis.updateFooterTokens();\n\n\t\t\tconst msgCount = context.messages.length;\n\t\t\tthis.showStatus(chalk.green(`Resumed session (${msgCount} messages)`));\n\t\t} catch (error) {\n\t\t\tconst msg = error instanceof Error ? error.message : String(error);\n\t\t\tthis.showStatus(chalk.red(`Failed to resume session: ${msg}`));\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Select List (overlay pattern from pi-coding-agent)\n\t// ========================================================================\n\n\tprivate showSelectList(title: string, items: SelectItem[]): Promise<string | null> {\n\t\treturn new Promise((resolve) => {\n\t\t\tconst container = new Container();\n\n\t\t\tcontainer.addChild(new Spacer(1));\n\t\t\tcontainer.addChild(new Text(chalk.bold.cyan(title), 1, 0));\n\n\t\t\tconst selectList = new SelectList(items, Math.min(items.length, 10), getSelectListTheme());\n\t\t\tselectList.onSelect = (item) => {\n\t\t\t\t// Restore normal UI\n\t\t\t\tthis.pendingContainer.clear();\n\t\t\t\tthis.editorContainer.addChild(this.editor);\n\t\t\t\tthis.ui.setFocus(this.editor);\n\t\t\t\tthis.ui.requestRender();\n\t\t\t\tresolve(item.value);\n\t\t\t};\n\t\t\tselectList.onCancel = () => {\n\t\t\t\tthis.pendingContainer.clear();\n\t\t\t\tthis.editorContainer.addChild(this.editor);\n\t\t\t\tthis.ui.setFocus(this.editor);\n\t\t\t\tthis.ui.requestRender();\n\t\t\t\tresolve(null);\n\t\t\t};\n\t\t\tcontainer.addChild(selectList);\n\t\t\tcontainer.addChild(new Text(chalk.dim(\"↑↓ navigate • enter select • esc cancel\"), 1, 0));\n\t\t\tcontainer.addChild(new Spacer(1));\n\n\t\t\t// Replace editor area with select list\n\t\t\tthis.editorContainer.clear();\n\t\t\tthis.pendingContainer.clear();\n\t\t\tthis.pendingContainer.addChild(container);\n\t\t\tthis.ui.setFocus(selectList);\n\t\t\tthis.ui.requestRender();\n\t\t});\n\t}\n\n\t// ========================================================================\n\t// Status & Utilities\n\t// ========================================================================\n\n\tprivate showStatus(text: string): void {\n\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\tthis.chatContainer.addChild(new Text(text, 1, 0));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate updateEditorBorderColor(): void {\n\t\tthis.editorTheme.borderColor = this.isBashMode ? (s: string) => chalk.yellow(s) : (s: string) => chalk.gray(s);\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate shutdown(): void {\n\t\tthis.ui.stop();\n\t\tconsole.log(chalk.dim(\"\\nGoodbye.\"));\n\t\tprocess.exit(0);\n\t}\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction buildUserMessage(text: string, imageParts?: ImagePart[]): ModelMessage[] {\n\tconst content: Array<{ type: \"text\"; text: string } | ImagePart> = [{ type: \"text\" as const, text }];\n\tif (imageParts && imageParts.length > 0) {\n\t\tcontent.push(...imageParts);\n\t}\n\treturn [{ role: \"user\" as const, content }];\n}\n\nfunction extractTextFromMessage(msg: ModelMessage): string {\n\tif (msg.role === \"user\") {\n\t\tconst content = (msg as import(\"edge-pi\").UserModelMessage).content;\n\t\tif (typeof content === \"string\") return content;\n\t\tif (Array.isArray(content)) {\n\t\t\treturn content\n\t\t\t\t.filter((c): c is { type: \"text\"; text: string } => (c as { type: string }).type === \"text\")\n\t\t\t\t.map((c) => c.text)\n\t\t\t\t.join(\"\");\n\t\t}\n\t}\n\treturn \"\";\n}\n"]}
@@ -271,7 +271,6 @@ class InteractiveMode {
271
271
  if (!text)
272
272
  return;
273
273
  if (this.isStreaming) {
274
- this.agent.followUp({ role: "user", content: [{ type: "text", text }] });
275
274
  this.followUpMessages.push(text);
276
275
  this.editor.setText("");
277
276
  this.updatePendingMessagesDisplay();
@@ -673,9 +672,6 @@ class InteractiveMode {
673
672
  const restored = [...this.steeringMessages, ...this.followUpMessages];
674
673
  this.steeringMessages = [];
675
674
  this.followUpMessages = [];
676
- // Force agent to drop its internal queue too if possible, but edge-pi agent doesn't expose that yet.
677
- // However, steering messages are consumed immediately by the agent loop so they might already be gone.
678
- // Follow-ups are managed here in the loop, so clearing this array stops them.
679
675
  return restored;
680
676
  }
681
677
  // ========================================================================
@@ -1 +1 @@
1
- {"version":3,"file":"interactive-mode.js","sourceRoot":"","sources":["../../../src/modes/interactive/interactive-mode.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Interactive mode using @mariozechner/pi-tui.\n *\n * Replaces the old readline-based REPL with a proper TUI that matches\n * the UX patterns from @mariozechner/pi-coding-agent:\n * - Editor component for input with submit/escape handling\n * - Markdown rendering for assistant responses\n * - Tool execution components with collapsible output\n * - Footer with model/provider info and token stats\n * - Container-based layout (header → chat → pending → editor → footer)\n * - Context compaction (manual /compact + auto mode)\n */\n\nimport { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n\tCombinedAutocompleteProvider,\n\tContainer,\n\tEditor,\n\tKey,\n\tLoader,\n\tmatchesKey,\n\tProcessTerminal,\n\ttype SelectItem,\n\tSelectList,\n\ttype SlashCommand,\n\tSpacer,\n\tText,\n\tTUI,\n} from \"@mariozechner/pi-tui\";\nimport type { ImagePart } from \"ai\";\nimport chalk from \"chalk\";\nimport type { CodingAgent, CodingAgentConfig, ModelMessage, SessionManager } from \"edge-pi\";\nimport {\n\ttype CompactionResult,\n\ttype CompactionSettings,\n\tcompact,\n\tDEFAULT_COMPACTION_SETTINGS,\n\testimateContextTokens,\n\tprepareCompaction,\n\tSessionManager as SessionManagerClass,\n\tshouldCompact,\n} from \"edge-pi\";\nimport type { AuthStorage } from \"../../auth/auth-storage.js\";\nimport type { ContextFile } from \"../../context.js\";\nimport { getLatestModels } from \"../../model-factory.js\";\nimport type { PromptTemplate } from \"../../prompts.js\";\nimport { expandPromptTemplate } from \"../../prompts.js\";\nimport type { SettingsManager } from \"../../settings.js\";\nimport type { Skill } from \"../../skills.js\";\nimport { executeBashCommand } from \"../../utils/bash-executor.js\";\nimport { type ClipboardImage, extensionForImageMimeType, readClipboardImage } from \"../../utils/clipboard-image.js\";\nimport { formatPendingMessages, parseBashInput } from \"./bash-helpers.js\";\nimport { AssistantMessageComponent } from \"./components/assistant-message.js\";\nimport { BashExecutionComponent } from \"./components/bash-execution.js\";\nimport { CompactionSummaryComponent } from \"./components/compaction-summary.js\";\nimport { FooterComponent } from \"./components/footer.js\";\nimport { ToolExecutionComponent, type ToolOutput } from \"./components/tool-execution.js\";\nimport { UserMessageComponent } from \"./components/user-message.js\";\nimport { getEditorTheme, getMarkdownTheme, getSelectListTheme } from \"./theme.js\";\n\n/** Default context window size (used when model doesn't report one). */\nconst DEFAULT_CONTEXT_WINDOW = 200_000;\n\n/** Extract display-friendly output from a tool result. Handles both plain strings and structured objects with text/image fields. */\nfunction extractToolOutput(output: unknown): ToolOutput {\n\tif (typeof output === \"string\") {\n\t\treturn { text: output };\n\t}\n\tif (typeof output === \"object\" && output !== null && \"text\" in output && typeof (output as any).text === \"string\") {\n\t\tconst obj = output as any;\n\t\treturn {\n\t\t\ttext: obj.text,\n\t\t\t...(obj.image && { image: obj.image }),\n\t\t};\n\t}\n\treturn { text: JSON.stringify(output) };\n}\n\nexport interface InteractiveModeOptions {\n\tinitialMessage?: string;\n\tinitialMessages?: string[];\n\tsessionManager?: SessionManager;\n\tskills?: Skill[];\n\tcontextFiles?: ContextFile[];\n\tprompts?: PromptTemplate[];\n\tverbose?: boolean;\n\tprovider: string;\n\tmodelId: string;\n\tauthStorage?: AuthStorage;\n\t/** Settings manager for persisting user preferences (provider, model, compaction). */\n\tsettingsManager?: SettingsManager;\n\t/** Path to the `fd` binary for @ file autocomplete, or undefined if unavailable. */\n\tfdPath?: string;\n\t/** Called when the user switches model via Ctrl+L. Returns a new agent for the new model. */\n\tonModelChange?: (provider: string, modelId: string) => Promise<CodingAgent>;\n\t/** Context window size for the model. Defaults to 200k. */\n\tcontextWindow?: number;\n\t/** Directory where session files are stored. Required for /resume. */\n\tsessionDir?: string;\n\t/** Agent config used to recreate agents when resuming sessions. */\n\tagentConfig?: CodingAgentConfig;\n\t/** When true, show the session picker immediately on startup. */\n\tresumeOnStart?: boolean;\n}\n\n/**\n * Run the interactive TUI mode with streaming output.\n */\nexport async function runInteractiveMode(agent: CodingAgent, options: InteractiveModeOptions): Promise<void> {\n\tconst mode = new InteractiveMode(agent, options);\n\tawait mode.run();\n}\n\n// ============================================================================\n// InteractiveMode class\n// ============================================================================\n\nclass InteractiveMode {\n\tprivate agent: CodingAgent;\n\tprivate options: InteractiveModeOptions;\n\tprivate currentProvider: string;\n\tprivate currentModelId: string;\n\n\tprivate ui!: TUI;\n\tprivate headerContainer!: Container;\n\tprivate chatContainer!: Container;\n\tprivate pendingContainer!: Container;\n\tprivate pendingMessagesContainer!: Container;\n\tprivate editorContainer!: Container;\n\tprivate editor!: Editor;\n\tprivate editorTheme!: import(\"@mariozechner/pi-tui\").EditorTheme;\n\n\t// Message queues\n\tprivate steeringMessages: string[] = [];\n\tprivate followUpMessages: string[] = [];\n\tprivate isStreaming = false;\n\n\t// Inline bash state\n\tprivate isBashMode = false;\n\tprivate isBashRunning = false;\n\tprivate bashAbortController: AbortController | null = null;\n\tprivate bashComponent: import(\"./components/bash-execution.js\").BashExecutionComponent | undefined = undefined;\n\tprivate footer!: FooterComponent;\n\n\t// Loading animation during agent processing\n\tprivate loadingAnimation: Loader | undefined = undefined;\n\n\t// Streaming state\n\tprivate streamingComponent: AssistantMessageComponent | undefined = undefined;\n\tprivate streamingText = \"\";\n\tprivate hadToolResults = false;\n\n\t// Tool execution tracking: toolCallId → component\n\tprivate pendingTools = new Map<string, ToolExecutionComponent>();\n\n\t// Tool output expansion state\n\tprivate toolOutputExpanded = false;\n\n\t// Callback for resolving user input promise\n\tprivate onInputCallback?: (text: string) => void;\n\n\t// Pending clipboard images to attach to the next message\n\tprivate pendingImages: ClipboardImage[] = [];\n\n\t// Compaction state\n\tprivate contextWindow: number;\n\tprivate compactionSettings: CompactionSettings;\n\tprivate autoCompaction = true;\n\tprivate isCompacting = false;\n\tprivate compactionAbortController: AbortController | null = null;\n\n\tconstructor(agent: CodingAgent, options: InteractiveModeOptions) {\n\t\tthis.agent = agent;\n\t\tthis.options = options;\n\t\tthis.currentProvider = options.provider;\n\t\tthis.currentModelId = options.modelId;\n\t\tthis.contextWindow = options.contextWindow ?? DEFAULT_CONTEXT_WINDOW;\n\n\t\t// Initialize compaction settings from persisted settings if available\n\t\tconst savedCompaction = options.settingsManager?.getCompaction();\n\t\tthis.compactionSettings = {\n\t\t\t...DEFAULT_COMPACTION_SETTINGS,\n\t\t\t...(savedCompaction?.reserveTokens !== undefined && { reserveTokens: savedCompaction.reserveTokens }),\n\t\t\t...(savedCompaction?.keepRecentTokens !== undefined && { keepRecentTokens: savedCompaction.keepRecentTokens }),\n\t\t};\n\t\tthis.autoCompaction = options.settingsManager?.getCompactionEnabled() ?? true;\n\t}\n\n\tasync run(): Promise<void> {\n\t\tthis.initUI();\n\t\tthis.updateFooterTokens();\n\n\t\t// Show session picker immediately if --resume was passed\n\t\tif (this.options.resumeOnStart) {\n\t\t\tawait this.handleResume();\n\t\t}\n\n\t\t// Process initial messages\n\t\tconst { initialMessage, initialMessages = [] } = this.options;\n\n\t\tconst allInitial: string[] = [];\n\t\tif (initialMessage) allInitial.push(initialMessage);\n\t\tallInitial.push(...initialMessages);\n\n\t\tfor (const msg of allInitial) {\n\t\t\tthis.chatContainer.addChild(new UserMessageComponent(msg, getMarkdownTheme()));\n\t\t\tthis.ui.requestRender();\n\t\t\tawait this.streamPrompt(msg);\n\t\t}\n\n\t\t// Main interactive loop\n\t\twhile (true) {\n\t\t\tconst userInput = await this.getUserInput();\n\t\t\tawait this.handleUserInput(userInput);\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// UI Setup\n\t// ========================================================================\n\n\tprivate initUI(): void {\n\t\tconst { provider, modelId, skills = [], contextFiles = [], prompts = [], verbose, sessionManager } = this.options;\n\n\t\tthis.ui = new TUI(new ProcessTerminal());\n\n\t\t// Header\n\t\tthis.headerContainer = new Container();\n\t\tconst logo = chalk.bold(\"epi\") + chalk.dim(` - ${provider}/${modelId}`);\n\n\t\tconst hints = [\n\t\t\t`${chalk.dim(\"Escape\")} to abort`,\n\t\t\t`${chalk.dim(\"!\")} inline bash`,\n\t\t\t`${chalk.dim(\"Alt+Enter\")} follow-up while streaming`,\n\t\t\t`${chalk.dim(\"Ctrl+C\")} to exit`,\n\t\t\t`${chalk.dim(\"Ctrl+E\")} to expand tools`,\n\t\t\t`${chalk.dim(\"Ctrl+L\")} to switch model`,\n\t\t\t`${chalk.dim(\"Ctrl+V\")} to paste image`,\n\t\t\t`${chalk.dim(\"↑/↓\")} to browse history`,\n\t\t\t`${chalk.dim(\"@\")} for file references`,\n\t\t\t`${chalk.dim(\"/\")} for commands`,\n\t\t].join(\"\\n\");\n\n\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\tthis.headerContainer.addChild(new Text(`${logo}\\n${hints}`, 1, 0));\n\t\tthis.headerContainer.addChild(new Spacer(1));\n\n\t\tif (verbose && sessionManager?.getSessionFile()) {\n\t\t\tthis.headerContainer.addChild(new Text(chalk.dim(`Session: ${sessionManager.getSessionFile()}`), 1, 0));\n\t\t}\n\n\t\t// Show loaded context, skills, and prompts at startup\n\t\tthis.showLoadedResources(contextFiles, skills, prompts);\n\n\t\t// Chat area\n\t\tthis.chatContainer = new Container();\n\n\t\t// Pending messages (loading animations, status)\n\t\tthis.pendingContainer = new Container();\n\n\t\t// Pending steering/follow-up messages\n\t\tthis.pendingMessagesContainer = new Container();\n\n\t\t// Editor with slash command autocomplete\n\t\tthis.editorTheme = getEditorTheme();\n\t\tthis.editor = new Editor(this.ui, this.editorTheme);\n\t\tthis.editor.setAutocompleteProvider(this.buildAutocompleteProvider());\n\t\tthis.editorContainer = new Container();\n\t\tthis.editorContainer.addChild(this.editor);\n\n\t\t// Footer\n\t\tthis.footer = new FooterComponent(this.currentProvider, this.currentModelId);\n\t\tthis.footer.setAutoCompaction(this.autoCompaction);\n\t\tthis.footer.setSubscription(this.isSubscriptionProvider());\n\n\t\t// Assemble layout\n\t\tthis.ui.addChild(this.headerContainer);\n\t\tthis.ui.addChild(this.chatContainer);\n\t\tthis.ui.addChild(this.pendingMessagesContainer);\n\t\tthis.ui.addChild(this.pendingContainer);\n\t\tthis.ui.addChild(this.editorContainer);\n\t\tthis.ui.addChild(this.footer);\n\n\t\tthis.ui.setFocus(this.editor);\n\t\tthis.setupKeyHandlers();\n\n\t\tthis.ui.start();\n\t}\n\n\t// ========================================================================\n\t// Key Handlers\n\t// ========================================================================\n\n\tprivate setupKeyHandlers(): void {\n\t\tthis.editor.onChange = (text: string) => {\n\t\t\tconst wasBashMode = this.isBashMode;\n\t\t\tthis.isBashMode = text.trimStart().startsWith(\"!\");\n\t\t\tif (wasBashMode !== this.isBashMode) {\n\t\t\t\tthis.updateEditorBorderColor();\n\t\t\t}\n\t\t};\n\t\tthis.editor.onSubmit = (text: string) => {\n\t\t\ttext = text.trim();\n\t\t\tif (!text) return;\n\n\t\t\t// If agent is streaming, Enter becomes a steering message\n\t\t\tif (this.isStreaming) {\n\t\t\t\tthis.agent.steer({ role: \"user\", content: [{ type: \"text\", text }] });\n\t\t\t\tthis.steeringMessages.push(text);\n\t\t\t\tthis.editor.setText(\"\");\n\t\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.editor.addToHistory(text);\n\t\t\tthis.editor.setText(\"\");\n\n\t\t\tif (this.onInputCallback) {\n\t\t\t\tthis.onInputCallback(text);\n\t\t\t}\n\t\t};\n\n\t\tconst origHandleInput = this.editor.handleInput.bind(this.editor);\n\t\tthis.editor.handleInput = (data: string) => {\n\t\t\t// Escape: abort if agent is running or compacting\n\t\t\tif (matchesKey(data, Key.escape)) {\n\t\t\t\tif (this.isBashRunning && this.bashAbortController) {\n\t\t\t\t\tthis.bashAbortController.abort();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (this.isCompacting && this.compactionAbortController) {\n\t\t\t\t\tthis.compactionAbortController.abort();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (this.loadingAnimation) {\n\t\t\t\t\tthis.agent.abort();\n\t\t\t\t\tthis.stopLoading();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Ctrl+C: exit\n\t\t\tif (matchesKey(data, Key.ctrl(\"c\"))) {\n\t\t\t\tthis.shutdown();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Ctrl+D: exit if editor is empty\n\t\t\tif (matchesKey(data, Key.ctrl(\"d\"))) {\n\t\t\t\tif (this.editor.getText().length === 0) {\n\t\t\t\t\tthis.shutdown();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Ctrl+E: toggle tool output expansion\n\t\t\tif (matchesKey(data, Key.ctrl(\"e\"))) {\n\t\t\t\tthis.toggleToolExpansion();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Ctrl+L: select model\n\t\t\tif (matchesKey(data, Key.ctrl(\"l\"))) {\n\t\t\t\tthis.handleModelSelect();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Ctrl+V: paste image from clipboard\n\t\t\tif (matchesKey(data, Key.ctrl(\"v\"))) {\n\t\t\t\tthis.handleClipboardImagePaste();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Alt+Enter (Option+Enter on Mac): follow-up while streaming (or submit normally when idle)\n\t\t\tif (matchesKey(data, Key.alt(\"enter\"))) {\n\t\t\t\tconst text = this.editor.getText().trim();\n\t\t\t\tif (!text) return;\n\n\t\t\t\tif (this.isStreaming) {\n\t\t\t\t\tthis.agent.followUp({ role: \"user\", content: [{ type: \"text\", text }] });\n\t\t\t\t\tthis.followUpMessages.push(text);\n\t\t\t\t\tthis.editor.setText(\"\");\n\t\t\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Not streaming: treat like regular submit\n\t\t\t\tthis.editor.onSubmit?.(text);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Alt+Up (Option+Up on Mac): dequeue all queued messages back into the editor\n\t\t\tif (matchesKey(data, Key.alt(\"up\"))) {\n\t\t\t\tconst restored = this.clearAllQueues();\n\t\t\t\tif (restored.length > 0) {\n\t\t\t\t\tthis.editor.setText(restored.join(\"\\n\\n\"));\n\t\t\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\torigHandleInput(data);\n\t\t};\n\t}\n\n\t// ========================================================================\n\t// User Input\n\t// ========================================================================\n\n\tprivate getUserInput(): Promise<string> {\n\t\treturn new Promise((resolve) => {\n\t\t\tthis.onInputCallback = (text: string) => {\n\t\t\t\tthis.onInputCallback = undefined;\n\t\t\t\tresolve(text);\n\t\t\t};\n\t\t});\n\t}\n\n\tprivate async handleUserInput(input: string): Promise<void> {\n\t\t// Handle commands\n\t\tif (input === \"/help\") {\n\t\t\tthis.showHelp();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/skills\") {\n\t\t\tthis.showSkills();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/quit\" || input === \"/exit\") {\n\t\t\tthis.shutdown();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/model\") {\n\t\t\tawait this.handleModelSelect();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/login\") {\n\t\t\tawait this.handleLogin();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/logout\") {\n\t\t\tawait this.handleLogout();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/compact\" || input.startsWith(\"/compact \")) {\n\t\t\tconst customInstructions = input.startsWith(\"/compact \") ? input.slice(9).trim() : undefined;\n\t\t\tawait this.handleCompactCommand(customInstructions);\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/auto-compact\") {\n\t\t\tthis.toggleAutoCompaction();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/resume\") {\n\t\t\tawait this.handleResume();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input.startsWith(\"/skill:\")) {\n\t\t\tconst skillName = input.slice(\"/skill:\".length).trim();\n\t\t\tawait this.handleSkillInvocation(skillName);\n\t\t\treturn;\n\t\t}\n\n\t\t// Handle bash commands (! for normal, !! for excluded from context)\n\t\tconst bashParsed = parseBashInput(input);\n\t\tif (bashParsed) {\n\t\t\tif (this.isBashRunning) {\n\t\t\t\tthis.showStatus(chalk.yellow(\"A bash command is already running. Press Escape to cancel it first.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait this.handleBashCommand(bashParsed.command, bashParsed.excludeFromContext);\n\t\t\tthis.isBashMode = false;\n\t\t\tthis.updateEditorBorderColor();\n\t\t\treturn;\n\t\t}\n\n\t\t// Try expanding prompt templates\n\t\tconst { prompts = [] } = this.options;\n\t\tconst expanded = expandPromptTemplate(input, prompts);\n\n\t\t// Capture and clear pending images\n\t\tconst images = this.pendingImages.length > 0 ? [...this.pendingImages] : undefined;\n\t\tthis.pendingImages = [];\n\n\t\t// Regular message (use expanded text if a prompt template was matched)\n\t\tconst imageLabel = images ? chalk.dim(` (${images.length} image${images.length > 1 ? \"s\" : \"\"})`) : \"\";\n\t\tthis.chatContainer.addChild(new UserMessageComponent(`${expanded}${imageLabel}`, getMarkdownTheme()));\n\t\tthis.ui.requestRender();\n\t\tawait this.streamPrompt(expanded, images);\n\t}\n\n\tprivate async handleBashCommand(command: string, excludeFromContext: boolean): Promise<void> {\n\t\tconst { sessionManager } = this.options;\n\n\t\tthis.bashAbortController = new AbortController();\n\t\tthis.isBashRunning = true;\n\n\t\tthis.bashComponent = new BashExecutionComponent(command, this.ui, excludeFromContext);\n\t\tif (this.toolOutputExpanded) {\n\t\t\tthis.bashComponent.setExpanded(true);\n\t\t}\n\t\tthis.chatContainer.addChild(this.bashComponent);\n\t\tthis.ui.requestRender();\n\n\t\ttry {\n\t\t\tconst result = await executeBashCommand(command, {\n\t\t\t\tsignal: this.bashAbortController.signal,\n\t\t\t\tonChunk: (chunk) => {\n\t\t\t\t\tthis.bashComponent?.appendOutput(chunk);\n\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tthis.bashComponent.setComplete(result.exitCode, result.cancelled, result.truncated, result.fullOutputPath);\n\t\t\tthis.ui.requestRender();\n\n\t\t\tif (!excludeFromContext) {\n\t\t\t\tconst msgText = `Ran \\`${command}\\`\\n\\n\\`\\`\\`\\n${result.output.trimEnd()}\\n\\`\\`\\``;\n\t\t\t\tconst userMsg: ModelMessage = { role: \"user\", content: [{ type: \"text\", text: msgText }] };\n\t\t\t\tthis.agent.setMessages([...this.agent.messages, userMsg]);\n\t\t\t\tsessionManager?.appendMessage(userMsg);\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.isBashRunning = false;\n\t\t\tthis.bashAbortController = null;\n\t\t\tthis.bashComponent = undefined;\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Autocomplete\n\t// ========================================================================\n\n\tprivate buildAutocompleteProvider(): CombinedAutocompleteProvider {\n\t\tconst { skills = [], prompts = [], fdPath } = this.options;\n\n\t\tconst commands: SlashCommand[] = [\n\t\t\t{ name: \"help\", description: \"Show available commands\" },\n\t\t\t{ name: \"resume\", description: \"Resume a previous session\" },\n\t\t\t{ name: \"compact\", description: \"Manually compact the session context\" },\n\t\t\t{ name: \"auto-compact\", description: \"Toggle automatic context compaction\" },\n\t\t\t{ name: \"login\", description: \"Login to an OAuth provider\" },\n\t\t\t{ name: \"logout\", description: \"Logout from an OAuth provider\" },\n\t\t\t{ name: \"skills\", description: \"List loaded skills\" },\n\t\t\t{ name: \"model\", description: \"Switch model (Ctrl+L)\" },\n\t\t\t{ name: \"quit\", description: \"Exit the CLI\" },\n\t\t\t{ name: \"exit\", description: \"Exit the CLI\" },\n\t\t];\n\n\t\tfor (const skill of skills) {\n\t\t\tcommands.push({\n\t\t\t\tname: `skill:${skill.name}`,\n\t\t\t\tdescription: skill.description,\n\t\t\t});\n\t\t}\n\n\t\t// Add prompt templates as slash commands\n\t\tfor (const prompt of prompts) {\n\t\t\tcommands.push({\n\t\t\t\tname: prompt.name,\n\t\t\t\tdescription: prompt.description,\n\t\t\t});\n\t\t}\n\n\t\treturn new CombinedAutocompleteProvider(commands, process.cwd(), fdPath ?? null);\n\t}\n\n\t// ========================================================================\n\t// Model Selection\n\t// ========================================================================\n\n\tprivate async handleModelSelect(): Promise<void> {\n\t\tconst latestModels = getLatestModels();\n\t\tconst modelOptions: { provider: string; modelId: string; label: string }[] = [];\n\t\tfor (const [provider, models] of Object.entries(latestModels)) {\n\t\t\tfor (const modelId of models) {\n\t\t\t\tmodelOptions.push({ provider, modelId, label: `${provider}/${modelId}` });\n\t\t\t}\n\t\t}\n\n\t\tconst items: SelectItem[] = modelOptions.map((m) => {\n\t\t\tconst current = m.provider === this.currentProvider && m.modelId === this.currentModelId;\n\t\t\treturn {\n\t\t\t\tvalue: `${m.provider}/${m.modelId}`,\n\t\t\t\tlabel: current ? `${m.label} (current)` : m.label,\n\t\t\t};\n\t\t});\n\n\t\tconst selected = await this.showSelectList(\"Switch model\", items);\n\t\tif (!selected) return;\n\n\t\tconst [newProvider, ...modelParts] = selected.split(\"/\");\n\t\tconst newModelId = modelParts.join(\"/\");\n\n\t\tif (newProvider === this.currentProvider && newModelId === this.currentModelId) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.showStatus(chalk.dim(`Switching to ${newProvider}/${newModelId}...`));\n\n\t\tif (!this.options.onModelChange) {\n\t\t\tthis.showStatus(chalk.yellow(\"Model switching is not available.\"));\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconst newAgent = await this.options.onModelChange(newProvider, newModelId);\n\t\t\t// Preserve conversation history\n\t\t\tnewAgent.setMessages([...this.agent.messages]);\n\t\t\tthis.agent = newAgent;\n\n\t\t\tthis.currentProvider = newProvider;\n\t\t\tthis.currentModelId = newModelId;\n\t\t\tthis.updateFooter();\n\n\t\t\t// Persist the choice for next startup\n\t\t\tthis.options.settingsManager?.setDefaults(newProvider, newModelId);\n\n\t\t\tthis.showStatus(chalk.green(`Switched to ${newProvider}/${newModelId}`));\n\t\t} catch (error) {\n\t\t\tconst msg = error instanceof Error ? error.message : String(error);\n\t\t\tthis.showStatus(chalk.red(`Failed to switch model: ${msg}`));\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Streaming\n\t// ========================================================================\n\n\tprivate async streamPrompt(prompt: string, images?: ClipboardImage[]): Promise<void> {\n\t\tthis.isStreaming = true;\n\t\tthis.updatePendingMessagesDisplay();\n\t\tconst { sessionManager } = this.options;\n\t\tconst messagesBefore = this.agent.messages.length;\n\n\t\t// Build image parts from clipboard images\n\t\tconst imageParts: ImagePart[] = (images ?? []).map((img) => ({\n\t\t\ttype: \"image\" as const,\n\t\t\timage: Buffer.from(img.bytes).toString(\"base64\"),\n\t\t\tmediaType: img.mimeType,\n\t\t}));\n\n\t\t// Start loading animation\n\t\tthis.startLoading();\n\n\t\tthis.streamingComponent = undefined;\n\t\tthis.streamingText = \"\";\n\t\tthis.hadToolResults = false;\n\n\t\tlet errorDisplayed = false;\n\t\tlet streamFailed = false;\n\t\ttry {\n\t\t\tconst result =\n\t\t\t\timageParts.length > 0\n\t\t\t\t\t? await this.agent.stream({\n\t\t\t\t\t\t\tmessages: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\trole: \"user\" as const,\n\t\t\t\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: prompt }, ...imageParts],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t})\n\t\t\t\t\t: await this.agent.stream({ prompt });\n\n\t\t\tfor await (const part of result.fullStream) {\n\t\t\t\tswitch (part.type) {\n\t\t\t\t\tcase \"text-delta\":\n\t\t\t\t\t\t// After tool results, or for the very first text part, start a new assistant message component\n\t\t\t\t\t\t// so each agent step gets its own message bubble\n\t\t\t\t\t\tif (this.hadToolResults || !this.streamingComponent) {\n\t\t\t\t\t\t\tthis.streamingComponent = new AssistantMessageComponent(getMarkdownTheme());\n\t\t\t\t\t\t\tthis.streamingText = \"\";\n\t\t\t\t\t\t\tthis.hadToolResults = false;\n\t\t\t\t\t\t\tthis.chatContainer.addChild(this.streamingComponent);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis.streamingText += part.text;\n\t\t\t\t\t\tthis.streamingComponent!.updateText(this.streamingText);\n\t\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase \"tool-call\": {\n\t\t\t\t\t\tconst args =\n\t\t\t\t\t\t\ttypeof part.input === \"object\" && part.input !== null\n\t\t\t\t\t\t\t\t? (part.input as Record<string, unknown>)\n\t\t\t\t\t\t\t\t: {};\n\t\t\t\t\t\tconst toolComponent = new ToolExecutionComponent(part.toolName, args);\n\n\t\t\t\t\t\tif (this.toolOutputExpanded) {\n\t\t\t\t\t\t\ttoolComponent.setExpanded(true);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthis.pendingTools.set(part.toolCallId, toolComponent);\n\t\t\t\t\t\tthis.chatContainer.addChild(toolComponent);\n\t\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase \"tool-result\": {\n\t\t\t\t\t\tconst toolComponent = this.pendingTools.get(part.toolCallId);\n\t\t\t\t\t\tif (toolComponent) {\n\t\t\t\t\t\t\tconst toolOutput = extractToolOutput(part.output);\n\t\t\t\t\t\t\ttoolComponent.updateResult(toolOutput, /* isError */ false, /* isPartial */ false);\n\t\t\t\t\t\t\tthis.pendingTools.delete(part.toolCallId);\n\t\t\t\t\t\t\tthis.hadToolResults = true;\n\t\t\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase \"error\": {\n\t\t\t\t\t\tconst errorMessage =\n\t\t\t\t\t\t\t(part.error as any)?.message ??\n\t\t\t\t\t\t\t(typeof part.error === \"object\" && part.error !== null\n\t\t\t\t\t\t\t\t? JSON.stringify(part.error)\n\t\t\t\t\t\t\t\t: String(part.error));\n\t\t\t\t\t\tif (this.streamingComponent) {\n\t\t\t\t\t\t\tthis.streamingComponent.setError(errorMessage);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.showStatus(chalk.red(`Error: ${errorMessage}`));\n\t\t\t\t\t\t}\n\t\t\t\t\t\terrorDisplayed = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (errorDisplayed) return;\n\n\t\t\t// Get final response and update messages\n\t\t\tconst response = await result.response;\n\t\t\tconst responseMessages = response.messages as ModelMessage[];\n\t\t\tthis.agent.setMessages([\n\t\t\t\t...this.agent.messages.slice(0, messagesBefore),\n\t\t\t\t...buildUserMessage(prompt, imageParts),\n\t\t\t\t...responseMessages,\n\t\t\t]);\n\n\t\t\t// Save to session\n\t\t\tif (sessionManager) {\n\t\t\t\tconst userMsg: ModelMessage = {\n\t\t\t\t\trole: \"user\",\n\t\t\t\t\tcontent: [{ type: \"text\", text: prompt }, ...imageParts],\n\t\t\t\t};\n\t\t\t\tsessionManager.appendMessage(userMsg);\n\t\t\t\tfor (const msg of responseMessages) {\n\t\t\t\t\tsessionManager.appendMessage(msg);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Update footer token stats\n\t\t\tthis.updateFooterTokens();\n\n\t\t\t// Check for auto-compaction after successful response\n\t\t\tawait this.checkAutoCompaction();\n\t\t} catch (error) {\n\t\t\tif (errorDisplayed) {\n\t\t\t\tstreamFailed = true;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tstreamFailed = true;\n\t\t\tif ((error as Error).name === \"AbortError\") {\n\t\t\t\tif (this.streamingComponent) {\n\t\t\t\t\tthis.streamingComponent.setAborted();\n\t\t\t\t} else {\n\t\t\t\t\tthis.showStatus(chalk.dim(\"[aborted]\"));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst msg =\n\t\t\t\t\terror instanceof Error\n\t\t\t\t\t\t? error.message\n\t\t\t\t\t\t: typeof error === \"object\" && error !== null\n\t\t\t\t\t\t\t? JSON.stringify(error)\n\t\t\t\t\t\t\t: String(error);\n\t\t\t\tif (this.streamingComponent) {\n\t\t\t\t\tthis.streamingComponent.setError(msg);\n\t\t\t\t} else {\n\t\t\t\t\tthis.showStatus(chalk.red(`Error: ${msg}`));\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.stopLoading();\n\t\t\tthis.streamingComponent = undefined;\n\t\t\tthis.streamingText = \"\";\n\t\t\tthis.hadToolResults = false;\n\t\t\tthis.pendingTools.clear();\n\t\t\tthis.isStreaming = false;\n\t\t\tthis.steeringMessages = [];\n\t\t\tif (streamFailed) {\n\t\t\t\tthis.followUpMessages = [];\n\t\t\t}\n\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\tthis.ui.requestRender();\n\t\t}\n\n\t\t// Process queued follow-ups (skipped if stream failed/aborted)\n\t\twhile (this.followUpMessages.length > 0) {\n\t\t\tconst next = this.followUpMessages.shift();\n\t\t\tif (!next) break;\n\n\t\t\tthis.updatePendingMessagesDisplay();\n\n\t\t\tthis.chatContainer.addChild(new UserMessageComponent(next, getMarkdownTheme()));\n\t\t\tthis.ui.requestRender();\n\t\t\tawait this.streamPrompt(next);\n\t\t}\n\t}\n\n\tprivate updatePendingMessagesDisplay(): void {\n\t\tthis.pendingMessagesContainer.clear();\n\n\t\t// If no agent is running, clear pending messages (they've been consumed)\n\t\tif (!this.isStreaming && this.followUpMessages.length === 0 && this.steeringMessages.length === 0) {\n\t\t\tthis.ui.requestRender();\n\t\t\treturn;\n\t\t}\n\n\t\tconst lines = formatPendingMessages(this.steeringMessages, this.followUpMessages);\n\n\t\tif (lines.length === 0) {\n\t\t\tthis.ui.requestRender();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.pendingMessagesContainer.addChild(new Spacer(1));\n\t\tthis.pendingMessagesContainer.addChild(new Text(lines.map((l) => chalk.dim(l)).join(\"\\n\"), 1, 0));\n\t\tthis.pendingMessagesContainer.addChild(new Text(chalk.dim(\"↳ Alt+Up to edit queued messages\"), 1, 0));\n\t\tthis.pendingMessagesContainer.addChild(new Spacer(1));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate clearAllQueues(): string[] {\n\t\tconst restored = [...this.steeringMessages, ...this.followUpMessages];\n\t\tthis.steeringMessages = [];\n\t\tthis.followUpMessages = [];\n\t\t// Force agent to drop its internal queue too if possible, but edge-pi agent doesn't expose that yet.\n\t\t// However, steering messages are consumed immediately by the agent loop so they might already be gone.\n\t\t// Follow-ups are managed here in the loop, so clearing this array stops them.\n\t\treturn restored;\n\t}\n\n\t// ========================================================================\n\t// Loading Animation\n\t// ========================================================================\n\n\tprivate startLoading(): void {\n\t\tthis.stopLoading();\n\t\tthis.loadingAnimation = new Loader(\n\t\t\tthis.ui,\n\t\t\t(s: string) => chalk.cyan(s),\n\t\t\t(s: string) => chalk.dim(s),\n\t\t\t\"Working...\",\n\t\t);\n\t\tthis.loadingAnimation.start();\n\t\tthis.pendingContainer.addChild(new Spacer(1));\n\t\tthis.pendingContainer.addChild(this.loadingAnimation);\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate stopLoading(): void {\n\t\tif (this.loadingAnimation) {\n\t\t\tthis.loadingAnimation.stop();\n\t\t\tthis.pendingContainer.clear();\n\t\t\tthis.loadingAnimation = undefined;\n\t\t\tthis.ui.requestRender();\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Tool Expansion\n\t// ========================================================================\n\n\tprivate toggleToolExpansion(): void {\n\t\tthis.toolOutputExpanded = !this.toolOutputExpanded;\n\n\t\t// Update all tool components and compaction components in the chat\n\t\tfor (const child of this.chatContainer.children) {\n\t\t\tif (child instanceof ToolExecutionComponent) {\n\t\t\t\tchild.setExpanded(this.toolOutputExpanded);\n\t\t\t} else if (child instanceof CompactionSummaryComponent) {\n\t\t\t\tchild.setExpanded(this.toolOutputExpanded);\n\t\t\t} else if (child instanceof BashExecutionComponent) {\n\t\t\t\tchild.setExpanded(this.toolOutputExpanded);\n\t\t\t}\n\t\t}\n\n\t\tthis.ui.requestRender();\n\t}\n\n\t// ========================================================================\n\t// Clipboard Image Paste\n\t// ========================================================================\n\n\tprivate handleClipboardImagePaste(): void {\n\t\ttry {\n\t\t\tconst image = readClipboardImage();\n\t\t\tif (!image) return;\n\t\t\tthis.pendingImages.push(image);\n\t\t\tconst ext = extensionForImageMimeType(image.mimeType) ?? \"image\";\n\t\t\tconst label = `[image ${this.pendingImages.length}: ${ext}]`;\n\t\t\tthis.editor.insertTextAtCursor(label);\n\t\t\tthis.ui.requestRender();\n\t\t} catch {\n\t\t\t// Silently ignore clipboard errors (may not have permission, etc.)\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Compaction\n\t// ========================================================================\n\n\t/**\n\t * Handle the /compact command.\n\t */\n\tprivate async handleCompactCommand(_customInstructions?: string): Promise<void> {\n\t\tconst messages = this.agent.messages;\n\t\tif (messages.length < 2) {\n\t\t\tthis.showStatus(chalk.yellow(\"Nothing to compact (not enough messages).\"));\n\t\t\treturn;\n\t\t}\n\n\t\tawait this.executeCompaction(false);\n\t}\n\n\t/**\n\t * Toggle auto-compaction on/off.\n\t */\n\tprivate toggleAutoCompaction(): void {\n\t\tthis.autoCompaction = !this.autoCompaction;\n\t\tthis.footer.setAutoCompaction(this.autoCompaction);\n\t\tthis.options.settingsManager?.setCompactionEnabled(this.autoCompaction);\n\t\tthis.showStatus(\n\t\t\tthis.autoCompaction ? chalk.green(\"Auto-compaction enabled\") : chalk.dim(\"Auto-compaction disabled\"),\n\t\t);\n\t\tthis.ui.requestRender();\n\t}\n\n\t/**\n\t * Check if auto-compaction should trigger after an agent response.\n\t */\n\tprivate async checkAutoCompaction(): Promise<void> {\n\t\tif (!this.autoCompaction) return;\n\n\t\tconst contextTokens = estimateContextTokens([...this.agent.messages]);\n\t\tif (!shouldCompact(contextTokens, this.contextWindow, this.compactionSettings)) return;\n\n\t\tawait this.executeCompaction(true);\n\t}\n\n\t/**\n\t * Execute compaction (used by both manual /compact and auto mode).\n\t */\n\tprivate async executeCompaction(isAuto: boolean): Promise<CompactionResult | undefined> {\n\t\tif (this.isCompacting) return undefined;\n\n\t\tconst { sessionManager } = this.options;\n\n\t\t// Build path entries from session if available, otherwise from agent messages\n\t\tconst pathEntries = sessionManager ? sessionManager.getBranch() : this.buildSessionEntriesFromMessages();\n\n\t\tif (pathEntries.length < 2) {\n\t\t\tif (!isAuto) {\n\t\t\t\tthis.showStatus(chalk.yellow(\"Nothing to compact (not enough messages).\"));\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\n\t\t// Prepare compaction\n\t\tconst preparation = prepareCompaction(pathEntries, this.compactionSettings);\n\t\tif (!preparation) {\n\t\t\tif (!isAuto) {\n\t\t\t\tthis.showStatus(chalk.yellow(\"Nothing to compact (already compacted or insufficient history).\"));\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (preparation.messagesToSummarize.length === 0) {\n\t\t\tif (!isAuto) {\n\t\t\t\tthis.showStatus(chalk.yellow(\"Nothing to compact (no messages to summarize).\"));\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\n\t\tthis.isCompacting = true;\n\t\tthis.compactionAbortController = new AbortController();\n\n\t\t// Show compaction indicator\n\t\tconst label = isAuto\n\t\t\t? \"Auto-compacting context... (Escape to cancel)\"\n\t\t\t: \"Compacting context... (Escape to cancel)\";\n\t\tconst compactingLoader = new Loader(\n\t\t\tthis.ui,\n\t\t\t(s: string) => chalk.cyan(s),\n\t\t\t(s: string) => chalk.dim(s),\n\t\t\tlabel,\n\t\t);\n\t\tcompactingLoader.start();\n\t\tthis.pendingContainer.clear();\n\t\tthis.pendingContainer.addChild(new Spacer(1));\n\t\tthis.pendingContainer.addChild(compactingLoader);\n\t\tthis.ui.requestRender();\n\n\t\tlet result: CompactionResult | undefined;\n\n\t\ttry {\n\t\t\t// We need a LanguageModel for summarization. Use the agent's model\n\t\t\t// by extracting it from the config. The model is accessible through\n\t\t\t// the onModelChange callback pattern, but for simplicity we create\n\t\t\t// a model via the same factory used at startup.\n\t\t\tconst { model } = await this.getCompactionModel();\n\n\t\t\tresult = await compact(preparation, model, this.compactionAbortController.signal);\n\n\t\t\t// Record compaction in session\n\t\t\tif (sessionManager) {\n\t\t\t\tsessionManager.appendCompaction(\n\t\t\t\t\tresult.summary,\n\t\t\t\t\tresult.firstKeptEntryId,\n\t\t\t\t\tresult.tokensBefore,\n\t\t\t\t\tresult.details,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Rebuild agent messages from the session context\n\t\t\tif (sessionManager) {\n\t\t\t\tconst context = sessionManager.buildSessionContext();\n\t\t\t\tthis.agent.setMessages(context.messages);\n\t\t\t}\n\n\t\t\t// Rebuild the chat UI\n\t\t\tthis.rebuildChatFromSession();\n\n\t\t\t// Add compaction summary component so user sees it\n\t\t\tconst summaryComponent = new CompactionSummaryComponent(result.tokensBefore, result.summary);\n\t\t\tif (this.toolOutputExpanded) {\n\t\t\t\tsummaryComponent.setExpanded(true);\n\t\t\t}\n\t\t\tthis.chatContainer.addChild(summaryComponent);\n\n\t\t\t// Update footer tokens\n\t\t\tthis.updateFooterTokens();\n\n\t\t\tif (this.options.verbose) {\n\t\t\t\tconst tokensAfter = estimateContextTokens([...this.agent.messages]);\n\t\t\t\tthis.showStatus(\n\t\t\t\t\tchalk.dim(\n\t\t\t\t\t\t`Compacted: ${result.tokensBefore.toLocaleString()} -> ${tokensAfter.toLocaleString()} tokens`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tif (\n\t\t\t\tthis.compactionAbortController.signal.aborted ||\n\t\t\t\tmessage === \"Compaction cancelled\" ||\n\t\t\t\t(error instanceof Error && error.name === \"AbortError\")\n\t\t\t) {\n\t\t\t\tthis.showStatus(chalk.dim(\"Compaction cancelled.\"));\n\t\t\t} else {\n\t\t\t\tthis.showStatus(chalk.red(`Compaction failed: ${message}`));\n\t\t\t}\n\t\t} finally {\n\t\t\tcompactingLoader.stop();\n\t\t\tthis.pendingContainer.clear();\n\t\t\tthis.isCompacting = false;\n\t\t\tthis.compactionAbortController = null;\n\t\t\tthis.ui.requestRender();\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Get the language model for compaction summarization.\n\t * Uses the same model creation path as the main agent.\n\t */\n\tprivate async getCompactionModel(): Promise<{ model: import(\"ai\").LanguageModel }> {\n\t\tconst { createModel } = await import(\"../../model-factory.js\");\n\t\treturn createModel({\n\t\t\tprovider: this.currentProvider,\n\t\t\tmodel: this.currentModelId,\n\t\t\tauthStorage: this.options.authStorage,\n\t\t});\n\t}\n\n\t/**\n\t * Build session entries from agent messages (when no session manager).\n\t * Creates synthetic SessionEntry objects for the compaction algorithm.\n\t */\n\tprivate buildSessionEntriesFromMessages(): import(\"edge-pi\").SessionEntry[] {\n\t\tconst messages = this.agent.messages;\n\t\tconst entries: import(\"edge-pi\").SessionEntry[] = [];\n\t\tlet parentId: string | null = null;\n\n\t\tfor (let i = 0; i < messages.length; i++) {\n\t\t\tconst id = `msg-${i}`;\n\t\t\tentries.push({\n\t\t\t\ttype: \"message\",\n\t\t\t\tid,\n\t\t\t\tparentId,\n\t\t\t\ttimestamp: new Date().toISOString(),\n\t\t\t\tmessage: messages[i],\n\t\t\t});\n\t\t\tparentId = id;\n\t\t}\n\n\t\treturn entries;\n\t}\n\n\t/**\n\t * Rebuild the chat UI from session context after compaction.\n\t */\n\tprivate rebuildChatFromSession(): void {\n\t\tthis.chatContainer.clear();\n\n\t\tconst messages = this.agent.messages;\n\t\tfor (const msg of messages) {\n\t\t\tif (msg.role === \"user\") {\n\t\t\t\t// Check if this is a compaction summary\n\t\t\t\tconst content = msg.content;\n\t\t\t\tif (Array.isArray(content) && content.length > 0) {\n\t\t\t\t\tconst textBlock = content[0] as { type: string; text?: string };\n\t\t\t\t\tif (textBlock.type === \"text\" && textBlock.text?.startsWith('<summary type=\"compaction\"')) {\n\t\t\t\t\t\t// Skip compaction summaries in rebuild (they are injected by buildSessionContext)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (textBlock.type === \"text\" && textBlock.text?.startsWith('<summary type=\"branch\"')) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst text = extractTextFromMessage(msg);\n\t\t\t\tif (text) {\n\t\t\t\t\tthis.chatContainer.addChild(new UserMessageComponent(text, getMarkdownTheme()));\n\t\t\t\t}\n\t\t\t} else if (msg.role === \"assistant\") {\n\t\t\t\tconst assistantMsg = msg as import(\"edge-pi\").AssistantModelMessage;\n\t\t\t\tconst textParts: string[] = [];\n\t\t\t\tfor (const block of assistantMsg.content) {\n\t\t\t\t\tconst b = block as {\n\t\t\t\t\t\ttype: string;\n\t\t\t\t\t\ttext?: string;\n\t\t\t\t\t\ttoolName?: string;\n\t\t\t\t\t\tinput?: unknown;\n\t\t\t\t\t\ttoolCallId?: string;\n\t\t\t\t\t};\n\t\t\t\t\tif (b.type === \"text\" && b.text) {\n\t\t\t\t\t\ttextParts.push(b.text);\n\t\t\t\t\t} else if (b.type === \"tool-call\" && b.toolName) {\n\t\t\t\t\t\tconst args =\n\t\t\t\t\t\t\ttypeof b.input === \"object\" && b.input !== null ? (b.input as Record<string, unknown>) : {};\n\t\t\t\t\t\tconst toolComp = new ToolExecutionComponent(b.toolName, args);\n\t\t\t\t\t\tif (this.toolOutputExpanded) {\n\t\t\t\t\t\t\ttoolComp.setExpanded(true);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Mark as completed (we don't have the result here, just show collapsed)\n\t\t\t\t\t\ttoolComp.updateResult({ text: \"(from history)\" }, false, false);\n\t\t\t\t\t\tthis.chatContainer.addChild(toolComp);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (textParts.length > 0) {\n\t\t\t\t\tconst comp = new AssistantMessageComponent(getMarkdownTheme());\n\t\t\t\t\tcomp.updateText(textParts.join(\"\"));\n\t\t\t\t\tthis.chatContainer.addChild(comp);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Skip tool messages in UI rebuild - they are consumed by tool-call components\n\t\t}\n\n\t\tthis.ui.requestRender();\n\t}\n\n\t// ========================================================================\n\t// Footer Token Tracking\n\t// ========================================================================\n\n\t/**\n\t * Update the footer with current token count information.\n\t */\n\tprivate updateFooterTokens(): void {\n\t\tconst contextTokens = estimateContextTokens([...this.agent.messages]);\n\t\tthis.footer.setTokenInfo(contextTokens, this.contextWindow);\n\t\tthis.footer.setAutoCompaction(this.autoCompaction);\n\t\tthis.ui?.requestRender();\n\t}\n\n\t/**\n\t * Check if the current provider is using an OAuth subscription credential.\n\t */\n\tprivate isSubscriptionProvider(): boolean {\n\t\tconst { authStorage } = this.options;\n\t\tif (!authStorage) return false;\n\t\tconst cred = authStorage.get(this.currentProvider);\n\t\treturn cred?.type === \"oauth\";\n\t}\n\n\t/**\n\t * Replace the footer component and update token info.\n\t */\n\tprivate updateFooter(): void {\n\t\tthis.footer = new FooterComponent(this.currentProvider, this.currentModelId);\n\t\tthis.footer.setSubscription(this.isSubscriptionProvider());\n\t\tthis.updateFooterTokens();\n\n\t\t// Replace footer in UI\n\t\tconst children = this.ui.children;\n\t\tchildren[children.length - 1] = this.footer;\n\t\tthis.ui.requestRender();\n\t}\n\n\t// ========================================================================\n\t// Startup Resource Display\n\t// ========================================================================\n\n\tprivate formatDisplayPath(p: string): string {\n\t\tconst home = process.env.HOME || process.env.USERPROFILE || \"\";\n\t\tif (home && p.startsWith(home)) {\n\t\t\treturn `~${p.slice(home.length)}`;\n\t\t}\n\t\treturn p;\n\t}\n\n\tprivate showLoadedResources(contextFiles: ContextFile[], skills: Skill[], prompts: PromptTemplate[]): void {\n\t\tconst sectionHeader = (name: string) => chalk.cyan(`[${name}]`);\n\n\t\tif (contextFiles.length > 0) {\n\t\t\tconst contextList = contextFiles.map((f) => chalk.dim(` ${this.formatDisplayPath(f.path)}`)).join(\"\\n\");\n\t\t\tthis.headerContainer.addChild(new Text(`${sectionHeader(\"Context\")}\\n${contextList}`, 0, 0));\n\t\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\t}\n\n\t\tif (skills.length > 0) {\n\t\t\tconst skillList = skills.map((s) => chalk.dim(` ${this.formatDisplayPath(s.filePath)}`)).join(\"\\n\");\n\t\t\tthis.headerContainer.addChild(new Text(`${sectionHeader(\"Skills\")}\\n${skillList}`, 0, 0));\n\t\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\t}\n\n\t\tif (prompts.length > 0) {\n\t\t\tconst promptList = prompts\n\t\t\t\t.map((p) => {\n\t\t\t\t\tconst sourceLabel = chalk.cyan(p.source);\n\t\t\t\t\treturn chalk.dim(` ${sourceLabel} /${p.name}`);\n\t\t\t\t})\n\t\t\t\t.join(\"\\n\");\n\t\t\tthis.headerContainer.addChild(new Text(`${sectionHeader(\"Prompts\")}\\n${promptList}`, 0, 0));\n\t\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Commands\n\t// ========================================================================\n\n\tprivate showHelp(): void {\n\t\tconst helpText = [\n\t\t\tchalk.bold(\"Commands:\"),\n\t\t\t\" !<command> Run inline bash and include output in context\",\n\t\t\t\" !!<command> Run inline bash but exclude output from context\",\n\t\t\t\" /resume Resume a previous session\",\n\t\t\t\" /compact [text] Compact the session context (optional instructions)\",\n\t\t\t\" /auto-compact Toggle automatic context compaction\",\n\t\t\t\" /model Switch model (Ctrl+L)\",\n\t\t\t\" /login Login to an OAuth provider\",\n\t\t\t\" /logout Logout from an OAuth provider\",\n\t\t\t\" /skills List loaded skills\",\n\t\t\t\" /skill:<name> Invoke a skill by name\",\n\t\t\t\" /quit, /exit Exit the CLI\",\n\t\t].join(\"\\n\");\n\n\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\tthis.chatContainer.addChild(new Text(helpText, 1, 0));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate showSkills(): void {\n\t\tconst { skills = [] } = this.options;\n\n\t\tif (skills.length === 0) {\n\t\t\tthis.showStatus(chalk.dim(\"No skills loaded.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst lines: string[] = [];\n\t\tfor (const skill of skills) {\n\t\t\tconst hidden = skill.disableModelInvocation ? chalk.dim(\" (hidden from model)\") : \"\";\n\t\t\tlines.push(` ${chalk.bold(skill.name)}${hidden}`);\n\t\t\tlines.push(chalk.dim(` ${skill.description}`));\n\t\t\tlines.push(chalk.dim(` ${skill.filePath}`));\n\t\t}\n\n\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\tthis.chatContainer.addChild(new Text(lines.join(\"\\n\"), 1, 0));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate async handleSkillInvocation(skillName: string): Promise<void> {\n\t\tconst { skills = [] } = this.options;\n\t\tconst skill = skills.find((s) => s.name === skillName);\n\n\t\tif (!skill) {\n\t\t\tthis.showStatus(chalk.red(`Skill \"${skillName}\" not found.`));\n\t\t\treturn;\n\t\t}\n\n\t\tconst skillPrompt = `Please read and follow the instructions in the skill file: ${skill.filePath}`;\n\t\tthis.chatContainer.addChild(new UserMessageComponent(skillPrompt, getMarkdownTheme()));\n\t\tthis.ui.requestRender();\n\t\tawait this.streamPrompt(skillPrompt);\n\t}\n\n\t// ========================================================================\n\t// OAuth Login/Logout\n\t// ========================================================================\n\n\tprivate async handleLogin(): Promise<void> {\n\t\tconst { authStorage } = this.options;\n\t\tif (!authStorage) {\n\t\t\tthis.showStatus(chalk.red(\"Auth storage not available.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst providers = authStorage.getProviders();\n\t\tif (providers.length === 0) {\n\t\t\tthis.showStatus(chalk.dim(\"No OAuth providers registered.\"));\n\t\t\treturn;\n\t\t}\n\n\t\t// Use SelectList overlay for provider selection\n\t\tconst items: SelectItem[] = providers.map((p) => {\n\t\t\tconst loggedIn = authStorage.get(p.id)?.type === \"oauth\" ? \" (logged in)\" : \"\";\n\t\t\treturn { value: p.id, label: `${p.name}${loggedIn}` };\n\t\t});\n\n\t\tconst selected = await this.showSelectList(\"Login to OAuth provider\", items);\n\t\tif (!selected) return;\n\n\t\tconst provider = providers.find((p) => p.id === selected);\n\t\tif (!provider) return;\n\n\t\tthis.showStatus(chalk.dim(`Logging in to ${provider.name}...`));\n\n\t\ttry {\n\t\t\tawait authStorage.login(provider.id, {\n\t\t\t\tonAuth: (info) => {\n\t\t\t\t\tconst lines = [chalk.bold(\"Open this URL in your browser:\"), chalk.cyan(info.url)];\n\t\t\t\t\tif (info.instructions) {\n\t\t\t\t\t\tlines.push(chalk.dim(info.instructions));\n\t\t\t\t\t}\n\t\t\t\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\t\t\t\tthis.chatContainer.addChild(new Text(lines.join(\"\\n\"), 1, 0));\n\t\t\t\t\tthis.ui.requestRender();\n\n\t\t\t\t\t// Try to open browser\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst { execSync } = require(\"node:child_process\") as typeof import(\"node:child_process\");\n\t\t\t\t\t\tconst platform = process.platform;\n\t\t\t\t\t\tif (platform === \"darwin\") {\n\t\t\t\t\t\t\texecSync(`open \"${info.url}\"`, { stdio: \"ignore\" });\n\t\t\t\t\t\t} else if (platform === \"linux\") {\n\t\t\t\t\t\t\texecSync(`xdg-open \"${info.url}\" 2>/dev/null || sensible-browser \"${info.url}\" 2>/dev/null`, {\n\t\t\t\t\t\t\t\tstdio: \"ignore\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t} else if (platform === \"win32\") {\n\t\t\t\t\t\t\texecSync(`start \"\" \"${info.url}\"`, { stdio: \"ignore\" });\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Silently fail - user can open manually\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tonPrompt: async (promptInfo) => {\n\t\t\t\t\t// Show prompt message and wait for user input\n\t\t\t\t\tthis.showStatus(chalk.dim(promptInfo.message));\n\t\t\t\t\tconst answer = await this.getUserInput();\n\t\t\t\t\treturn answer.trim();\n\t\t\t\t},\n\t\t\t\tonProgress: (message) => {\n\t\t\t\t\tthis.showStatus(chalk.dim(message));\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tthis.footer.setSubscription(this.isSubscriptionProvider());\n\t\t\tthis.ui.requestRender();\n\t\t\tthis.showStatus(chalk.green(`Logged in to ${provider.name}. Credentials saved.`));\n\t\t} catch (error) {\n\t\t\tconst msg = error instanceof Error ? error.message : String(error);\n\t\t\tif (msg !== \"Login cancelled\") {\n\t\t\t\tthis.showStatus(chalk.red(`Login failed: ${msg}`));\n\t\t\t} else {\n\t\t\t\tthis.showStatus(chalk.dim(\"Login cancelled.\"));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async handleLogout(): Promise<void> {\n\t\tconst { authStorage } = this.options;\n\t\tif (!authStorage) {\n\t\t\tthis.showStatus(chalk.red(\"Auth storage not available.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst loggedIn = authStorage\n\t\t\t.list()\n\t\t\t.filter((id) => authStorage.get(id)?.type === \"oauth\")\n\t\t\t.map((id) => {\n\t\t\t\tconst provider = authStorage.getProvider(id);\n\t\t\t\treturn { id, name: provider?.name ?? id };\n\t\t\t});\n\n\t\tif (loggedIn.length === 0) {\n\t\t\tthis.showStatus(chalk.dim(\"No OAuth providers logged in. Use /login first.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst items: SelectItem[] = loggedIn.map((p) => ({\n\t\t\tvalue: p.id,\n\t\t\tlabel: p.name,\n\t\t}));\n\n\t\tconst selected = await this.showSelectList(\"Logout from OAuth provider\", items);\n\t\tif (!selected) return;\n\n\t\tconst entry = loggedIn.find((p) => p.id === selected);\n\t\tif (!entry) return;\n\n\t\tauthStorage.logout(entry.id);\n\t\tthis.showStatus(chalk.green(`Logged out of ${entry.name}.`));\n\t}\n\n\t// ========================================================================\n\t// Resume Session\n\t// ========================================================================\n\n\t/**\n\t * List session files from the session directory, sorted by modification time (newest first).\n\t * Returns metadata for each session including the first user message as a preview.\n\t */\n\tprivate listAvailableSessions(): { path: string; mtime: number; preview: string; timestamp: string }[] {\n\t\tconst { sessionDir } = this.options;\n\t\tif (!sessionDir || !existsSync(sessionDir)) return [];\n\n\t\ttry {\n\t\t\tconst files = readdirSync(sessionDir)\n\t\t\t\t.filter((f: string) => f.endsWith(\".jsonl\"))\n\t\t\t\t.map((f: string) => {\n\t\t\t\t\tconst filePath = join(sessionDir, f);\n\t\t\t\t\tconst mtime = statSync(filePath).mtime.getTime();\n\t\t\t\t\treturn { name: f, path: filePath, mtime };\n\t\t\t\t})\n\t\t\t\t.sort((a, b) => b.mtime - a.mtime);\n\n\t\t\tconst sessions: { path: string; mtime: number; preview: string; timestamp: string }[] = [];\n\t\t\tfor (const file of files) {\n\t\t\t\t// Skip the current session file\n\t\t\t\tif (this.options.sessionManager?.getSessionFile() === file.path) continue;\n\n\t\t\t\tconst preview = this.getSessionPreview(file.path);\n\t\t\t\tconst timestamp = new Date(file.mtime).toLocaleString();\n\t\t\t\tsessions.push({ path: file.path, mtime: file.mtime, preview, timestamp });\n\t\t\t}\n\t\t\treturn sessions;\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Extract the first user message from a session file for preview.\n\t */\n\tprivate getSessionPreview(filePath: string): string {\n\t\ttry {\n\t\t\tconst content = readFileSync(filePath, \"utf-8\");\n\t\t\tconst lines = content.trim().split(\"\\n\");\n\t\t\tfor (const line of lines) {\n\t\t\t\tif (!line.trim()) continue;\n\t\t\t\ttry {\n\t\t\t\t\tconst entry = JSON.parse(line);\n\t\t\t\t\tif (entry.type === \"message\" && entry.message?.role === \"user\") {\n\t\t\t\t\t\tconst msg = entry.message;\n\t\t\t\t\t\tlet text = \"\";\n\t\t\t\t\t\tif (typeof msg.content === \"string\") {\n\t\t\t\t\t\t\ttext = msg.content;\n\t\t\t\t\t\t} else if (Array.isArray(msg.content)) {\n\t\t\t\t\t\t\tfor (const block of msg.content) {\n\t\t\t\t\t\t\t\tif (block.type === \"text\" && block.text) {\n\t\t\t\t\t\t\t\t\ttext = block.text;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Truncate and clean up for display\n\t\t\t\t\t\ttext = text.replace(/\\n/g, \" \").trim();\n\t\t\t\t\t\tif (text.length > 80) {\n\t\t\t\t\t\t\ttext = `${text.slice(0, 77)}...`;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn text || \"(empty message)\";\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Skip malformed lines\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn \"(no messages)\";\n\t\t} catch {\n\t\t\treturn \"(unreadable)\";\n\t\t}\n\t}\n\n\t/**\n\t * Format a relative time string (e.g. \"2 hours ago\", \"3 days ago\").\n\t */\n\tprivate formatRelativeTime(mtime: number): string {\n\t\tconst now = Date.now();\n\t\tconst diffMs = now - mtime;\n\t\tconst diffSec = Math.floor(diffMs / 1000);\n\t\tconst diffMin = Math.floor(diffSec / 60);\n\t\tconst diffHour = Math.floor(diffMin / 60);\n\t\tconst diffDay = Math.floor(diffHour / 24);\n\n\t\tif (diffMin < 1) return \"just now\";\n\t\tif (diffMin < 60) return `${diffMin}m ago`;\n\t\tif (diffHour < 24) return `${diffHour}h ago`;\n\t\tif (diffDay < 30) return `${diffDay}d ago`;\n\t\treturn new Date(mtime).toLocaleDateString();\n\t}\n\n\t/**\n\t * Handle the /resume command: show a list of previous sessions and load the selected one.\n\t */\n\tprivate async handleResume(): Promise<void> {\n\t\tconst sessions = this.listAvailableSessions();\n\t\tif (sessions.length === 0) {\n\t\t\tthis.showStatus(chalk.yellow(\"No previous sessions found.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst items: SelectItem[] = sessions.map((s) => ({\n\t\t\tvalue: s.path,\n\t\t\tlabel: `${chalk.dim(this.formatRelativeTime(s.mtime))} ${s.preview}`,\n\t\t}));\n\n\t\tconst selected = await this.showSelectList(\"Resume session\", items);\n\t\tif (!selected) return;\n\n\t\tconst session = sessions.find((s) => s.path === selected);\n\t\tif (!session) return;\n\n\t\ttry {\n\t\t\t// Open the selected session\n\t\t\tconst sessionDir = this.options.sessionDir!;\n\t\t\tconst newSessionManager = SessionManagerClass.open(selected, sessionDir);\n\n\t\t\t// Rebuild agent messages from session context\n\t\t\tconst context = newSessionManager.buildSessionContext();\n\t\t\tthis.agent.setMessages(context.messages);\n\n\t\t\t// Update session manager reference\n\t\t\tthis.options.sessionManager = newSessionManager;\n\n\t\t\t// Rebuild the chat UI\n\t\t\tthis.chatContainer.clear();\n\t\t\tthis.rebuildChatFromSession();\n\n\t\t\t// Update footer tokens\n\t\t\tthis.updateFooterTokens();\n\n\t\t\tconst msgCount = context.messages.length;\n\t\t\tthis.showStatus(chalk.green(`Resumed session (${msgCount} messages)`));\n\t\t} catch (error) {\n\t\t\tconst msg = error instanceof Error ? error.message : String(error);\n\t\t\tthis.showStatus(chalk.red(`Failed to resume session: ${msg}`));\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Select List (overlay pattern from pi-coding-agent)\n\t// ========================================================================\n\n\tprivate showSelectList(title: string, items: SelectItem[]): Promise<string | null> {\n\t\treturn new Promise((resolve) => {\n\t\t\tconst container = new Container();\n\n\t\t\tcontainer.addChild(new Spacer(1));\n\t\t\tcontainer.addChild(new Text(chalk.bold.cyan(title), 1, 0));\n\n\t\t\tconst selectList = new SelectList(items, Math.min(items.length, 10), getSelectListTheme());\n\t\t\tselectList.onSelect = (item) => {\n\t\t\t\t// Restore normal UI\n\t\t\t\tthis.pendingContainer.clear();\n\t\t\t\tthis.editorContainer.addChild(this.editor);\n\t\t\t\tthis.ui.setFocus(this.editor);\n\t\t\t\tthis.ui.requestRender();\n\t\t\t\tresolve(item.value);\n\t\t\t};\n\t\t\tselectList.onCancel = () => {\n\t\t\t\tthis.pendingContainer.clear();\n\t\t\t\tthis.editorContainer.addChild(this.editor);\n\t\t\t\tthis.ui.setFocus(this.editor);\n\t\t\t\tthis.ui.requestRender();\n\t\t\t\tresolve(null);\n\t\t\t};\n\t\t\tcontainer.addChild(selectList);\n\t\t\tcontainer.addChild(new Text(chalk.dim(\"↑↓ navigate • enter select • esc cancel\"), 1, 0));\n\t\t\tcontainer.addChild(new Spacer(1));\n\n\t\t\t// Replace editor area with select list\n\t\t\tthis.editorContainer.clear();\n\t\t\tthis.pendingContainer.clear();\n\t\t\tthis.pendingContainer.addChild(container);\n\t\t\tthis.ui.setFocus(selectList);\n\t\t\tthis.ui.requestRender();\n\t\t});\n\t}\n\n\t// ========================================================================\n\t// Status & Utilities\n\t// ========================================================================\n\n\tprivate showStatus(text: string): void {\n\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\tthis.chatContainer.addChild(new Text(text, 1, 0));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate updateEditorBorderColor(): void {\n\t\tthis.editorTheme.borderColor = this.isBashMode ? (s: string) => chalk.yellow(s) : (s: string) => chalk.gray(s);\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate shutdown(): void {\n\t\tthis.ui.stop();\n\t\tconsole.log(chalk.dim(\"\\nGoodbye.\"));\n\t\tprocess.exit(0);\n\t}\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction buildUserMessage(text: string, imageParts?: ImagePart[]): ModelMessage[] {\n\tconst content: Array<{ type: \"text\"; text: string } | ImagePart> = [{ type: \"text\" as const, text }];\n\tif (imageParts && imageParts.length > 0) {\n\t\tcontent.push(...imageParts);\n\t}\n\treturn [{ role: \"user\" as const, content }];\n}\n\nfunction extractTextFromMessage(msg: ModelMessage): string {\n\tif (msg.role === \"user\") {\n\t\tconst content = (msg as import(\"edge-pi\").UserModelMessage).content;\n\t\tif (typeof content === \"string\") return content;\n\t\tif (Array.isArray(content)) {\n\t\t\treturn content\n\t\t\t\t.filter((c): c is { type: \"text\"; text: string } => (c as { type: string }).type === \"text\")\n\t\t\t\t.map((c) => c.text)\n\t\t\t\t.join(\"\");\n\t\t}\n\t}\n\treturn \"\";\n}\n"]}
1
+ {"version":3,"file":"interactive-mode.js","sourceRoot":"","sources":["../../../src/modes/interactive/interactive-mode.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Interactive mode using @mariozechner/pi-tui.\n *\n * Replaces the old readline-based REPL with a proper TUI that matches\n * the UX patterns from @mariozechner/pi-coding-agent:\n * - Editor component for input with submit/escape handling\n * - Markdown rendering for assistant responses\n * - Tool execution components with collapsible output\n * - Footer with model/provider info and token stats\n * - Container-based layout (header → chat → pending → editor → footer)\n * - Context compaction (manual /compact + auto mode)\n */\n\nimport { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n\tCombinedAutocompleteProvider,\n\tContainer,\n\tEditor,\n\tKey,\n\tLoader,\n\tmatchesKey,\n\tProcessTerminal,\n\ttype SelectItem,\n\tSelectList,\n\ttype SlashCommand,\n\tSpacer,\n\tText,\n\tTUI,\n} from \"@mariozechner/pi-tui\";\nimport type { ImagePart } from \"ai\";\nimport chalk from \"chalk\";\nimport type { CodingAgent, CodingAgentConfig, ModelMessage, SessionManager } from \"edge-pi\";\nimport {\n\ttype CompactionResult,\n\ttype CompactionSettings,\n\tcompact,\n\tDEFAULT_COMPACTION_SETTINGS,\n\testimateContextTokens,\n\tprepareCompaction,\n\tSessionManager as SessionManagerClass,\n\tshouldCompact,\n} from \"edge-pi\";\nimport type { AuthStorage } from \"../../auth/auth-storage.js\";\nimport type { ContextFile } from \"../../context.js\";\nimport { getLatestModels } from \"../../model-factory.js\";\nimport type { PromptTemplate } from \"../../prompts.js\";\nimport { expandPromptTemplate } from \"../../prompts.js\";\nimport type { SettingsManager } from \"../../settings.js\";\nimport type { Skill } from \"../../skills.js\";\nimport { executeBashCommand } from \"../../utils/bash-executor.js\";\nimport { type ClipboardImage, extensionForImageMimeType, readClipboardImage } from \"../../utils/clipboard-image.js\";\nimport { formatPendingMessages, parseBashInput } from \"./bash-helpers.js\";\nimport { AssistantMessageComponent } from \"./components/assistant-message.js\";\nimport { BashExecutionComponent } from \"./components/bash-execution.js\";\nimport { CompactionSummaryComponent } from \"./components/compaction-summary.js\";\nimport { FooterComponent } from \"./components/footer.js\";\nimport { ToolExecutionComponent, type ToolOutput } from \"./components/tool-execution.js\";\nimport { UserMessageComponent } from \"./components/user-message.js\";\nimport { getEditorTheme, getMarkdownTheme, getSelectListTheme } from \"./theme.js\";\n\n/** Default context window size (used when model doesn't report one). */\nconst DEFAULT_CONTEXT_WINDOW = 200_000;\n\n/** Extract display-friendly output from a tool result. Handles both plain strings and structured objects with text/image fields. */\nfunction extractToolOutput(output: unknown): ToolOutput {\n\tif (typeof output === \"string\") {\n\t\treturn { text: output };\n\t}\n\tif (typeof output === \"object\" && output !== null && \"text\" in output && typeof (output as any).text === \"string\") {\n\t\tconst obj = output as any;\n\t\treturn {\n\t\t\ttext: obj.text,\n\t\t\t...(obj.image && { image: obj.image }),\n\t\t};\n\t}\n\treturn { text: JSON.stringify(output) };\n}\n\nexport interface InteractiveModeOptions {\n\tinitialMessage?: string;\n\tinitialMessages?: string[];\n\tsessionManager?: SessionManager;\n\tskills?: Skill[];\n\tcontextFiles?: ContextFile[];\n\tprompts?: PromptTemplate[];\n\tverbose?: boolean;\n\tprovider: string;\n\tmodelId: string;\n\tauthStorage?: AuthStorage;\n\t/** Settings manager for persisting user preferences (provider, model, compaction). */\n\tsettingsManager?: SettingsManager;\n\t/** Path to the `fd` binary for @ file autocomplete, or undefined if unavailable. */\n\tfdPath?: string;\n\t/** Called when the user switches model via Ctrl+L. Returns a new agent for the new model. */\n\tonModelChange?: (provider: string, modelId: string) => Promise<CodingAgent>;\n\t/** Context window size for the model. Defaults to 200k. */\n\tcontextWindow?: number;\n\t/** Directory where session files are stored. Required for /resume. */\n\tsessionDir?: string;\n\t/** Agent config used to recreate agents when resuming sessions. */\n\tagentConfig?: CodingAgentConfig;\n\t/** When true, show the session picker immediately on startup. */\n\tresumeOnStart?: boolean;\n}\n\n/**\n * Run the interactive TUI mode with streaming output.\n */\nexport async function runInteractiveMode(agent: CodingAgent, options: InteractiveModeOptions): Promise<void> {\n\tconst mode = new InteractiveMode(agent, options);\n\tawait mode.run();\n}\n\n// ============================================================================\n// InteractiveMode class\n// ============================================================================\n\nclass InteractiveMode {\n\tprivate agent: CodingAgent;\n\tprivate options: InteractiveModeOptions;\n\tprivate currentProvider: string;\n\tprivate currentModelId: string;\n\n\tprivate ui!: TUI;\n\tprivate headerContainer!: Container;\n\tprivate chatContainer!: Container;\n\tprivate pendingContainer!: Container;\n\tprivate pendingMessagesContainer!: Container;\n\tprivate editorContainer!: Container;\n\tprivate editor!: Editor;\n\tprivate editorTheme!: import(\"@mariozechner/pi-tui\").EditorTheme;\n\n\t// Message queues\n\tprivate steeringMessages: string[] = [];\n\tprivate followUpMessages: string[] = [];\n\tprivate isStreaming = false;\n\n\t// Inline bash state\n\tprivate isBashMode = false;\n\tprivate isBashRunning = false;\n\tprivate bashAbortController: AbortController | null = null;\n\tprivate bashComponent: import(\"./components/bash-execution.js\").BashExecutionComponent | undefined = undefined;\n\tprivate footer!: FooterComponent;\n\n\t// Loading animation during agent processing\n\tprivate loadingAnimation: Loader | undefined = undefined;\n\n\t// Streaming state\n\tprivate streamingComponent: AssistantMessageComponent | undefined = undefined;\n\tprivate streamingText = \"\";\n\tprivate hadToolResults = false;\n\n\t// Tool execution tracking: toolCallId → component\n\tprivate pendingTools = new Map<string, ToolExecutionComponent>();\n\n\t// Tool output expansion state\n\tprivate toolOutputExpanded = false;\n\n\t// Callback for resolving user input promise\n\tprivate onInputCallback?: (text: string) => void;\n\n\t// Pending clipboard images to attach to the next message\n\tprivate pendingImages: ClipboardImage[] = [];\n\n\t// Compaction state\n\tprivate contextWindow: number;\n\tprivate compactionSettings: CompactionSettings;\n\tprivate autoCompaction = true;\n\tprivate isCompacting = false;\n\tprivate compactionAbortController: AbortController | null = null;\n\n\tconstructor(agent: CodingAgent, options: InteractiveModeOptions) {\n\t\tthis.agent = agent;\n\t\tthis.options = options;\n\t\tthis.currentProvider = options.provider;\n\t\tthis.currentModelId = options.modelId;\n\t\tthis.contextWindow = options.contextWindow ?? DEFAULT_CONTEXT_WINDOW;\n\n\t\t// Initialize compaction settings from persisted settings if available\n\t\tconst savedCompaction = options.settingsManager?.getCompaction();\n\t\tthis.compactionSettings = {\n\t\t\t...DEFAULT_COMPACTION_SETTINGS,\n\t\t\t...(savedCompaction?.reserveTokens !== undefined && { reserveTokens: savedCompaction.reserveTokens }),\n\t\t\t...(savedCompaction?.keepRecentTokens !== undefined && { keepRecentTokens: savedCompaction.keepRecentTokens }),\n\t\t};\n\t\tthis.autoCompaction = options.settingsManager?.getCompactionEnabled() ?? true;\n\t}\n\n\tasync run(): Promise<void> {\n\t\tthis.initUI();\n\t\tthis.updateFooterTokens();\n\n\t\t// Show session picker immediately if --resume was passed\n\t\tif (this.options.resumeOnStart) {\n\t\t\tawait this.handleResume();\n\t\t}\n\n\t\t// Process initial messages\n\t\tconst { initialMessage, initialMessages = [] } = this.options;\n\n\t\tconst allInitial: string[] = [];\n\t\tif (initialMessage) allInitial.push(initialMessage);\n\t\tallInitial.push(...initialMessages);\n\n\t\tfor (const msg of allInitial) {\n\t\t\tthis.chatContainer.addChild(new UserMessageComponent(msg, getMarkdownTheme()));\n\t\t\tthis.ui.requestRender();\n\t\t\tawait this.streamPrompt(msg);\n\t\t}\n\n\t\t// Main interactive loop\n\t\twhile (true) {\n\t\t\tconst userInput = await this.getUserInput();\n\t\t\tawait this.handleUserInput(userInput);\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// UI Setup\n\t// ========================================================================\n\n\tprivate initUI(): void {\n\t\tconst { provider, modelId, skills = [], contextFiles = [], prompts = [], verbose, sessionManager } = this.options;\n\n\t\tthis.ui = new TUI(new ProcessTerminal());\n\n\t\t// Header\n\t\tthis.headerContainer = new Container();\n\t\tconst logo = chalk.bold(\"epi\") + chalk.dim(` - ${provider}/${modelId}`);\n\n\t\tconst hints = [\n\t\t\t`${chalk.dim(\"Escape\")} to abort`,\n\t\t\t`${chalk.dim(\"!\")} inline bash`,\n\t\t\t`${chalk.dim(\"Alt+Enter\")} follow-up while streaming`,\n\t\t\t`${chalk.dim(\"Ctrl+C\")} to exit`,\n\t\t\t`${chalk.dim(\"Ctrl+E\")} to expand tools`,\n\t\t\t`${chalk.dim(\"Ctrl+L\")} to switch model`,\n\t\t\t`${chalk.dim(\"Ctrl+V\")} to paste image`,\n\t\t\t`${chalk.dim(\"↑/↓\")} to browse history`,\n\t\t\t`${chalk.dim(\"@\")} for file references`,\n\t\t\t`${chalk.dim(\"/\")} for commands`,\n\t\t].join(\"\\n\");\n\n\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\tthis.headerContainer.addChild(new Text(`${logo}\\n${hints}`, 1, 0));\n\t\tthis.headerContainer.addChild(new Spacer(1));\n\n\t\tif (verbose && sessionManager?.getSessionFile()) {\n\t\t\tthis.headerContainer.addChild(new Text(chalk.dim(`Session: ${sessionManager.getSessionFile()}`), 1, 0));\n\t\t}\n\n\t\t// Show loaded context, skills, and prompts at startup\n\t\tthis.showLoadedResources(contextFiles, skills, prompts);\n\n\t\t// Chat area\n\t\tthis.chatContainer = new Container();\n\n\t\t// Pending messages (loading animations, status)\n\t\tthis.pendingContainer = new Container();\n\n\t\t// Pending steering/follow-up messages\n\t\tthis.pendingMessagesContainer = new Container();\n\n\t\t// Editor with slash command autocomplete\n\t\tthis.editorTheme = getEditorTheme();\n\t\tthis.editor = new Editor(this.ui, this.editorTheme);\n\t\tthis.editor.setAutocompleteProvider(this.buildAutocompleteProvider());\n\t\tthis.editorContainer = new Container();\n\t\tthis.editorContainer.addChild(this.editor);\n\n\t\t// Footer\n\t\tthis.footer = new FooterComponent(this.currentProvider, this.currentModelId);\n\t\tthis.footer.setAutoCompaction(this.autoCompaction);\n\t\tthis.footer.setSubscription(this.isSubscriptionProvider());\n\n\t\t// Assemble layout\n\t\tthis.ui.addChild(this.headerContainer);\n\t\tthis.ui.addChild(this.chatContainer);\n\t\tthis.ui.addChild(this.pendingMessagesContainer);\n\t\tthis.ui.addChild(this.pendingContainer);\n\t\tthis.ui.addChild(this.editorContainer);\n\t\tthis.ui.addChild(this.footer);\n\n\t\tthis.ui.setFocus(this.editor);\n\t\tthis.setupKeyHandlers();\n\n\t\tthis.ui.start();\n\t}\n\n\t// ========================================================================\n\t// Key Handlers\n\t// ========================================================================\n\n\tprivate setupKeyHandlers(): void {\n\t\tthis.editor.onChange = (text: string) => {\n\t\t\tconst wasBashMode = this.isBashMode;\n\t\t\tthis.isBashMode = text.trimStart().startsWith(\"!\");\n\t\t\tif (wasBashMode !== this.isBashMode) {\n\t\t\t\tthis.updateEditorBorderColor();\n\t\t\t}\n\t\t};\n\t\tthis.editor.onSubmit = (text: string) => {\n\t\t\ttext = text.trim();\n\t\t\tif (!text) return;\n\n\t\t\t// If agent is streaming, Enter becomes a steering message\n\t\t\tif (this.isStreaming) {\n\t\t\t\tthis.agent.steer({ role: \"user\", content: [{ type: \"text\", text }] });\n\t\t\t\tthis.steeringMessages.push(text);\n\t\t\t\tthis.editor.setText(\"\");\n\t\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.editor.addToHistory(text);\n\t\t\tthis.editor.setText(\"\");\n\n\t\t\tif (this.onInputCallback) {\n\t\t\t\tthis.onInputCallback(text);\n\t\t\t}\n\t\t};\n\n\t\tconst origHandleInput = this.editor.handleInput.bind(this.editor);\n\t\tthis.editor.handleInput = (data: string) => {\n\t\t\t// Escape: abort if agent is running or compacting\n\t\t\tif (matchesKey(data, Key.escape)) {\n\t\t\t\tif (this.isBashRunning && this.bashAbortController) {\n\t\t\t\t\tthis.bashAbortController.abort();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (this.isCompacting && this.compactionAbortController) {\n\t\t\t\t\tthis.compactionAbortController.abort();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (this.loadingAnimation) {\n\t\t\t\t\tthis.agent.abort();\n\t\t\t\t\tthis.stopLoading();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Ctrl+C: exit\n\t\t\tif (matchesKey(data, Key.ctrl(\"c\"))) {\n\t\t\t\tthis.shutdown();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Ctrl+D: exit if editor is empty\n\t\t\tif (matchesKey(data, Key.ctrl(\"d\"))) {\n\t\t\t\tif (this.editor.getText().length === 0) {\n\t\t\t\t\tthis.shutdown();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Ctrl+E: toggle tool output expansion\n\t\t\tif (matchesKey(data, Key.ctrl(\"e\"))) {\n\t\t\t\tthis.toggleToolExpansion();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Ctrl+L: select model\n\t\t\tif (matchesKey(data, Key.ctrl(\"l\"))) {\n\t\t\t\tthis.handleModelSelect();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Ctrl+V: paste image from clipboard\n\t\t\tif (matchesKey(data, Key.ctrl(\"v\"))) {\n\t\t\t\tthis.handleClipboardImagePaste();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Alt+Enter (Option+Enter on Mac): follow-up while streaming (or submit normally when idle)\n\t\t\tif (matchesKey(data, Key.alt(\"enter\"))) {\n\t\t\t\tconst text = this.editor.getText().trim();\n\t\t\t\tif (!text) return;\n\n\t\t\t\tif (this.isStreaming) {\n\t\t\t\t\tthis.followUpMessages.push(text);\n\t\t\t\t\tthis.editor.setText(\"\");\n\t\t\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Not streaming: treat like regular submit\n\t\t\t\tthis.editor.onSubmit?.(text);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Alt+Up (Option+Up on Mac): dequeue all queued messages back into the editor\n\t\t\tif (matchesKey(data, Key.alt(\"up\"))) {\n\t\t\t\tconst restored = this.clearAllQueues();\n\t\t\t\tif (restored.length > 0) {\n\t\t\t\t\tthis.editor.setText(restored.join(\"\\n\\n\"));\n\t\t\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\torigHandleInput(data);\n\t\t};\n\t}\n\n\t// ========================================================================\n\t// User Input\n\t// ========================================================================\n\n\tprivate getUserInput(): Promise<string> {\n\t\treturn new Promise((resolve) => {\n\t\t\tthis.onInputCallback = (text: string) => {\n\t\t\t\tthis.onInputCallback = undefined;\n\t\t\t\tresolve(text);\n\t\t\t};\n\t\t});\n\t}\n\n\tprivate async handleUserInput(input: string): Promise<void> {\n\t\t// Handle commands\n\t\tif (input === \"/help\") {\n\t\t\tthis.showHelp();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/skills\") {\n\t\t\tthis.showSkills();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/quit\" || input === \"/exit\") {\n\t\t\tthis.shutdown();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/model\") {\n\t\t\tawait this.handleModelSelect();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/login\") {\n\t\t\tawait this.handleLogin();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/logout\") {\n\t\t\tawait this.handleLogout();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/compact\" || input.startsWith(\"/compact \")) {\n\t\t\tconst customInstructions = input.startsWith(\"/compact \") ? input.slice(9).trim() : undefined;\n\t\t\tawait this.handleCompactCommand(customInstructions);\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/auto-compact\") {\n\t\t\tthis.toggleAutoCompaction();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input === \"/resume\") {\n\t\t\tawait this.handleResume();\n\t\t\treturn;\n\t\t}\n\n\t\tif (input.startsWith(\"/skill:\")) {\n\t\t\tconst skillName = input.slice(\"/skill:\".length).trim();\n\t\t\tawait this.handleSkillInvocation(skillName);\n\t\t\treturn;\n\t\t}\n\n\t\t// Handle bash commands (! for normal, !! for excluded from context)\n\t\tconst bashParsed = parseBashInput(input);\n\t\tif (bashParsed) {\n\t\t\tif (this.isBashRunning) {\n\t\t\t\tthis.showStatus(chalk.yellow(\"A bash command is already running. Press Escape to cancel it first.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait this.handleBashCommand(bashParsed.command, bashParsed.excludeFromContext);\n\t\t\tthis.isBashMode = false;\n\t\t\tthis.updateEditorBorderColor();\n\t\t\treturn;\n\t\t}\n\n\t\t// Try expanding prompt templates\n\t\tconst { prompts = [] } = this.options;\n\t\tconst expanded = expandPromptTemplate(input, prompts);\n\n\t\t// Capture and clear pending images\n\t\tconst images = this.pendingImages.length > 0 ? [...this.pendingImages] : undefined;\n\t\tthis.pendingImages = [];\n\n\t\t// Regular message (use expanded text if a prompt template was matched)\n\t\tconst imageLabel = images ? chalk.dim(` (${images.length} image${images.length > 1 ? \"s\" : \"\"})`) : \"\";\n\t\tthis.chatContainer.addChild(new UserMessageComponent(`${expanded}${imageLabel}`, getMarkdownTheme()));\n\t\tthis.ui.requestRender();\n\t\tawait this.streamPrompt(expanded, images);\n\t}\n\n\tprivate async handleBashCommand(command: string, excludeFromContext: boolean): Promise<void> {\n\t\tconst { sessionManager } = this.options;\n\n\t\tthis.bashAbortController = new AbortController();\n\t\tthis.isBashRunning = true;\n\n\t\tthis.bashComponent = new BashExecutionComponent(command, this.ui, excludeFromContext);\n\t\tif (this.toolOutputExpanded) {\n\t\t\tthis.bashComponent.setExpanded(true);\n\t\t}\n\t\tthis.chatContainer.addChild(this.bashComponent);\n\t\tthis.ui.requestRender();\n\n\t\ttry {\n\t\t\tconst result = await executeBashCommand(command, {\n\t\t\t\tsignal: this.bashAbortController.signal,\n\t\t\t\tonChunk: (chunk) => {\n\t\t\t\t\tthis.bashComponent?.appendOutput(chunk);\n\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tthis.bashComponent.setComplete(result.exitCode, result.cancelled, result.truncated, result.fullOutputPath);\n\t\t\tthis.ui.requestRender();\n\n\t\t\tif (!excludeFromContext) {\n\t\t\t\tconst msgText = `Ran \\`${command}\\`\\n\\n\\`\\`\\`\\n${result.output.trimEnd()}\\n\\`\\`\\``;\n\t\t\t\tconst userMsg: ModelMessage = { role: \"user\", content: [{ type: \"text\", text: msgText }] };\n\t\t\t\tthis.agent.setMessages([...this.agent.messages, userMsg]);\n\t\t\t\tsessionManager?.appendMessage(userMsg);\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.isBashRunning = false;\n\t\t\tthis.bashAbortController = null;\n\t\t\tthis.bashComponent = undefined;\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Autocomplete\n\t// ========================================================================\n\n\tprivate buildAutocompleteProvider(): CombinedAutocompleteProvider {\n\t\tconst { skills = [], prompts = [], fdPath } = this.options;\n\n\t\tconst commands: SlashCommand[] = [\n\t\t\t{ name: \"help\", description: \"Show available commands\" },\n\t\t\t{ name: \"resume\", description: \"Resume a previous session\" },\n\t\t\t{ name: \"compact\", description: \"Manually compact the session context\" },\n\t\t\t{ name: \"auto-compact\", description: \"Toggle automatic context compaction\" },\n\t\t\t{ name: \"login\", description: \"Login to an OAuth provider\" },\n\t\t\t{ name: \"logout\", description: \"Logout from an OAuth provider\" },\n\t\t\t{ name: \"skills\", description: \"List loaded skills\" },\n\t\t\t{ name: \"model\", description: \"Switch model (Ctrl+L)\" },\n\t\t\t{ name: \"quit\", description: \"Exit the CLI\" },\n\t\t\t{ name: \"exit\", description: \"Exit the CLI\" },\n\t\t];\n\n\t\tfor (const skill of skills) {\n\t\t\tcommands.push({\n\t\t\t\tname: `skill:${skill.name}`,\n\t\t\t\tdescription: skill.description,\n\t\t\t});\n\t\t}\n\n\t\t// Add prompt templates as slash commands\n\t\tfor (const prompt of prompts) {\n\t\t\tcommands.push({\n\t\t\t\tname: prompt.name,\n\t\t\t\tdescription: prompt.description,\n\t\t\t});\n\t\t}\n\n\t\treturn new CombinedAutocompleteProvider(commands, process.cwd(), fdPath ?? null);\n\t}\n\n\t// ========================================================================\n\t// Model Selection\n\t// ========================================================================\n\n\tprivate async handleModelSelect(): Promise<void> {\n\t\tconst latestModels = getLatestModels();\n\t\tconst modelOptions: { provider: string; modelId: string; label: string }[] = [];\n\t\tfor (const [provider, models] of Object.entries(latestModels)) {\n\t\t\tfor (const modelId of models) {\n\t\t\t\tmodelOptions.push({ provider, modelId, label: `${provider}/${modelId}` });\n\t\t\t}\n\t\t}\n\n\t\tconst items: SelectItem[] = modelOptions.map((m) => {\n\t\t\tconst current = m.provider === this.currentProvider && m.modelId === this.currentModelId;\n\t\t\treturn {\n\t\t\t\tvalue: `${m.provider}/${m.modelId}`,\n\t\t\t\tlabel: current ? `${m.label} (current)` : m.label,\n\t\t\t};\n\t\t});\n\n\t\tconst selected = await this.showSelectList(\"Switch model\", items);\n\t\tif (!selected) return;\n\n\t\tconst [newProvider, ...modelParts] = selected.split(\"/\");\n\t\tconst newModelId = modelParts.join(\"/\");\n\n\t\tif (newProvider === this.currentProvider && newModelId === this.currentModelId) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.showStatus(chalk.dim(`Switching to ${newProvider}/${newModelId}...`));\n\n\t\tif (!this.options.onModelChange) {\n\t\t\tthis.showStatus(chalk.yellow(\"Model switching is not available.\"));\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconst newAgent = await this.options.onModelChange(newProvider, newModelId);\n\t\t\t// Preserve conversation history\n\t\t\tnewAgent.setMessages([...this.agent.messages]);\n\t\t\tthis.agent = newAgent;\n\n\t\t\tthis.currentProvider = newProvider;\n\t\t\tthis.currentModelId = newModelId;\n\t\t\tthis.updateFooter();\n\n\t\t\t// Persist the choice for next startup\n\t\t\tthis.options.settingsManager?.setDefaults(newProvider, newModelId);\n\n\t\t\tthis.showStatus(chalk.green(`Switched to ${newProvider}/${newModelId}`));\n\t\t} catch (error) {\n\t\t\tconst msg = error instanceof Error ? error.message : String(error);\n\t\t\tthis.showStatus(chalk.red(`Failed to switch model: ${msg}`));\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Streaming\n\t// ========================================================================\n\n\tprivate async streamPrompt(prompt: string, images?: ClipboardImage[]): Promise<void> {\n\t\tthis.isStreaming = true;\n\t\tthis.updatePendingMessagesDisplay();\n\t\tconst { sessionManager } = this.options;\n\t\tconst messagesBefore = this.agent.messages.length;\n\n\t\t// Build image parts from clipboard images\n\t\tconst imageParts: ImagePart[] = (images ?? []).map((img) => ({\n\t\t\ttype: \"image\" as const,\n\t\t\timage: Buffer.from(img.bytes).toString(\"base64\"),\n\t\t\tmediaType: img.mimeType,\n\t\t}));\n\n\t\t// Start loading animation\n\t\tthis.startLoading();\n\n\t\tthis.streamingComponent = undefined;\n\t\tthis.streamingText = \"\";\n\t\tthis.hadToolResults = false;\n\n\t\tlet errorDisplayed = false;\n\t\tlet streamFailed = false;\n\t\ttry {\n\t\t\tconst result =\n\t\t\t\timageParts.length > 0\n\t\t\t\t\t? await this.agent.stream({\n\t\t\t\t\t\t\tmessages: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\trole: \"user\" as const,\n\t\t\t\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: prompt }, ...imageParts],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t})\n\t\t\t\t\t: await this.agent.stream({ prompt });\n\n\t\t\tfor await (const part of result.fullStream) {\n\t\t\t\tswitch (part.type) {\n\t\t\t\t\tcase \"text-delta\":\n\t\t\t\t\t\t// After tool results, or for the very first text part, start a new assistant message component\n\t\t\t\t\t\t// so each agent step gets its own message bubble\n\t\t\t\t\t\tif (this.hadToolResults || !this.streamingComponent) {\n\t\t\t\t\t\t\tthis.streamingComponent = new AssistantMessageComponent(getMarkdownTheme());\n\t\t\t\t\t\t\tthis.streamingText = \"\";\n\t\t\t\t\t\t\tthis.hadToolResults = false;\n\t\t\t\t\t\t\tthis.chatContainer.addChild(this.streamingComponent);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis.streamingText += part.text;\n\t\t\t\t\t\tthis.streamingComponent!.updateText(this.streamingText);\n\t\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase \"tool-call\": {\n\t\t\t\t\t\tconst args =\n\t\t\t\t\t\t\ttypeof part.input === \"object\" && part.input !== null\n\t\t\t\t\t\t\t\t? (part.input as Record<string, unknown>)\n\t\t\t\t\t\t\t\t: {};\n\t\t\t\t\t\tconst toolComponent = new ToolExecutionComponent(part.toolName, args);\n\n\t\t\t\t\t\tif (this.toolOutputExpanded) {\n\t\t\t\t\t\t\ttoolComponent.setExpanded(true);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthis.pendingTools.set(part.toolCallId, toolComponent);\n\t\t\t\t\t\tthis.chatContainer.addChild(toolComponent);\n\t\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase \"tool-result\": {\n\t\t\t\t\t\tconst toolComponent = this.pendingTools.get(part.toolCallId);\n\t\t\t\t\t\tif (toolComponent) {\n\t\t\t\t\t\t\tconst toolOutput = extractToolOutput(part.output);\n\t\t\t\t\t\t\ttoolComponent.updateResult(toolOutput, /* isError */ false, /* isPartial */ false);\n\t\t\t\t\t\t\tthis.pendingTools.delete(part.toolCallId);\n\t\t\t\t\t\t\tthis.hadToolResults = true;\n\t\t\t\t\t\t\tthis.ui.requestRender();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase \"error\": {\n\t\t\t\t\t\tconst errorMessage =\n\t\t\t\t\t\t\t(part.error as any)?.message ??\n\t\t\t\t\t\t\t(typeof part.error === \"object\" && part.error !== null\n\t\t\t\t\t\t\t\t? JSON.stringify(part.error)\n\t\t\t\t\t\t\t\t: String(part.error));\n\t\t\t\t\t\tif (this.streamingComponent) {\n\t\t\t\t\t\t\tthis.streamingComponent.setError(errorMessage);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.showStatus(chalk.red(`Error: ${errorMessage}`));\n\t\t\t\t\t\t}\n\t\t\t\t\t\terrorDisplayed = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (errorDisplayed) return;\n\n\t\t\t// Get final response and update messages\n\t\t\tconst response = await result.response;\n\t\t\tconst responseMessages = response.messages as ModelMessage[];\n\t\t\tthis.agent.setMessages([\n\t\t\t\t...this.agent.messages.slice(0, messagesBefore),\n\t\t\t\t...buildUserMessage(prompt, imageParts),\n\t\t\t\t...responseMessages,\n\t\t\t]);\n\n\t\t\t// Save to session\n\t\t\tif (sessionManager) {\n\t\t\t\tconst userMsg: ModelMessage = {\n\t\t\t\t\trole: \"user\",\n\t\t\t\t\tcontent: [{ type: \"text\", text: prompt }, ...imageParts],\n\t\t\t\t};\n\t\t\t\tsessionManager.appendMessage(userMsg);\n\t\t\t\tfor (const msg of responseMessages) {\n\t\t\t\t\tsessionManager.appendMessage(msg);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Update footer token stats\n\t\t\tthis.updateFooterTokens();\n\n\t\t\t// Check for auto-compaction after successful response\n\t\t\tawait this.checkAutoCompaction();\n\t\t} catch (error) {\n\t\t\tif (errorDisplayed) {\n\t\t\t\tstreamFailed = true;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tstreamFailed = true;\n\t\t\tif ((error as Error).name === \"AbortError\") {\n\t\t\t\tif (this.streamingComponent) {\n\t\t\t\t\tthis.streamingComponent.setAborted();\n\t\t\t\t} else {\n\t\t\t\t\tthis.showStatus(chalk.dim(\"[aborted]\"));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst msg =\n\t\t\t\t\terror instanceof Error\n\t\t\t\t\t\t? error.message\n\t\t\t\t\t\t: typeof error === \"object\" && error !== null\n\t\t\t\t\t\t\t? JSON.stringify(error)\n\t\t\t\t\t\t\t: String(error);\n\t\t\t\tif (this.streamingComponent) {\n\t\t\t\t\tthis.streamingComponent.setError(msg);\n\t\t\t\t} else {\n\t\t\t\t\tthis.showStatus(chalk.red(`Error: ${msg}`));\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.stopLoading();\n\t\t\tthis.streamingComponent = undefined;\n\t\t\tthis.streamingText = \"\";\n\t\t\tthis.hadToolResults = false;\n\t\t\tthis.pendingTools.clear();\n\t\t\tthis.isStreaming = false;\n\t\t\tthis.steeringMessages = [];\n\t\t\tif (streamFailed) {\n\t\t\t\tthis.followUpMessages = [];\n\t\t\t}\n\t\t\tthis.updatePendingMessagesDisplay();\n\t\t\tthis.ui.requestRender();\n\t\t}\n\n\t\t// Process queued follow-ups (skipped if stream failed/aborted)\n\t\twhile (this.followUpMessages.length > 0) {\n\t\t\tconst next = this.followUpMessages.shift();\n\t\t\tif (!next) break;\n\n\t\t\tthis.updatePendingMessagesDisplay();\n\n\t\t\tthis.chatContainer.addChild(new UserMessageComponent(next, getMarkdownTheme()));\n\t\t\tthis.ui.requestRender();\n\t\t\tawait this.streamPrompt(next);\n\t\t}\n\t}\n\n\tprivate updatePendingMessagesDisplay(): void {\n\t\tthis.pendingMessagesContainer.clear();\n\n\t\t// If no agent is running, clear pending messages (they've been consumed)\n\t\tif (!this.isStreaming && this.followUpMessages.length === 0 && this.steeringMessages.length === 0) {\n\t\t\tthis.ui.requestRender();\n\t\t\treturn;\n\t\t}\n\n\t\tconst lines = formatPendingMessages(this.steeringMessages, this.followUpMessages);\n\n\t\tif (lines.length === 0) {\n\t\t\tthis.ui.requestRender();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.pendingMessagesContainer.addChild(new Spacer(1));\n\t\tthis.pendingMessagesContainer.addChild(new Text(lines.map((l) => chalk.dim(l)).join(\"\\n\"), 1, 0));\n\t\tthis.pendingMessagesContainer.addChild(new Text(chalk.dim(\"↳ Alt+Up to edit queued messages\"), 1, 0));\n\t\tthis.pendingMessagesContainer.addChild(new Spacer(1));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate clearAllQueues(): string[] {\n\t\tconst restored = [...this.steeringMessages, ...this.followUpMessages];\n\t\tthis.steeringMessages = [];\n\t\tthis.followUpMessages = [];\n\t\treturn restored;\n\t}\n\n\t// ========================================================================\n\t// Loading Animation\n\t// ========================================================================\n\n\tprivate startLoading(): void {\n\t\tthis.stopLoading();\n\t\tthis.loadingAnimation = new Loader(\n\t\t\tthis.ui,\n\t\t\t(s: string) => chalk.cyan(s),\n\t\t\t(s: string) => chalk.dim(s),\n\t\t\t\"Working...\",\n\t\t);\n\t\tthis.loadingAnimation.start();\n\t\tthis.pendingContainer.addChild(new Spacer(1));\n\t\tthis.pendingContainer.addChild(this.loadingAnimation);\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate stopLoading(): void {\n\t\tif (this.loadingAnimation) {\n\t\t\tthis.loadingAnimation.stop();\n\t\t\tthis.pendingContainer.clear();\n\t\t\tthis.loadingAnimation = undefined;\n\t\t\tthis.ui.requestRender();\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Tool Expansion\n\t// ========================================================================\n\n\tprivate toggleToolExpansion(): void {\n\t\tthis.toolOutputExpanded = !this.toolOutputExpanded;\n\n\t\t// Update all tool components and compaction components in the chat\n\t\tfor (const child of this.chatContainer.children) {\n\t\t\tif (child instanceof ToolExecutionComponent) {\n\t\t\t\tchild.setExpanded(this.toolOutputExpanded);\n\t\t\t} else if (child instanceof CompactionSummaryComponent) {\n\t\t\t\tchild.setExpanded(this.toolOutputExpanded);\n\t\t\t} else if (child instanceof BashExecutionComponent) {\n\t\t\t\tchild.setExpanded(this.toolOutputExpanded);\n\t\t\t}\n\t\t}\n\n\t\tthis.ui.requestRender();\n\t}\n\n\t// ========================================================================\n\t// Clipboard Image Paste\n\t// ========================================================================\n\n\tprivate handleClipboardImagePaste(): void {\n\t\ttry {\n\t\t\tconst image = readClipboardImage();\n\t\t\tif (!image) return;\n\t\t\tthis.pendingImages.push(image);\n\t\t\tconst ext = extensionForImageMimeType(image.mimeType) ?? \"image\";\n\t\t\tconst label = `[image ${this.pendingImages.length}: ${ext}]`;\n\t\t\tthis.editor.insertTextAtCursor(label);\n\t\t\tthis.ui.requestRender();\n\t\t} catch {\n\t\t\t// Silently ignore clipboard errors (may not have permission, etc.)\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Compaction\n\t// ========================================================================\n\n\t/**\n\t * Handle the /compact command.\n\t */\n\tprivate async handleCompactCommand(_customInstructions?: string): Promise<void> {\n\t\tconst messages = this.agent.messages;\n\t\tif (messages.length < 2) {\n\t\t\tthis.showStatus(chalk.yellow(\"Nothing to compact (not enough messages).\"));\n\t\t\treturn;\n\t\t}\n\n\t\tawait this.executeCompaction(false);\n\t}\n\n\t/**\n\t * Toggle auto-compaction on/off.\n\t */\n\tprivate toggleAutoCompaction(): void {\n\t\tthis.autoCompaction = !this.autoCompaction;\n\t\tthis.footer.setAutoCompaction(this.autoCompaction);\n\t\tthis.options.settingsManager?.setCompactionEnabled(this.autoCompaction);\n\t\tthis.showStatus(\n\t\t\tthis.autoCompaction ? chalk.green(\"Auto-compaction enabled\") : chalk.dim(\"Auto-compaction disabled\"),\n\t\t);\n\t\tthis.ui.requestRender();\n\t}\n\n\t/**\n\t * Check if auto-compaction should trigger after an agent response.\n\t */\n\tprivate async checkAutoCompaction(): Promise<void> {\n\t\tif (!this.autoCompaction) return;\n\n\t\tconst contextTokens = estimateContextTokens([...this.agent.messages]);\n\t\tif (!shouldCompact(contextTokens, this.contextWindow, this.compactionSettings)) return;\n\n\t\tawait this.executeCompaction(true);\n\t}\n\n\t/**\n\t * Execute compaction (used by both manual /compact and auto mode).\n\t */\n\tprivate async executeCompaction(isAuto: boolean): Promise<CompactionResult | undefined> {\n\t\tif (this.isCompacting) return undefined;\n\n\t\tconst { sessionManager } = this.options;\n\n\t\t// Build path entries from session if available, otherwise from agent messages\n\t\tconst pathEntries = sessionManager ? sessionManager.getBranch() : this.buildSessionEntriesFromMessages();\n\n\t\tif (pathEntries.length < 2) {\n\t\t\tif (!isAuto) {\n\t\t\t\tthis.showStatus(chalk.yellow(\"Nothing to compact (not enough messages).\"));\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\n\t\t// Prepare compaction\n\t\tconst preparation = prepareCompaction(pathEntries, this.compactionSettings);\n\t\tif (!preparation) {\n\t\t\tif (!isAuto) {\n\t\t\t\tthis.showStatus(chalk.yellow(\"Nothing to compact (already compacted or insufficient history).\"));\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (preparation.messagesToSummarize.length === 0) {\n\t\t\tif (!isAuto) {\n\t\t\t\tthis.showStatus(chalk.yellow(\"Nothing to compact (no messages to summarize).\"));\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\n\t\tthis.isCompacting = true;\n\t\tthis.compactionAbortController = new AbortController();\n\n\t\t// Show compaction indicator\n\t\tconst label = isAuto\n\t\t\t? \"Auto-compacting context... (Escape to cancel)\"\n\t\t\t: \"Compacting context... (Escape to cancel)\";\n\t\tconst compactingLoader = new Loader(\n\t\t\tthis.ui,\n\t\t\t(s: string) => chalk.cyan(s),\n\t\t\t(s: string) => chalk.dim(s),\n\t\t\tlabel,\n\t\t);\n\t\tcompactingLoader.start();\n\t\tthis.pendingContainer.clear();\n\t\tthis.pendingContainer.addChild(new Spacer(1));\n\t\tthis.pendingContainer.addChild(compactingLoader);\n\t\tthis.ui.requestRender();\n\n\t\tlet result: CompactionResult | undefined;\n\n\t\ttry {\n\t\t\t// We need a LanguageModel for summarization. Use the agent's model\n\t\t\t// by extracting it from the config. The model is accessible through\n\t\t\t// the onModelChange callback pattern, but for simplicity we create\n\t\t\t// a model via the same factory used at startup.\n\t\t\tconst { model } = await this.getCompactionModel();\n\n\t\t\tresult = await compact(preparation, model, this.compactionAbortController.signal);\n\n\t\t\t// Record compaction in session\n\t\t\tif (sessionManager) {\n\t\t\t\tsessionManager.appendCompaction(\n\t\t\t\t\tresult.summary,\n\t\t\t\t\tresult.firstKeptEntryId,\n\t\t\t\t\tresult.tokensBefore,\n\t\t\t\t\tresult.details,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Rebuild agent messages from the session context\n\t\t\tif (sessionManager) {\n\t\t\t\tconst context = sessionManager.buildSessionContext();\n\t\t\t\tthis.agent.setMessages(context.messages);\n\t\t\t}\n\n\t\t\t// Rebuild the chat UI\n\t\t\tthis.rebuildChatFromSession();\n\n\t\t\t// Add compaction summary component so user sees it\n\t\t\tconst summaryComponent = new CompactionSummaryComponent(result.tokensBefore, result.summary);\n\t\t\tif (this.toolOutputExpanded) {\n\t\t\t\tsummaryComponent.setExpanded(true);\n\t\t\t}\n\t\t\tthis.chatContainer.addChild(summaryComponent);\n\n\t\t\t// Update footer tokens\n\t\t\tthis.updateFooterTokens();\n\n\t\t\tif (this.options.verbose) {\n\t\t\t\tconst tokensAfter = estimateContextTokens([...this.agent.messages]);\n\t\t\t\tthis.showStatus(\n\t\t\t\t\tchalk.dim(\n\t\t\t\t\t\t`Compacted: ${result.tokensBefore.toLocaleString()} -> ${tokensAfter.toLocaleString()} tokens`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tif (\n\t\t\t\tthis.compactionAbortController.signal.aborted ||\n\t\t\t\tmessage === \"Compaction cancelled\" ||\n\t\t\t\t(error instanceof Error && error.name === \"AbortError\")\n\t\t\t) {\n\t\t\t\tthis.showStatus(chalk.dim(\"Compaction cancelled.\"));\n\t\t\t} else {\n\t\t\t\tthis.showStatus(chalk.red(`Compaction failed: ${message}`));\n\t\t\t}\n\t\t} finally {\n\t\t\tcompactingLoader.stop();\n\t\t\tthis.pendingContainer.clear();\n\t\t\tthis.isCompacting = false;\n\t\t\tthis.compactionAbortController = null;\n\t\t\tthis.ui.requestRender();\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Get the language model for compaction summarization.\n\t * Uses the same model creation path as the main agent.\n\t */\n\tprivate async getCompactionModel(): Promise<{ model: import(\"ai\").LanguageModel }> {\n\t\tconst { createModel } = await import(\"../../model-factory.js\");\n\t\treturn createModel({\n\t\t\tprovider: this.currentProvider,\n\t\t\tmodel: this.currentModelId,\n\t\t\tauthStorage: this.options.authStorage,\n\t\t});\n\t}\n\n\t/**\n\t * Build session entries from agent messages (when no session manager).\n\t * Creates synthetic SessionEntry objects for the compaction algorithm.\n\t */\n\tprivate buildSessionEntriesFromMessages(): import(\"edge-pi\").SessionEntry[] {\n\t\tconst messages = this.agent.messages;\n\t\tconst entries: import(\"edge-pi\").SessionEntry[] = [];\n\t\tlet parentId: string | null = null;\n\n\t\tfor (let i = 0; i < messages.length; i++) {\n\t\t\tconst id = `msg-${i}`;\n\t\t\tentries.push({\n\t\t\t\ttype: \"message\",\n\t\t\t\tid,\n\t\t\t\tparentId,\n\t\t\t\ttimestamp: new Date().toISOString(),\n\t\t\t\tmessage: messages[i],\n\t\t\t});\n\t\t\tparentId = id;\n\t\t}\n\n\t\treturn entries;\n\t}\n\n\t/**\n\t * Rebuild the chat UI from session context after compaction.\n\t */\n\tprivate rebuildChatFromSession(): void {\n\t\tthis.chatContainer.clear();\n\n\t\tconst messages = this.agent.messages;\n\t\tfor (const msg of messages) {\n\t\t\tif (msg.role === \"user\") {\n\t\t\t\t// Check if this is a compaction summary\n\t\t\t\tconst content = msg.content;\n\t\t\t\tif (Array.isArray(content) && content.length > 0) {\n\t\t\t\t\tconst textBlock = content[0] as { type: string; text?: string };\n\t\t\t\t\tif (textBlock.type === \"text\" && textBlock.text?.startsWith('<summary type=\"compaction\"')) {\n\t\t\t\t\t\t// Skip compaction summaries in rebuild (they are injected by buildSessionContext)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (textBlock.type === \"text\" && textBlock.text?.startsWith('<summary type=\"branch\"')) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst text = extractTextFromMessage(msg);\n\t\t\t\tif (text) {\n\t\t\t\t\tthis.chatContainer.addChild(new UserMessageComponent(text, getMarkdownTheme()));\n\t\t\t\t}\n\t\t\t} else if (msg.role === \"assistant\") {\n\t\t\t\tconst assistantMsg = msg as import(\"edge-pi\").AssistantModelMessage;\n\t\t\t\tconst textParts: string[] = [];\n\t\t\t\tfor (const block of assistantMsg.content) {\n\t\t\t\t\tconst b = block as {\n\t\t\t\t\t\ttype: string;\n\t\t\t\t\t\ttext?: string;\n\t\t\t\t\t\ttoolName?: string;\n\t\t\t\t\t\tinput?: unknown;\n\t\t\t\t\t\ttoolCallId?: string;\n\t\t\t\t\t};\n\t\t\t\t\tif (b.type === \"text\" && b.text) {\n\t\t\t\t\t\ttextParts.push(b.text);\n\t\t\t\t\t} else if (b.type === \"tool-call\" && b.toolName) {\n\t\t\t\t\t\tconst args =\n\t\t\t\t\t\t\ttypeof b.input === \"object\" && b.input !== null ? (b.input as Record<string, unknown>) : {};\n\t\t\t\t\t\tconst toolComp = new ToolExecutionComponent(b.toolName, args);\n\t\t\t\t\t\tif (this.toolOutputExpanded) {\n\t\t\t\t\t\t\ttoolComp.setExpanded(true);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Mark as completed (we don't have the result here, just show collapsed)\n\t\t\t\t\t\ttoolComp.updateResult({ text: \"(from history)\" }, false, false);\n\t\t\t\t\t\tthis.chatContainer.addChild(toolComp);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (textParts.length > 0) {\n\t\t\t\t\tconst comp = new AssistantMessageComponent(getMarkdownTheme());\n\t\t\t\t\tcomp.updateText(textParts.join(\"\"));\n\t\t\t\t\tthis.chatContainer.addChild(comp);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Skip tool messages in UI rebuild - they are consumed by tool-call components\n\t\t}\n\n\t\tthis.ui.requestRender();\n\t}\n\n\t// ========================================================================\n\t// Footer Token Tracking\n\t// ========================================================================\n\n\t/**\n\t * Update the footer with current token count information.\n\t */\n\tprivate updateFooterTokens(): void {\n\t\tconst contextTokens = estimateContextTokens([...this.agent.messages]);\n\t\tthis.footer.setTokenInfo(contextTokens, this.contextWindow);\n\t\tthis.footer.setAutoCompaction(this.autoCompaction);\n\t\tthis.ui?.requestRender();\n\t}\n\n\t/**\n\t * Check if the current provider is using an OAuth subscription credential.\n\t */\n\tprivate isSubscriptionProvider(): boolean {\n\t\tconst { authStorage } = this.options;\n\t\tif (!authStorage) return false;\n\t\tconst cred = authStorage.get(this.currentProvider);\n\t\treturn cred?.type === \"oauth\";\n\t}\n\n\t/**\n\t * Replace the footer component and update token info.\n\t */\n\tprivate updateFooter(): void {\n\t\tthis.footer = new FooterComponent(this.currentProvider, this.currentModelId);\n\t\tthis.footer.setSubscription(this.isSubscriptionProvider());\n\t\tthis.updateFooterTokens();\n\n\t\t// Replace footer in UI\n\t\tconst children = this.ui.children;\n\t\tchildren[children.length - 1] = this.footer;\n\t\tthis.ui.requestRender();\n\t}\n\n\t// ========================================================================\n\t// Startup Resource Display\n\t// ========================================================================\n\n\tprivate formatDisplayPath(p: string): string {\n\t\tconst home = process.env.HOME || process.env.USERPROFILE || \"\";\n\t\tif (home && p.startsWith(home)) {\n\t\t\treturn `~${p.slice(home.length)}`;\n\t\t}\n\t\treturn p;\n\t}\n\n\tprivate showLoadedResources(contextFiles: ContextFile[], skills: Skill[], prompts: PromptTemplate[]): void {\n\t\tconst sectionHeader = (name: string) => chalk.cyan(`[${name}]`);\n\n\t\tif (contextFiles.length > 0) {\n\t\t\tconst contextList = contextFiles.map((f) => chalk.dim(` ${this.formatDisplayPath(f.path)}`)).join(\"\\n\");\n\t\t\tthis.headerContainer.addChild(new Text(`${sectionHeader(\"Context\")}\\n${contextList}`, 0, 0));\n\t\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\t}\n\n\t\tif (skills.length > 0) {\n\t\t\tconst skillList = skills.map((s) => chalk.dim(` ${this.formatDisplayPath(s.filePath)}`)).join(\"\\n\");\n\t\t\tthis.headerContainer.addChild(new Text(`${sectionHeader(\"Skills\")}\\n${skillList}`, 0, 0));\n\t\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\t}\n\n\t\tif (prompts.length > 0) {\n\t\t\tconst promptList = prompts\n\t\t\t\t.map((p) => {\n\t\t\t\t\tconst sourceLabel = chalk.cyan(p.source);\n\t\t\t\t\treturn chalk.dim(` ${sourceLabel} /${p.name}`);\n\t\t\t\t})\n\t\t\t\t.join(\"\\n\");\n\t\t\tthis.headerContainer.addChild(new Text(`${sectionHeader(\"Prompts\")}\\n${promptList}`, 0, 0));\n\t\t\tthis.headerContainer.addChild(new Spacer(1));\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Commands\n\t// ========================================================================\n\n\tprivate showHelp(): void {\n\t\tconst helpText = [\n\t\t\tchalk.bold(\"Commands:\"),\n\t\t\t\" !<command> Run inline bash and include output in context\",\n\t\t\t\" !!<command> Run inline bash but exclude output from context\",\n\t\t\t\" /resume Resume a previous session\",\n\t\t\t\" /compact [text] Compact the session context (optional instructions)\",\n\t\t\t\" /auto-compact Toggle automatic context compaction\",\n\t\t\t\" /model Switch model (Ctrl+L)\",\n\t\t\t\" /login Login to an OAuth provider\",\n\t\t\t\" /logout Logout from an OAuth provider\",\n\t\t\t\" /skills List loaded skills\",\n\t\t\t\" /skill:<name> Invoke a skill by name\",\n\t\t\t\" /quit, /exit Exit the CLI\",\n\t\t].join(\"\\n\");\n\n\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\tthis.chatContainer.addChild(new Text(helpText, 1, 0));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate showSkills(): void {\n\t\tconst { skills = [] } = this.options;\n\n\t\tif (skills.length === 0) {\n\t\t\tthis.showStatus(chalk.dim(\"No skills loaded.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst lines: string[] = [];\n\t\tfor (const skill of skills) {\n\t\t\tconst hidden = skill.disableModelInvocation ? chalk.dim(\" (hidden from model)\") : \"\";\n\t\t\tlines.push(` ${chalk.bold(skill.name)}${hidden}`);\n\t\t\tlines.push(chalk.dim(` ${skill.description}`));\n\t\t\tlines.push(chalk.dim(` ${skill.filePath}`));\n\t\t}\n\n\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\tthis.chatContainer.addChild(new Text(lines.join(\"\\n\"), 1, 0));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate async handleSkillInvocation(skillName: string): Promise<void> {\n\t\tconst { skills = [] } = this.options;\n\t\tconst skill = skills.find((s) => s.name === skillName);\n\n\t\tif (!skill) {\n\t\t\tthis.showStatus(chalk.red(`Skill \"${skillName}\" not found.`));\n\t\t\treturn;\n\t\t}\n\n\t\tconst skillPrompt = `Please read and follow the instructions in the skill file: ${skill.filePath}`;\n\t\tthis.chatContainer.addChild(new UserMessageComponent(skillPrompt, getMarkdownTheme()));\n\t\tthis.ui.requestRender();\n\t\tawait this.streamPrompt(skillPrompt);\n\t}\n\n\t// ========================================================================\n\t// OAuth Login/Logout\n\t// ========================================================================\n\n\tprivate async handleLogin(): Promise<void> {\n\t\tconst { authStorage } = this.options;\n\t\tif (!authStorage) {\n\t\t\tthis.showStatus(chalk.red(\"Auth storage not available.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst providers = authStorage.getProviders();\n\t\tif (providers.length === 0) {\n\t\t\tthis.showStatus(chalk.dim(\"No OAuth providers registered.\"));\n\t\t\treturn;\n\t\t}\n\n\t\t// Use SelectList overlay for provider selection\n\t\tconst items: SelectItem[] = providers.map((p) => {\n\t\t\tconst loggedIn = authStorage.get(p.id)?.type === \"oauth\" ? \" (logged in)\" : \"\";\n\t\t\treturn { value: p.id, label: `${p.name}${loggedIn}` };\n\t\t});\n\n\t\tconst selected = await this.showSelectList(\"Login to OAuth provider\", items);\n\t\tif (!selected) return;\n\n\t\tconst provider = providers.find((p) => p.id === selected);\n\t\tif (!provider) return;\n\n\t\tthis.showStatus(chalk.dim(`Logging in to ${provider.name}...`));\n\n\t\ttry {\n\t\t\tawait authStorage.login(provider.id, {\n\t\t\t\tonAuth: (info) => {\n\t\t\t\t\tconst lines = [chalk.bold(\"Open this URL in your browser:\"), chalk.cyan(info.url)];\n\t\t\t\t\tif (info.instructions) {\n\t\t\t\t\t\tlines.push(chalk.dim(info.instructions));\n\t\t\t\t\t}\n\t\t\t\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\t\t\t\tthis.chatContainer.addChild(new Text(lines.join(\"\\n\"), 1, 0));\n\t\t\t\t\tthis.ui.requestRender();\n\n\t\t\t\t\t// Try to open browser\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst { execSync } = require(\"node:child_process\") as typeof import(\"node:child_process\");\n\t\t\t\t\t\tconst platform = process.platform;\n\t\t\t\t\t\tif (platform === \"darwin\") {\n\t\t\t\t\t\t\texecSync(`open \"${info.url}\"`, { stdio: \"ignore\" });\n\t\t\t\t\t\t} else if (platform === \"linux\") {\n\t\t\t\t\t\t\texecSync(`xdg-open \"${info.url}\" 2>/dev/null || sensible-browser \"${info.url}\" 2>/dev/null`, {\n\t\t\t\t\t\t\t\tstdio: \"ignore\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t} else if (platform === \"win32\") {\n\t\t\t\t\t\t\texecSync(`start \"\" \"${info.url}\"`, { stdio: \"ignore\" });\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Silently fail - user can open manually\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tonPrompt: async (promptInfo) => {\n\t\t\t\t\t// Show prompt message and wait for user input\n\t\t\t\t\tthis.showStatus(chalk.dim(promptInfo.message));\n\t\t\t\t\tconst answer = await this.getUserInput();\n\t\t\t\t\treturn answer.trim();\n\t\t\t\t},\n\t\t\t\tonProgress: (message) => {\n\t\t\t\t\tthis.showStatus(chalk.dim(message));\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tthis.footer.setSubscription(this.isSubscriptionProvider());\n\t\t\tthis.ui.requestRender();\n\t\t\tthis.showStatus(chalk.green(`Logged in to ${provider.name}. Credentials saved.`));\n\t\t} catch (error) {\n\t\t\tconst msg = error instanceof Error ? error.message : String(error);\n\t\t\tif (msg !== \"Login cancelled\") {\n\t\t\t\tthis.showStatus(chalk.red(`Login failed: ${msg}`));\n\t\t\t} else {\n\t\t\t\tthis.showStatus(chalk.dim(\"Login cancelled.\"));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async handleLogout(): Promise<void> {\n\t\tconst { authStorage } = this.options;\n\t\tif (!authStorage) {\n\t\t\tthis.showStatus(chalk.red(\"Auth storage not available.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst loggedIn = authStorage\n\t\t\t.list()\n\t\t\t.filter((id) => authStorage.get(id)?.type === \"oauth\")\n\t\t\t.map((id) => {\n\t\t\t\tconst provider = authStorage.getProvider(id);\n\t\t\t\treturn { id, name: provider?.name ?? id };\n\t\t\t});\n\n\t\tif (loggedIn.length === 0) {\n\t\t\tthis.showStatus(chalk.dim(\"No OAuth providers logged in. Use /login first.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst items: SelectItem[] = loggedIn.map((p) => ({\n\t\t\tvalue: p.id,\n\t\t\tlabel: p.name,\n\t\t}));\n\n\t\tconst selected = await this.showSelectList(\"Logout from OAuth provider\", items);\n\t\tif (!selected) return;\n\n\t\tconst entry = loggedIn.find((p) => p.id === selected);\n\t\tif (!entry) return;\n\n\t\tauthStorage.logout(entry.id);\n\t\tthis.showStatus(chalk.green(`Logged out of ${entry.name}.`));\n\t}\n\n\t// ========================================================================\n\t// Resume Session\n\t// ========================================================================\n\n\t/**\n\t * List session files from the session directory, sorted by modification time (newest first).\n\t * Returns metadata for each session including the first user message as a preview.\n\t */\n\tprivate listAvailableSessions(): { path: string; mtime: number; preview: string; timestamp: string }[] {\n\t\tconst { sessionDir } = this.options;\n\t\tif (!sessionDir || !existsSync(sessionDir)) return [];\n\n\t\ttry {\n\t\t\tconst files = readdirSync(sessionDir)\n\t\t\t\t.filter((f: string) => f.endsWith(\".jsonl\"))\n\t\t\t\t.map((f: string) => {\n\t\t\t\t\tconst filePath = join(sessionDir, f);\n\t\t\t\t\tconst mtime = statSync(filePath).mtime.getTime();\n\t\t\t\t\treturn { name: f, path: filePath, mtime };\n\t\t\t\t})\n\t\t\t\t.sort((a, b) => b.mtime - a.mtime);\n\n\t\t\tconst sessions: { path: string; mtime: number; preview: string; timestamp: string }[] = [];\n\t\t\tfor (const file of files) {\n\t\t\t\t// Skip the current session file\n\t\t\t\tif (this.options.sessionManager?.getSessionFile() === file.path) continue;\n\n\t\t\t\tconst preview = this.getSessionPreview(file.path);\n\t\t\t\tconst timestamp = new Date(file.mtime).toLocaleString();\n\t\t\t\tsessions.push({ path: file.path, mtime: file.mtime, preview, timestamp });\n\t\t\t}\n\t\t\treturn sessions;\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Extract the first user message from a session file for preview.\n\t */\n\tprivate getSessionPreview(filePath: string): string {\n\t\ttry {\n\t\t\tconst content = readFileSync(filePath, \"utf-8\");\n\t\t\tconst lines = content.trim().split(\"\\n\");\n\t\t\tfor (const line of lines) {\n\t\t\t\tif (!line.trim()) continue;\n\t\t\t\ttry {\n\t\t\t\t\tconst entry = JSON.parse(line);\n\t\t\t\t\tif (entry.type === \"message\" && entry.message?.role === \"user\") {\n\t\t\t\t\t\tconst msg = entry.message;\n\t\t\t\t\t\tlet text = \"\";\n\t\t\t\t\t\tif (typeof msg.content === \"string\") {\n\t\t\t\t\t\t\ttext = msg.content;\n\t\t\t\t\t\t} else if (Array.isArray(msg.content)) {\n\t\t\t\t\t\t\tfor (const block of msg.content) {\n\t\t\t\t\t\t\t\tif (block.type === \"text\" && block.text) {\n\t\t\t\t\t\t\t\t\ttext = block.text;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Truncate and clean up for display\n\t\t\t\t\t\ttext = text.replace(/\\n/g, \" \").trim();\n\t\t\t\t\t\tif (text.length > 80) {\n\t\t\t\t\t\t\ttext = `${text.slice(0, 77)}...`;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn text || \"(empty message)\";\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Skip malformed lines\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn \"(no messages)\";\n\t\t} catch {\n\t\t\treturn \"(unreadable)\";\n\t\t}\n\t}\n\n\t/**\n\t * Format a relative time string (e.g. \"2 hours ago\", \"3 days ago\").\n\t */\n\tprivate formatRelativeTime(mtime: number): string {\n\t\tconst now = Date.now();\n\t\tconst diffMs = now - mtime;\n\t\tconst diffSec = Math.floor(diffMs / 1000);\n\t\tconst diffMin = Math.floor(diffSec / 60);\n\t\tconst diffHour = Math.floor(diffMin / 60);\n\t\tconst diffDay = Math.floor(diffHour / 24);\n\n\t\tif (diffMin < 1) return \"just now\";\n\t\tif (diffMin < 60) return `${diffMin}m ago`;\n\t\tif (diffHour < 24) return `${diffHour}h ago`;\n\t\tif (diffDay < 30) return `${diffDay}d ago`;\n\t\treturn new Date(mtime).toLocaleDateString();\n\t}\n\n\t/**\n\t * Handle the /resume command: show a list of previous sessions and load the selected one.\n\t */\n\tprivate async handleResume(): Promise<void> {\n\t\tconst sessions = this.listAvailableSessions();\n\t\tif (sessions.length === 0) {\n\t\t\tthis.showStatus(chalk.yellow(\"No previous sessions found.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tconst items: SelectItem[] = sessions.map((s) => ({\n\t\t\tvalue: s.path,\n\t\t\tlabel: `${chalk.dim(this.formatRelativeTime(s.mtime))} ${s.preview}`,\n\t\t}));\n\n\t\tconst selected = await this.showSelectList(\"Resume session\", items);\n\t\tif (!selected) return;\n\n\t\tconst session = sessions.find((s) => s.path === selected);\n\t\tif (!session) return;\n\n\t\ttry {\n\t\t\t// Open the selected session\n\t\t\tconst sessionDir = this.options.sessionDir!;\n\t\t\tconst newSessionManager = SessionManagerClass.open(selected, sessionDir);\n\n\t\t\t// Rebuild agent messages from session context\n\t\t\tconst context = newSessionManager.buildSessionContext();\n\t\t\tthis.agent.setMessages(context.messages);\n\n\t\t\t// Update session manager reference\n\t\t\tthis.options.sessionManager = newSessionManager;\n\n\t\t\t// Rebuild the chat UI\n\t\t\tthis.chatContainer.clear();\n\t\t\tthis.rebuildChatFromSession();\n\n\t\t\t// Update footer tokens\n\t\t\tthis.updateFooterTokens();\n\n\t\t\tconst msgCount = context.messages.length;\n\t\t\tthis.showStatus(chalk.green(`Resumed session (${msgCount} messages)`));\n\t\t} catch (error) {\n\t\t\tconst msg = error instanceof Error ? error.message : String(error);\n\t\t\tthis.showStatus(chalk.red(`Failed to resume session: ${msg}`));\n\t\t}\n\t}\n\n\t// ========================================================================\n\t// Select List (overlay pattern from pi-coding-agent)\n\t// ========================================================================\n\n\tprivate showSelectList(title: string, items: SelectItem[]): Promise<string | null> {\n\t\treturn new Promise((resolve) => {\n\t\t\tconst container = new Container();\n\n\t\t\tcontainer.addChild(new Spacer(1));\n\t\t\tcontainer.addChild(new Text(chalk.bold.cyan(title), 1, 0));\n\n\t\t\tconst selectList = new SelectList(items, Math.min(items.length, 10), getSelectListTheme());\n\t\t\tselectList.onSelect = (item) => {\n\t\t\t\t// Restore normal UI\n\t\t\t\tthis.pendingContainer.clear();\n\t\t\t\tthis.editorContainer.addChild(this.editor);\n\t\t\t\tthis.ui.setFocus(this.editor);\n\t\t\t\tthis.ui.requestRender();\n\t\t\t\tresolve(item.value);\n\t\t\t};\n\t\t\tselectList.onCancel = () => {\n\t\t\t\tthis.pendingContainer.clear();\n\t\t\t\tthis.editorContainer.addChild(this.editor);\n\t\t\t\tthis.ui.setFocus(this.editor);\n\t\t\t\tthis.ui.requestRender();\n\t\t\t\tresolve(null);\n\t\t\t};\n\t\t\tcontainer.addChild(selectList);\n\t\t\tcontainer.addChild(new Text(chalk.dim(\"↑↓ navigate • enter select • esc cancel\"), 1, 0));\n\t\t\tcontainer.addChild(new Spacer(1));\n\n\t\t\t// Replace editor area with select list\n\t\t\tthis.editorContainer.clear();\n\t\t\tthis.pendingContainer.clear();\n\t\t\tthis.pendingContainer.addChild(container);\n\t\t\tthis.ui.setFocus(selectList);\n\t\t\tthis.ui.requestRender();\n\t\t});\n\t}\n\n\t// ========================================================================\n\t// Status & Utilities\n\t// ========================================================================\n\n\tprivate showStatus(text: string): void {\n\t\tthis.chatContainer.addChild(new Spacer(1));\n\t\tthis.chatContainer.addChild(new Text(text, 1, 0));\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate updateEditorBorderColor(): void {\n\t\tthis.editorTheme.borderColor = this.isBashMode ? (s: string) => chalk.yellow(s) : (s: string) => chalk.gray(s);\n\t\tthis.ui.requestRender();\n\t}\n\n\tprivate shutdown(): void {\n\t\tthis.ui.stop();\n\t\tconsole.log(chalk.dim(\"\\nGoodbye.\"));\n\t\tprocess.exit(0);\n\t}\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction buildUserMessage(text: string, imageParts?: ImagePart[]): ModelMessage[] {\n\tconst content: Array<{ type: \"text\"; text: string } | ImagePart> = [{ type: \"text\" as const, text }];\n\tif (imageParts && imageParts.length > 0) {\n\t\tcontent.push(...imageParts);\n\t}\n\treturn [{ role: \"user\" as const, content }];\n}\n\nfunction extractTextFromMessage(msg: ModelMessage): string {\n\tif (msg.role === \"user\") {\n\t\tconst content = (msg as import(\"edge-pi\").UserModelMessage).content;\n\t\tif (typeof content === \"string\") return content;\n\t\tif (Array.isArray(content)) {\n\t\t\treturn content\n\t\t\t\t.filter((c): c is { type: \"text\"; text: string } => (c as { type: string }).type === \"text\")\n\t\t\t\t.map((c) => c.text)\n\t\t\t\t.join(\"\");\n\t\t}\n\t}\n\treturn \"\";\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"print-mode.d.ts","sourceRoot":"","sources":["../../src/modes/print-mode.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE3D,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,cAAc,CAAC;CAChC;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgD/F","sourcesContent":["/**\n * Print mode (single-shot): Send prompts, output result, exit.\n *\n * Used for:\n * - `epi -p \"prompt\"` - text output\n * - `epi --mode json \"prompt\"` - JSON event stream\n */\n\nimport type { CodingAgent, SessionManager } from \"edge-pi\";\n\nexport interface PrintModeOptions {\n\tmode: \"text\" | \"json\";\n\tmessages: string[];\n\tinitialMessage?: string;\n\tsessionManager?: SessionManager;\n}\n\n/**\n * Run in print (single-shot) mode.\n * Sends prompts to the agent and outputs the result.\n */\nexport async function runPrintMode(agent: CodingAgent, options: PrintModeOptions): Promise<void> {\n\tconst { mode, messages, initialMessage, sessionManager } = options;\n\n\tconst allMessages: string[] = [];\n\tif (initialMessage) {\n\t\tallMessages.push(initialMessage);\n\t}\n\tallMessages.push(...messages);\n\n\tif (allMessages.length === 0) {\n\t\tconsole.error(\"No prompt provided. Use -p with a message.\");\n\t\tprocess.exit(1);\n\t}\n\n\tfor (const message of allMessages) {\n\t\tconst result = await agent.prompt({ prompt: message });\n\n\t\t// Save messages to session\n\t\tif (sessionManager) {\n\t\t\tconst agentMessages = result.messages;\n\t\t\tfor (const msg of agentMessages.slice(agent.messages.length - agentMessages.length)) {\n\t\t\t\tsessionManager.appendMessage(msg);\n\t\t\t}\n\t\t}\n\n\t\tif (mode === \"json\") {\n\t\t\tconsole.log(\n\t\t\t\tJSON.stringify({\n\t\t\t\t\ttype: \"result\",\n\t\t\t\t\ttext: result.text,\n\t\t\t\t\tstepCount: result.stepCount,\n\t\t\t\t\tusage: result.totalUsage,\n\t\t\t\t}),\n\t\t\t);\n\t\t} else {\n\t\t\tif (result.text) {\n\t\t\t\tconsole.log(result.text);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Flush stdout\n\tawait new Promise<void>((resolve, reject) => {\n\t\tprocess.stdout.write(\"\", (err) => {\n\t\t\tif (err) reject(err);\n\t\t\telse resolve();\n\t\t});\n\t});\n}\n"]}
1
+ {"version":3,"file":"print-mode.d.ts","sourceRoot":"","sources":["../../src/modes/print-mode.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE3D,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,cAAc,CAAC;CAChC;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgD/F","sourcesContent":["/**\n * Print mode (single-shot): Send prompts, output result, exit.\n *\n * Used for:\n * - `epi -p \"prompt\"` - text output\n * - `epi --mode json \"prompt\"` - JSON event stream\n */\n\nimport type { CodingAgent, SessionManager } from \"edge-pi\";\n\nexport interface PrintModeOptions {\n\tmode: \"text\" | \"json\";\n\tmessages: string[];\n\tinitialMessage?: string;\n\tsessionManager?: SessionManager;\n}\n\n/**\n * Run in print (single-shot) mode.\n * Sends prompts to the agent and outputs the result.\n */\nexport async function runPrintMode(agent: CodingAgent, options: PrintModeOptions): Promise<void> {\n\tconst { mode, messages, initialMessage, sessionManager } = options;\n\n\tconst allMessages: string[] = [];\n\tif (initialMessage) {\n\t\tallMessages.push(initialMessage);\n\t}\n\tallMessages.push(...messages);\n\n\tif (allMessages.length === 0) {\n\t\tconsole.error(\"No prompt provided. Use -p with a message.\");\n\t\tprocess.exit(1);\n\t}\n\n\tfor (const message of allMessages) {\n\t\tconst result = await agent.generate({ prompt: message });\n\n\t\t// Save messages to session\n\t\tif (sessionManager) {\n\t\t\tconst agentMessages = [...agent.messages];\n\t\t\tfor (const msg of agentMessages.slice(agent.messages.length - agentMessages.length)) {\n\t\t\t\tsessionManager.appendMessage(msg);\n\t\t\t}\n\t\t}\n\n\t\tif (mode === \"json\") {\n\t\t\tconsole.log(\n\t\t\t\tJSON.stringify({\n\t\t\t\t\ttype: \"result\",\n\t\t\t\t\ttext: result.text,\n\t\t\t\t\tstepCount: result.steps.length,\n\t\t\t\t\tusage: result.totalUsage,\n\t\t\t\t}),\n\t\t\t);\n\t\t} else {\n\t\t\tif (result.text) {\n\t\t\t\tconsole.log(result.text);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Flush stdout\n\tawait new Promise<void>((resolve, reject) => {\n\t\tprocess.stdout.write(\"\", (err) => {\n\t\t\tif (err) reject(err);\n\t\t\telse resolve();\n\t\t});\n\t});\n}\n"]}
@@ -21,10 +21,10 @@ export async function runPrintMode(agent, options) {
21
21
  process.exit(1);
22
22
  }
23
23
  for (const message of allMessages) {
24
- const result = await agent.prompt({ prompt: message });
24
+ const result = await agent.generate({ prompt: message });
25
25
  // Save messages to session
26
26
  if (sessionManager) {
27
- const agentMessages = result.messages;
27
+ const agentMessages = [...agent.messages];
28
28
  for (const msg of agentMessages.slice(agent.messages.length - agentMessages.length)) {
29
29
  sessionManager.appendMessage(msg);
30
30
  }
@@ -33,7 +33,7 @@ export async function runPrintMode(agent, options) {
33
33
  console.log(JSON.stringify({
34
34
  type: "result",
35
35
  text: result.text,
36
- stepCount: result.stepCount,
36
+ stepCount: result.steps.length,
37
37
  usage: result.totalUsage,
38
38
  }));
39
39
  }
@@ -1 +1 @@
1
- {"version":3,"file":"print-mode.js","sourceRoot":"","sources":["../../src/modes/print-mode.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAWH;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAAkB,EAAE,OAAyB,EAAiB;IAChG,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,cAAc,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC;IAEnE,MAAM,WAAW,GAAa,EAAE,CAAC;IACjC,IAAI,cAAc,EAAE,CAAC;QACpB,WAAW,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAClC,CAAC;IACD,WAAW,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC;IAE9B,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAC5D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IAED,KAAK,MAAM,OAAO,IAAI,WAAW,EAAE,CAAC;QACnC,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;QAEvD,2BAA2B;QAC3B,IAAI,cAAc,EAAE,CAAC;YACpB,MAAM,aAAa,GAAG,MAAM,CAAC,QAAQ,CAAC;YACtC,KAAK,MAAM,GAAG,IAAI,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;gBACrF,cAAc,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;YACnC,CAAC;QACF,CAAC;QAED,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;YACrB,OAAO,CAAC,GAAG,CACV,IAAI,CAAC,SAAS,CAAC;gBACd,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,SAAS,EAAE,MAAM,CAAC,SAAS;gBAC3B,KAAK,EAAE,MAAM,CAAC,UAAU;aACxB,CAAC,CACF,CAAC;QACH,CAAC;aAAM,CAAC;YACP,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;gBACjB,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC1B,CAAC;QACF,CAAC;IACF,CAAC;IAED,eAAe;IACf,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC;QAC5C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC;YACjC,IAAI,GAAG;gBAAE,MAAM,CAAC,GAAG,CAAC,CAAC;;gBAChB,OAAO,EAAE,CAAC;QAAA,CACf,CAAC,CAAC;IAAA,CACH,CAAC,CAAC;AAAA,CACH","sourcesContent":["/**\n * Print mode (single-shot): Send prompts, output result, exit.\n *\n * Used for:\n * - `epi -p \"prompt\"` - text output\n * - `epi --mode json \"prompt\"` - JSON event stream\n */\n\nimport type { CodingAgent, SessionManager } from \"edge-pi\";\n\nexport interface PrintModeOptions {\n\tmode: \"text\" | \"json\";\n\tmessages: string[];\n\tinitialMessage?: string;\n\tsessionManager?: SessionManager;\n}\n\n/**\n * Run in print (single-shot) mode.\n * Sends prompts to the agent and outputs the result.\n */\nexport async function runPrintMode(agent: CodingAgent, options: PrintModeOptions): Promise<void> {\n\tconst { mode, messages, initialMessage, sessionManager } = options;\n\n\tconst allMessages: string[] = [];\n\tif (initialMessage) {\n\t\tallMessages.push(initialMessage);\n\t}\n\tallMessages.push(...messages);\n\n\tif (allMessages.length === 0) {\n\t\tconsole.error(\"No prompt provided. Use -p with a message.\");\n\t\tprocess.exit(1);\n\t}\n\n\tfor (const message of allMessages) {\n\t\tconst result = await agent.prompt({ prompt: message });\n\n\t\t// Save messages to session\n\t\tif (sessionManager) {\n\t\t\tconst agentMessages = result.messages;\n\t\t\tfor (const msg of agentMessages.slice(agent.messages.length - agentMessages.length)) {\n\t\t\t\tsessionManager.appendMessage(msg);\n\t\t\t}\n\t\t}\n\n\t\tif (mode === \"json\") {\n\t\t\tconsole.log(\n\t\t\t\tJSON.stringify({\n\t\t\t\t\ttype: \"result\",\n\t\t\t\t\ttext: result.text,\n\t\t\t\t\tstepCount: result.stepCount,\n\t\t\t\t\tusage: result.totalUsage,\n\t\t\t\t}),\n\t\t\t);\n\t\t} else {\n\t\t\tif (result.text) {\n\t\t\t\tconsole.log(result.text);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Flush stdout\n\tawait new Promise<void>((resolve, reject) => {\n\t\tprocess.stdout.write(\"\", (err) => {\n\t\t\tif (err) reject(err);\n\t\t\telse resolve();\n\t\t});\n\t});\n}\n"]}
1
+ {"version":3,"file":"print-mode.js","sourceRoot":"","sources":["../../src/modes/print-mode.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAWH;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAAkB,EAAE,OAAyB,EAAiB;IAChG,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,cAAc,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC;IAEnE,MAAM,WAAW,GAAa,EAAE,CAAC;IACjC,IAAI,cAAc,EAAE,CAAC;QACpB,WAAW,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAClC,CAAC;IACD,WAAW,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC;IAE9B,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAC5D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IAED,KAAK,MAAM,OAAO,IAAI,WAAW,EAAE,CAAC;QACnC,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;QAEzD,2BAA2B;QAC3B,IAAI,cAAc,EAAE,CAAC;YACpB,MAAM,aAAa,GAAG,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC;YAC1C,KAAK,MAAM,GAAG,IAAI,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;gBACrF,cAAc,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;YACnC,CAAC;QACF,CAAC;QAED,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;YACrB,OAAO,CAAC,GAAG,CACV,IAAI,CAAC,SAAS,CAAC;gBACd,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM;gBAC9B,KAAK,EAAE,MAAM,CAAC,UAAU;aACxB,CAAC,CACF,CAAC;QACH,CAAC;aAAM,CAAC;YACP,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;gBACjB,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC1B,CAAC;QACF,CAAC;IACF,CAAC;IAED,eAAe;IACf,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC;QAC5C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC;YACjC,IAAI,GAAG;gBAAE,MAAM,CAAC,GAAG,CAAC,CAAC;;gBAChB,OAAO,EAAE,CAAC;QAAA,CACf,CAAC,CAAC;IAAA,CACH,CAAC,CAAC;AAAA,CACH","sourcesContent":["/**\n * Print mode (single-shot): Send prompts, output result, exit.\n *\n * Used for:\n * - `epi -p \"prompt\"` - text output\n * - `epi --mode json \"prompt\"` - JSON event stream\n */\n\nimport type { CodingAgent, SessionManager } from \"edge-pi\";\n\nexport interface PrintModeOptions {\n\tmode: \"text\" | \"json\";\n\tmessages: string[];\n\tinitialMessage?: string;\n\tsessionManager?: SessionManager;\n}\n\n/**\n * Run in print (single-shot) mode.\n * Sends prompts to the agent and outputs the result.\n */\nexport async function runPrintMode(agent: CodingAgent, options: PrintModeOptions): Promise<void> {\n\tconst { mode, messages, initialMessage, sessionManager } = options;\n\n\tconst allMessages: string[] = [];\n\tif (initialMessage) {\n\t\tallMessages.push(initialMessage);\n\t}\n\tallMessages.push(...messages);\n\n\tif (allMessages.length === 0) {\n\t\tconsole.error(\"No prompt provided. Use -p with a message.\");\n\t\tprocess.exit(1);\n\t}\n\n\tfor (const message of allMessages) {\n\t\tconst result = await agent.generate({ prompt: message });\n\n\t\t// Save messages to session\n\t\tif (sessionManager) {\n\t\t\tconst agentMessages = [...agent.messages];\n\t\t\tfor (const msg of agentMessages.slice(agent.messages.length - agentMessages.length)) {\n\t\t\t\tsessionManager.appendMessage(msg);\n\t\t\t}\n\t\t}\n\n\t\tif (mode === \"json\") {\n\t\t\tconsole.log(\n\t\t\t\tJSON.stringify({\n\t\t\t\t\ttype: \"result\",\n\t\t\t\t\ttext: result.text,\n\t\t\t\t\tstepCount: result.steps.length,\n\t\t\t\t\tusage: result.totalUsage,\n\t\t\t\t}),\n\t\t\t);\n\t\t} else {\n\t\t\tif (result.text) {\n\t\t\t\tconsole.log(result.text);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Flush stdout\n\tawait new Promise<void>((resolve, reject) => {\n\t\tprocess.stdout.write(\"\", (err) => {\n\t\t\tif (err) reject(err);\n\t\t\telse resolve();\n\t\t});\n\t});\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "edge-pi-cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "CLI for the edge-pi coding agent SDK",
5
5
  "type": "module",
6
6
  "bin": {
@@ -23,7 +23,7 @@
23
23
  "@ai-sdk/openai": "^3.0.26",
24
24
  "@mariozechner/pi-tui": "^0.50.9",
25
25
  "chalk": "^5.5.0",
26
- "edge-pi": "^0.1.1",
26
+ "edge-pi": "^0.1.2",
27
27
  "proper-lockfile": "^4.1.2",
28
28
  "yaml": "^2.7.1"
29
29
  },