@@ -92,6 +92,8 @@ function AcpDemo() {
9292 resolvePermission,
9393 rejectPermission,
9494 agent : acp ,
95+ availableCommands,
96+ sessionMode,
9597 } = useAcpClient ( {
9698 wsUrl,
9799 reconnectAttempts : 3 ,
@@ -163,6 +165,19 @@ function AcpDemo() {
163165 } , [ acp , activeSessionId ] ) ,
164166 ) ;
165167
168+ const [ executeSetMode , isSettingMode , setModeError ] = useAsync (
169+ useCallback (
170+ async ( modeId : string ) => {
171+ if ( ! acp || ! activeSessionId ) throw new Error ( "ACP not connected or no active session" ) ;
172+ if ( ! ( "setSessionMode" in acp ) || typeof acp . setSessionMode !== "function" ) {
173+ throw new Error ( "Agent does not support session modes" ) ;
174+ }
175+ return acp . setSessionMode ( { sessionId : activeSessionId , modeId } ) ;
176+ } ,
177+ [ acp , activeSessionId ] ,
178+ ) ,
179+ ) ;
180+
166181 const handleConnect = async ( ) => {
167182 try {
168183 await connect ( ) ;
@@ -227,6 +242,17 @@ function AcpDemo() {
227242 setActiveSessionId ( null ) ;
228243 } ;
229244
245+ const handleSlashCommand = ( commandName : string ) => {
246+ setPromptText ( ( prev ) => `${ prev } /${ commandName } ` ) ;
247+ } ;
248+
249+ const handleSetSessionMode = async ( modeId : string ) => {
250+ const response = await executeSetMode ( modeId ) ;
251+ if ( response ) {
252+ console . log ( "Session mode changed:" , response ) ;
253+ }
254+ } ;
255+
230256 const getStatusColor = ( ) => {
231257 switch ( connectionState . status ) {
232258 case "connected" :
@@ -339,11 +365,14 @@ function AcpDemo() {
339365 ) }
340366 </ div >
341367
342- { ( newSessionError || promptError || cancelError ) && (
368+ { ( newSessionError || promptError || cancelError || setModeError ) && (
343369 < div className = "bg-red-50 border border-red-200 rounded-md p-3 mb-3" >
344370 < h4 className = "font-medium text-red-800 text-sm mb-1" > Error</ h4 >
345371 < p className = "text-sm text-red-700" >
346- { newSessionError ?. message || promptError ?. message || cancelError ?. message }
372+ { newSessionError ?. message ||
373+ promptError ?. message ||
374+ cancelError ?. message ||
375+ setModeError ?. message }
347376 </ p >
348377 </ div >
349378 ) }
@@ -405,42 +434,101 @@ function AcpDemo() {
405434 ) }
406435
407436 { activeSessionId && (
408- < div className = "space-y-2 pt-2 border-t" >
409- < label
410- htmlFor = "promptText"
411- className = "block text-sm font-medium text-gray-700"
412- >
413- Send Message to Agent
414- </ label >
415- < div className = "flex gap-2" >
416- < input
417- id = "promptText"
418- type = "text"
419- value = { promptText }
420- onChange = { ( e ) => setPromptText ( e . target . value ) }
421- onKeyPress = { ( e ) => e . key === "Enter" && ! isPrompting && handlePrompt ( ) }
422- placeholder = "Type your message..."
423- className = "flex-1 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm"
424- disabled = { isPrompting }
425- />
426- < button
427- type = "button"
428- onClick = { handlePrompt }
429- disabled = { isPrompting || ! promptText . trim ( ) }
430- className = "px-3 py-2 bg-green-500 text-white rounded-md hover:bg-green-600 disabled:bg-gray-400 disabled:cursor-not-allowed text-sm"
437+ < div className = "space-y-3 pt-2 border-t" >
438+ { /* Slash Commands */ }
439+ { availableCommands . length > 0 && (
440+ < div >
441+ < label
442+ htmlFor = "slashCommands"
443+ className = "block text-sm font-medium text-gray-700 mb-2"
444+ >
445+ Slash Commands
446+ </ label >
447+ < select
448+ id = "slashCommands"
449+ onChange = { ( e ) => {
450+ if ( e . target . value ) {
451+ handleSlashCommand ( e . target . value ) ;
452+ e . target . value = "" ;
453+ }
454+ } }
455+ className = "w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm"
456+ >
457+ < option value = "" > Insert slash command...</ option >
458+ { availableCommands . map ( ( command ) => (
459+ < option key = { command . name } value = { command . name } >
460+ /{ command . name } - { command . description }
461+ </ option >
462+ ) ) }
463+ </ select >
464+ </ div >
465+ ) }
466+
467+ { /* Session Modes */ }
468+ { sessionMode && sessionMode . availableModes . length > 1 && (
469+ < div >
470+ < label
471+ htmlFor = "sessionModes"
472+ className = "block text-sm font-medium text-gray-700 mb-2"
473+ >
474+ Session Mode
475+ </ label >
476+ < select
477+ id = "sessionModes"
478+ value = { sessionMode . currentModeId }
479+ onChange = { ( e ) => handleSetSessionMode ( e . target . value ) }
480+ disabled = { isSettingMode }
481+ className = "w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm disabled:bg-gray-100"
482+ >
483+ { sessionMode . availableModes . map ( ( mode ) => (
484+ < option key = { mode . id } value = { mode . id } >
485+ { mode . name } { mode . description ? `- ${ mode . description } ` : "" }
486+ </ option >
487+ ) ) }
488+ </ select >
489+ { isSettingMode && (
490+ < div className = "text-xs text-blue-600 mt-1" > Changing mode...</ div >
491+ ) }
492+ </ div >
493+ ) }
494+
495+ < div >
496+ < label
497+ htmlFor = "promptText"
498+ className = "block text-sm font-medium text-gray-700 mb-2"
431499 >
432- { isPrompting ? "..." : "Send" }
433- </ button >
434- { isPrompting && (
500+ Send Message to Agent
501+ </ label >
502+ < div className = "flex gap-2" >
503+ < input
504+ id = "promptText"
505+ type = "text"
506+ value = { promptText }
507+ onChange = { ( e ) => setPromptText ( e . target . value ) }
508+ onKeyPress = { ( e ) => e . key === "Enter" && ! isPrompting && handlePrompt ( ) }
509+ placeholder = "Type your message..."
510+ className = "flex-1 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm"
511+ disabled = { isPrompting }
512+ />
435513 < button
436514 type = "button"
437- onClick = { handleCancel }
438- disabled = { isCancelling }
439- className = "px-3 py-2 bg-red -500 text-white rounded-md hover:bg-red -600 disabled:bg-gray-400 disabled:cursor-not-allowed text-sm"
515+ onClick = { handlePrompt }
516+ disabled = { isPrompting || ! promptText . trim ( ) }
517+ className = "px-3 py-2 bg-green -500 text-white rounded-md hover:bg-green -600 disabled:bg-gray-400 disabled:cursor-not-allowed text-sm"
440518 >
441- { isCancelling ? "..." : "Cancel " }
519+ { isPrompting ? "..." : "Send " }
442520 </ button >
443- ) }
521+ { isPrompting && (
522+ < button
523+ type = "button"
524+ onClick = { handleCancel }
525+ disabled = { isCancelling }
526+ className = "px-3 py-2 bg-red-500 text-white rounded-md hover:bg-red-600 disabled:bg-gray-400 disabled:cursor-not-allowed text-sm"
527+ >
528+ { isCancelling ? "..." : "Cancel" }
529+ </ button >
530+ ) }
531+ </ div >
444532 </ div >
445533 </ div >
446534 ) }
0 commit comments