11import React , { useState , useEffect } from "react" ;
22import { invoke } from "@tauri-apps/api/core" ;
3- import { ApprovalRequest } from "@/types/codex" ;
3+ import { ApprovalRequest , CodexConfig } from "@/types/codex" ;
44import type { Conversation } from "@/types/chat" ;
55import { useConversationStore } from "@/stores/ConversationStore" ;
66import { useCodexStore } from "@/stores/CodexStore" ;
@@ -9,13 +9,13 @@ import { useModelStore } from "@/stores/ModelStore";
99import { sessionManager } from "@/services/sessionManager" ;
1010import { ChatInput } from "./ChatInput" ;
1111import { MessageList } from "./MessageList" ;
12- import { useCodexEvents } from "../.. /hooks/useCodexEvents" ;
13- import { ReasoningEffortSelector } from ' ./ReasoningEffortSelector' ;
12+ import { useCodexEvents } from "@ /hooks/useCodexEvents" ;
13+ import { ReasoningEffortSelector } from " ./ReasoningEffortSelector" ;
1414import { Sandbox } from "./Sandbox" ;
1515import { generateUniqueId } from "@/utils/genUniqueId" ;
16- import { ForkOriginBanner } from ' ./ForkOriginBanner' ;
17- import { useEphemeralStore } from ' @/stores/EphemeralStore' ;
18- import { ChangesSummary } from ' ./ChangesSummary' ;
16+ import { ForkOriginBanner } from " ./ForkOriginBanner" ;
17+ import { useEphemeralStore } from " @/stores/EphemeralStore" ;
18+ import { ChangesSummary } from " ./ChangesSummary" ;
1919import { ModelSelector } from "./ModelSelector" ;
2020import TokenCountInfo from "@/components/common/TokenCountInfo" ;
2121
@@ -46,19 +46,19 @@ export const ChatInterface: React.FC<ChatInterfaceProps> = ({
4646 setCurrentConversation,
4747 getCurrentConversation,
4848 } = useConversationStore ( ) ;
49-
49+
5050 // Get conversations filtered by current project
5151 const conversations = getCurrentProjectConversations ( ) ;
52-
53- const setSandboxMode = ( mode : typeof config . sandboxMode ) => {
54- updateConfig ( { sandboxMode : mode } ) ;
52+
53+ const setSandboxConfig = (
54+ updates : Partial < Pick < CodexConfig , "sandboxMode" | "approvalPolicy" > > ,
55+ ) => {
56+ updateConfig ( updates ) ;
5557 } ;
5658
5759 // Simplified: Use session_id to find conversation data
5860 // Priority: selectedConversation (from disk/history) > store currentConversation (unfiltered)
59- const currentConversation =
60- selectedConversation ||
61- getCurrentConversation ( ) ;
61+ const currentConversation = selectedConversation || getCurrentConversation ( ) ;
6262
6363 // Convert conversation messages to chat messages format
6464 const sessionMessages = currentConversation
@@ -68,14 +68,17 @@ export const ChatInterface: React.FC<ChatInterfaceProps> = ({
6868 role : msg . role ,
6969 content : msg . content ,
7070 title : msg . title ,
71- timestamp : typeof msg . timestamp === "number" ? msg . timestamp : Date . now ( ) ,
71+ timestamp :
72+ typeof msg . timestamp === "number" ? msg . timestamp : Date . now ( ) ,
7273 model : msg . role === "assistant" ? currentModel : undefined ,
7374 approvalRequest : msg . approvalRequest ,
7475 // Preserve optional rendering metadata
75- ...( msg as any ) . messageType && { messageType : ( msg as any ) . messageType } ,
76- ...( msg as any ) . eventType && { eventType : ( msg as any ) . eventType } ,
76+ ...( ( msg as any ) . messageType && {
77+ messageType : ( msg as any ) . messageType ,
78+ } ) ,
79+ ...( ( msg as any ) . eventType && { eventType : ( msg as any ) . eventType } ) ,
7780 // Preserve structured plan payload for plan_update messages
78- ...( msg as any ) . plan && { plan : ( msg as any ) . plan } ,
81+ ...( ( msg as any ) . plan && { plan : ( msg as any ) . plan } ) ,
7982 } ;
8083 } )
8184 : [ ] ;
@@ -107,8 +110,12 @@ export const ChatInterface: React.FC<ChatInterfaceProps> = ({
107110
108111 const messages = [ ...sessionMessages ] ;
109112 const isLoading = currentConversation ?. isLoading || false ;
110- const fileDiffMap = useEphemeralStore ( ( s ) => s . sessionFileDiffs [ activeSessionId ] ) ;
111- const sessionUsage = useEphemeralStore ( ( s ) => s . sessionTokenUsage [ activeSessionId ] ) ;
113+ const fileDiffMap = useEphemeralStore (
114+ ( s ) => s . sessionFileDiffs [ activeSessionId ] ,
115+ ) ;
116+ const sessionUsage = useEphemeralStore (
117+ ( s ) => s . sessionTokenUsage [ activeSessionId ] ,
118+ ) ;
112119
113120 // Update activeSessionId when sessionId prop changes
114121 useEffect ( ( ) => {
@@ -132,7 +139,10 @@ export const ChatInterface: React.FC<ChatInterfaceProps> = ({
132139 useEphemeralStore . getState ( ) . clearTurnDiffs ( activeSessionId ) ;
133140 }
134141 } catch ( e ) {
135- console . error ( 'Failed to clear diffs during new conversation setup:' , e ) ;
142+ console . error (
143+ "Failed to clear diffs during new conversation setup:" ,
144+ e ,
145+ ) ;
136146 }
137147 // Ensure we don't keep showing old diffs
138148 if ( activeSessionId !== sessionId ) {
@@ -271,7 +281,10 @@ export const ChatInterface: React.FC<ChatInterfaceProps> = ({
271281 useEphemeralStore . getState ( ) . clearTurnDiffs ( actualSessionId ) ;
272282 }
273283 } catch ( e ) {
274- console . error ( 'Failed to clear diffs before sending in new conversation:' , e ) ;
284+ console . error (
285+ "Failed to clear diffs before sending in new conversation:" ,
286+ e ,
287+ ) ;
275288 }
276289
277290 // Ensure session is running before sending message
@@ -318,30 +331,36 @@ export const ChatInterface: React.FC<ChatInterfaceProps> = ({
318331 }
319332
320333 // If this is a forked conversation and not yet applied, prepend context
321- if ( currentConversation ?. forkMeta && ! currentConversation . forkMeta . applied ) {
334+ if (
335+ currentConversation ?. forkMeta &&
336+ ! currentConversation . forkMeta . applied
337+ ) {
322338 try {
323339 const meta = currentConversation . forkMeta ;
324340 const historyText = meta . history
325- . filter ( ( m ) => m . role === ' user' || m . role === ' assistant' )
341+ . filter ( ( m ) => m . role === " user" || m . role === " assistant" )
326342 . map ( ( m ) => `${ m . role } :\n${ m . content } ` )
327343 . join ( "\n\n" ) ;
328344 const forkHeader = `parentId: ${ meta . parentMessageId } \nsourceSession: ${ meta . fromConversationId } ` ;
329345 const combined = `${ forkHeader } \n\nConversation history:\n${ historyText } \n\nUser:\n${ messageContent } ` ;
330346 messageContent = combined ;
331347 } catch ( e ) {
332- console . error ( ' Failed to build fork context:' , e ) ;
348+ console . error ( " Failed to build fork context:" , e ) ;
333349 }
334350 }
335351
336352 // If resending from an edited message, truncate messages from that point
337353 try {
338- const { editingTarget, clearEditingTarget } = useChatInputStore . getState ( ) ;
354+ const { editingTarget, clearEditingTarget } =
355+ useChatInputStore . getState ( ) ;
339356 if ( editingTarget && editingTarget . conversationId === actualSessionId ) {
340- useConversationStore . getState ( ) . truncateMessagesFrom ( actualSessionId , editingTarget . messageId ) ;
357+ useConversationStore
358+ . getState ( )
359+ . truncateMessagesFrom ( actualSessionId , editingTarget . messageId ) ;
341360 clearEditingTarget ( ) ;
342361 }
343362 } catch ( e ) {
344- console . error ( ' Failed to handle edit-resend truncation:' , e ) ;
363+ console . error ( " Failed to handle edit-resend truncation:" , e ) ;
345364 }
346365
347366 // Add user message to conversation store
@@ -369,7 +388,10 @@ export const ChatInterface: React.FC<ChatInterfaceProps> = ({
369388 console . log ( "📤 ChatInterface: Sending text message:" , messageContent ) ;
370389
371390 // Mark fork context as applied so future sends don't include it
372- if ( currentConversation ?. forkMeta && ! currentConversation . forkMeta . applied ) {
391+ if (
392+ currentConversation ?. forkMeta &&
393+ ! currentConversation . forkMeta . applied
394+ ) {
373395 useConversationStore . getState ( ) . setForkMetaApplied ( actualSessionId ) ;
374396 }
375397 } catch ( error ) {
@@ -385,7 +407,10 @@ export const ChatInterface: React.FC<ChatInterfaceProps> = ({
385407 }
386408 } ;
387409
388- const handleApproval = async ( approved : boolean , approvalRequest : ApprovalRequest ) => {
410+ const handleApproval = async (
411+ approved : boolean ,
412+ approvalRequest : ApprovalRequest ,
413+ ) => {
389414 try {
390415 // Extract raw session ID for backend communication
391416 const rawSessionId = sessionId . startsWith ( "codex-event-" )
@@ -394,47 +419,42 @@ export const ChatInterface: React.FC<ChatInterfaceProps> = ({
394419
395420 // Handle different approval types with appropriate backend calls
396421 switch ( approvalRequest . type ) {
397- case ' exec' :
422+ case " exec" :
398423 await invoke ( "approve_execution" , {
399424 sessionId : rawSessionId ,
400425 approvalId : approvalRequest . id ,
401426 approved,
402427 } ) ;
403428 break ;
404-
405- case 'patch' :
406- await invoke ( "approve_patch" , {
407- sessionId : rawSessionId ,
408- approvalId : approvalRequest . id ,
409- approved,
410- } ) ;
411- break ;
412-
413- case 'apply_patch' :
429+
430+ case "patch" :
431+ case "apply_patch" :
414432 await invoke ( "approve_patch" , {
415433 sessionId : rawSessionId ,
416434 approvalId : approvalRequest . id ,
417435 approved,
418436 } ) ;
419437 break ;
420-
438+
421439 default :
422- console . error ( ' Unknown approval request type:' , approvalRequest . type ) ;
440+ console . error ( " Unknown approval request type:" , approvalRequest . type ) ;
423441 return ;
424442 }
425-
426- console . log ( `✅ Approval ${ approved ? 'granted' : 'denied' } for ${ approvalRequest . type } request ${ approvalRequest . id } ` ) ;
427-
443+
444+ console . log (
445+ `✅ Approval ${ approved ? "granted" : "denied" } for ${ approvalRequest . type } request ${ approvalRequest . id } ` ,
446+ ) ;
447+
428448 // If denied, pause the session to stop further execution
429449 if ( ! approved ) {
430- console . log ( ' 🛑 Pausing session due to denied approval' ) ;
450+ console . log ( " 🛑 Pausing session due to denied approval" ) ;
431451 await invoke ( "pause_session" , {
432452 sessionId : rawSessionId ,
433453 } ) ;
434454 }
435455 } catch ( error ) {
436456 console . error ( "Failed to send approval:" , error ) ;
437-
457+
438458 // Add error message to conversation
439459 const errorMessage = {
440460 id : `${ sessionId } -approval-error-${ generateUniqueId ( ) } ` ,
@@ -472,13 +492,18 @@ export const ChatInterface: React.FC<ChatInterfaceProps> = ({
472492 onStopStreaming = { handleStopStreaming }
473493 disabled = { ! ! selectedConversation && ! selectedConversation . filePath }
474494 isLoading = { isLoading }
475- placeholderOverride = { editingTarget ? 'Editing message and resending from here' : undefined }
495+ placeholderOverride = {
496+ editingTarget
497+ ? "Editing message and resending from here"
498+ : undefined
499+ }
476500 />
477-
501+
478502 < div className = "flex px-2 pt-0.5" >
479- < Sandbox
503+ < Sandbox
480504 sandboxMode = { config . sandboxMode }
481- onModeChange = { setSandboxMode }
505+ approvalPolicy = { config . approvalPolicy }
506+ onConfigChange = { setSandboxConfig }
482507 />
483508 < ModelSelector />
484509 < ReasoningEffortSelector />
0 commit comments