Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions packages/ui/src/components/session-turn.css
Original file line number Diff line number Diff line change
Expand Up @@ -569,4 +569,20 @@
flex-direction: column;
gap: 12px;
}

[data-slot="session-turn-question-parts"] {
width: 100%;
min-width: 0;
display: flex;
flex-direction: column;
gap: 12px;
}

[data-slot="session-turn-answered-question-parts"] {
width: 100%;
min-width: 0;
display: flex;
flex-direction: column;
gap: 12px;
}
}
62 changes: 62 additions & 0 deletions packages/ui/src/components/session-turn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
Message as MessageType,
Part as PartType,
type PermissionRequest,
type QuestionRequest,
TextPart,
ToolPart,
} from "@opencode-ai/sdk/v2/client"
Expand Down Expand Up @@ -150,6 +151,8 @@ export function SessionTurn(
const emptyAssistant: AssistantMessage[] = []
const emptyPermissions: PermissionRequest[] = []
const emptyPermissionParts: { part: ToolPart; message: AssistantMessage }[] = []
const emptyQuestions: QuestionRequest[] = []
const emptyQuestionParts: { part: ToolPart; message: AssistantMessage }[] = []
const emptyDiffs: FileDiff[] = []
const idle = { type: "idle" as const }

Expand Down Expand Up @@ -281,6 +284,51 @@ export function SessionTurn(
return emptyPermissionParts
})

const questions = createMemo(() => data.store.question?.[props.sessionID] ?? emptyQuestions)
const nextQuestion = createMemo(() => questions()[0])

const questionParts = createMemo(() => {
if (props.stepsExpanded) return emptyQuestionParts

const next = nextQuestion()
if (!next || !next.tool) return emptyQuestionParts

const message = findLast(assistantMessages(), (m) => m.id === next.tool!.messageID)
if (!message) return emptyQuestionParts

const parts = data.store.part[message.id] ?? emptyParts
for (const part of parts) {
if (part?.type !== "tool") continue
const tool = part as ToolPart
if (tool.callID === next.tool?.callID) return [{ part: tool, message }]
}

return emptyQuestionParts
})

const answeredQuestionParts = createMemo(() => {
if (props.stepsExpanded) return emptyQuestionParts
if (questions().length > 0) return emptyQuestionParts

const result: { part: ToolPart; message: AssistantMessage }[] = []

for (const msg of assistantMessages()) {
const parts = data.store.part[msg.id] ?? emptyParts
for (const part of parts) {
if (part?.type !== "tool") continue
const tool = part as ToolPart
if (tool.tool !== "question") continue
// @ts-expect-error metadata may not exist on all tool states
const answers = tool.state?.metadata?.answers
if (answers && answers.length > 0) {
result.push({ part: tool, message: msg })
}
}
}

return result
})

const shellModePart = createMemo(() => {
const p = parts()
if (p.length === 0) return
Expand Down Expand Up @@ -640,6 +688,20 @@ export function SessionTurn(
</For>
</div>
</Show>
<Show when={!props.stepsExpanded && questionParts().length > 0}>
<div data-slot="session-turn-question-parts">
<For each={questionParts()}>
{({ part, message }) => <Part part={part} message={message} />}
</For>
</div>
</Show>
<Show when={!props.stepsExpanded && answeredQuestionParts().length > 0}>
<div data-slot="session-turn-answered-question-parts">
<For each={answeredQuestionParts()}>
{({ part, message }) => <Part part={part} message={message} />}
</For>
</div>
</Show>
{/* Response */}
<div class="sr-only" aria-live="polite">
{!working() && response() ? response() : ""}
Expand Down
Loading