@@ -103,6 +103,7 @@ import {
103103import { switchLabel } from "../../util/model"
104104import { findMessageBoundary , messageNavigationSlack } from "./message-navigation"
105105import { stringWidth } from "../../util/string-width"
106+ import { sessionDescendants } from "../../util/session"
106107import { useArgs } from "../../context/args"
107108import { withTimestampedFallback } from "@opencode-ai/util/session-title-fallback"
108109import { useSessionTabs } from "../../context/session-tabs"
@@ -111,6 +112,7 @@ import type { SessionInbox } from "@opencode-ai/schema/session-inbox"
111112import { generateThinkingSyntax } from "./thinking-syntax"
112113import { createDelayedPresence } from "../../util/delayed-presence"
113114import { SessionLocationMissing } from "./location-missing"
115+ import { selectSessionAttention , type SessionAttention } from "./attention"
114116import { isRecord } from "../../util/record"
115117import { createHistoryPrepend } from "./history"
116118import { useSessionTerminals } from "../../context/session-terminals"
@@ -202,23 +204,29 @@ export function Session(props: {
202204 setEpilogue ( sessionEpilogue ( { title, sessionID : session ( ) ?. id } ) )
203205 } )
204206 onCleanup ( ( ) => setEpilogue ( ) )
205- const descendantSessionIDs = createMemo ( ( ) => {
206- if ( session ( ) ?. parentID ) return [ ]
207- return data . session . family ( route . sessionID ) . filter ( ( id ) => id !== route . sessionID )
208- } )
209- const permissions = createMemo ( ( ) => {
210- if ( session ( ) ?. parentID ) return [ ]
211- return [ route . sessionID , ...descendantSessionIDs ( ) ] . flatMap (
212- ( sessionID ) => data . session . permission . list ( sessionID ) ?? [ ] ,
213- )
214- } )
207+ const descendantSessionIDs = createMemo ( ( ) =>
208+ session ( ) ? sessionDescendants ( data . session . list ( ) , route . sessionID ) . map ( ( item ) => item . id ) : [ ] ,
209+ )
210+ const permissions = createMemo ( ( ) =>
211+ [ route . sessionID , ...descendantSessionIDs ( ) ] . flatMap ( ( sessionID ) => data . session . permission . list ( sessionID ) ?? [ ] ) ,
212+ )
215213 const promptedPermissions = createMemo ( ( ) => ( local . permission . mode === "auto" ? [ ] : permissions ( ) ) )
216- const forms = createMemo ( ( ) => {
217- const global = data . session . form . list ( "global" , location ( ) ) ?? [ ]
218- if ( session ( ) ?. parentID ) return global
219- return [ route . sessionID , ...descendantSessionIDs ( ) ]
214+ const forms = createMemo ( ( ) =>
215+ [ route . sessionID , ...descendantSessionIDs ( ) ]
220216 . flatMap ( ( sessionID ) => data . session . form . list ( sessionID ) ?? [ ] )
221- . concat ( global )
217+ . concat ( data . session . form . list ( "global" , location ( ) ) ?? [ ] ) ,
218+ )
219+ const attention = createMemo (
220+ ( previous : SessionAttention | undefined ) => selectSessionAttention ( promptedPermissions ( ) , forms ( ) , previous ) ,
221+ undefined ,
222+ )
223+ const requestCount = createMemo ( ( ) => promptedPermissions ( ) . length + forms ( ) . length )
224+ const requestPosition = createMemo ( ( ) => {
225+ const current = attention ( )
226+ if ( ! current ) return 0
227+ if ( current . type === "permission" )
228+ return promptedPermissions ( ) . findIndex ( ( item ) => item . id === current . request . id ) + 1
229+ return promptedPermissions ( ) . length + forms ( ) . findIndex ( ( item ) => item . id === current . request . id ) + 1
222230 } )
223231 const pendingUsers = createMemo ( ( ) =>
224232 data . session . pending . list ( route . sessionID ) . flatMap ( ( item ) => ( item . type === "user" ? [ item ] : [ ] ) ) ,
@@ -235,6 +243,7 @@ export function Session(props: {
235243 if ( props . promptMuted && composer . open ) setComposer ( "open" , false )
236244 } )
237245 const disabled = createMemo ( ( ) => promptedPermissions ( ) . length > 0 || forms ( ) . length > 0 )
246+ const composerVisible = createMemo ( ( ) => ! disabled ( ) && ( composer . open || ! ! session ( ) ?. parentID ) )
238247
239248 const lastAssistant = createMemo ( ( ) => {
240249 return messages ( ) . findLast ( ( x ) => x . type === "assistant" )
@@ -324,7 +333,11 @@ export function Session(props: {
324333 on ( [ descendantSessionIDs , ( ) => client . connection . status ( ) ] , ( [ sessionIDs , status ] ) => {
325334 if ( status !== "connected" ) return
326335 void Promise . allSettled (
327- sessionIDs . flatMap ( ( sessionID ) => [ data . session . permission . sync ( sessionID ) , data . session . form . sync ( sessionID ) ] ) ,
336+ sessionIDs . flatMap ( ( sessionID ) => [
337+ data . session . sync ( sessionID , { children : true } ) ,
338+ data . session . permission . sync ( sessionID ) ,
339+ data . session . form . sync ( sessionID ) ,
340+ ] ) ,
328341 )
329342 } ) ,
330343 )
@@ -1440,7 +1453,7 @@ export function Session(props: {
14401453 < Slot path = "session.composer.top" input = { { sessionID : route . sessionID } } />
14411454 < Composer
14421455 sessionID = { route . sessionID }
1443- open = { composer . open || ( ! ! session ( ) ?. parentID && forms ( ) . length === 0 ) }
1456+ open = { composerVisible ( ) }
14441457 defaultTab = { composer . tab ?? ( session ( ) ?. parentID ? "subagents" : undefined ) }
14451458 onClose = { ( ) => {
14461459 const parent = session ( ) ?. parentID
@@ -1453,22 +1466,31 @@ export function Session(props: {
14531466 visibleTerminalID = { props . visibleTerminalID }
14541467 />
14551468 < Switch >
1456- < Match when = { composer . open || ( ! ! session ( ) ?. parentID && forms ( ) . length === 0 ) } > { null } </ Match >
1457- < Match when = { promptedPermissions ( ) . length > 0 } >
1458- < Show when = { promptedPermissions ( ) [ 0 ] ? .id } keyed >
1469+ < Match when = { composerVisible ( ) } > { null } </ Match >
1470+ < Match when = { attention ( ) ?. type === "permission" } >
1471+ < Show when = { attention ( ) ?. request . id } keyed >
14591472 { ( _ ) => {
1460- const request = promptedPermissions ( ) [ 0 ]
1461- return request ? (
1462- < PermissionPrompt request = { request } directory = { session ( ) ?. location . directory } />
1473+ const current = attention ( )
1474+ return current ?. type === "permission" ? (
1475+ < PermissionPrompt
1476+ request = { current . request }
1477+ directory = { session ( ) ?. location . directory }
1478+ pending = { { current : requestPosition ( ) , total : requestCount ( ) } }
1479+ />
14631480 ) : null
14641481 } }
14651482 </ Show >
14661483 </ Match >
1467- < Match when = { forms ( ) . length > 0 } >
1468- < Show when = { forms ( ) [ 0 ] ? .id } keyed >
1484+ < Match when = { attention ( ) ?. type === "form" } >
1485+ < Show when = { attention ( ) ?. request . id } keyed >
14691486 { ( _ ) => {
1470- const form = forms ( ) [ 0 ]
1471- return form ? < FormPrompt form = { form } /> : null
1487+ const current = attention ( )
1488+ return current ?. type === "form" ? (
1489+ < FormPrompt
1490+ form = { current . request }
1491+ pending = { { current : requestPosition ( ) , total : requestCount ( ) } }
1492+ />
1493+ ) : null
14721494 } }
14731495 </ Show >
14741496 </ Match >
0 commit comments