Compare commits

...

1 Commits

Author SHA1 Message Date
James Long cbe1b7d67e fix(tui): show content filter finishes 2026-07-02 03:19:25 +00:00
3 changed files with 39 additions and 20 deletions
@@ -297,6 +297,7 @@ export function SessionTurn(
const error = createMemo(
() => assistantMessages().find((m) => m.error && m.error.name !== "MessageAbortedError")?.error,
)
const contentFiltered = createMemo(() => assistantMessages().some((m) => m.finish === "content-filter"))
const showAssistantCopyPartID = createMemo(() => {
const messages = assistantMessages()
@@ -315,6 +316,7 @@ export function SessionTurn(
return undefined
})
const errorText = createMemo(() => {
if (contentFiltered()) return "The response was blocked by the provider's content filter"
const msg = error()?.data?.message
if (typeof msg === "string") return unwrap(msg)
if (msg === undefined || msg === null) return ""
@@ -525,7 +527,7 @@ export function SessionTurn(
</div>
</div>
</Show>
<Show when={error()}>
<Show when={error() || contentFiltered()}>
<Card variant="error" class="error-card">
{errorText()}
</Card>
+28 -16
View File
@@ -1197,9 +1197,13 @@ function AssistantFooter(props: { message: SessionMessageAssistant }) {
const duration = createMemo(() =>
props.message.time.completed ? props.message.time.completed - props.message.time.created : 0,
)
const contentFiltered = createMemo(() => props.message.finish === "content-filter")
const errorText = createMemo(() =>
contentFiltered() ? "The response was blocked by the provider's content filter" : errorMessage(props.message.error),
)
return (
<>
<Show when={props.message.error}>
<Show when={props.message.error || contentFiltered()}>
<box
border={["left"]}
paddingTop={1}
@@ -1209,12 +1213,16 @@ function AssistantFooter(props: { message: SessionMessageAssistant }) {
customBorderChars={SplitBorder.customBorderChars}
borderColor={theme.error}
>
<text fg={theme.textMuted}>{errorMessage(props.message.error)}</text>
<text fg={theme.textMuted}>{errorText()}</text>
</box>
</Show>
<box paddingLeft={3} marginTop={props.message.error ? 1 : 0}>
<box paddingLeft={3} marginTop={props.message.error || contentFiltered() ? 1 : 0}>
<text>
<span style={{ fg: props.message.error ? theme.textMuted : local.agent.color(props.message.agent) }}>
<span
style={{
fg: props.message.error || contentFiltered() ? theme.textMuted : local.agent.color(props.message.agent),
}}
>
{Locale.titlecase(props.message.agent)}
</span>
<span style={{ fg: theme.textMuted }}> · {model()}</span>
@@ -1386,13 +1394,7 @@ function UserMessage(props: { message: SessionMessageUser }) {
>
<text fg={theme.text}>{props.message.text}</text>
<Show when={files().length}>
<box
flexDirection="row"
paddingBottom={metadataVisible() ? 1 : 0}
paddingTop={1}
gap={1}
flexWrap="wrap"
>
<box flexDirection="row" paddingBottom={metadataVisible() ? 1 : 0} paddingTop={1} gap={1} flexWrap="wrap">
<For each={files()}>
{(file) => {
const directory = file.mime === "application/x-directory"
@@ -1452,6 +1454,10 @@ function AssistantMessage(props: { message: SessionMessageAssistant; last: boole
if (!props.message.time.completed) return 0
return props.message.time.completed - props.message.time.created
})
const contentFiltered = createMemo(() => props.message.finish === "content-filter")
const errorText = createMemo(() =>
contentFiltered() ? "The response was blocked by the provider's content filter" : errorMessage(props.message.error),
)
const exploration = createMemo(() => {
const grouped = new Map<string, { first: boolean; parts: SessionMessageAssistantTool[]; active: boolean }>()
@@ -1514,7 +1520,7 @@ function AssistantMessage(props: { message: SessionMessageAssistant; last: boole
</Switch>
)}
</For>
<Show when={props.message.error}>
<Show when={props.message.error || contentFiltered()}>
<box
border={["left"]}
paddingTop={1}
@@ -1524,14 +1530,19 @@ function AssistantMessage(props: { message: SessionMessageAssistant; last: boole
customBorderChars={SplitBorder.customBorderChars}
borderColor={theme.error}
>
<text fg={theme.textMuted}>{errorMessage(props.message.error)}</text>
<text fg={theme.textMuted}>{errorText()}</text>
</box>
</Show>
<Switch>
<Match when={props.last || final() || props.message.error}>
<Match when={props.last || final() || props.message.error || contentFiltered()}>
<box paddingLeft={3}>
<text>
<span style={{ fg: props.message.error ? theme.textMuted : local.agent.color(props.message.agent) }}>
<span
style={{
fg:
props.message.error || contentFiltered() ? theme.textMuted : local.agent.color(props.message.agent),
}}
>
{Locale.titlecase(props.message.agent)}
</span>
<span style={{ fg: theme.textMuted }}> · {model()}</span>
@@ -1722,7 +1733,8 @@ function ToolPart(props: { part: SessionMessageAssistantTool }) {
return Boolean(shellID && data.shell.get(shellID))
}
if (display() === "subagent") {
const sessionID = stringValue(props.part.state.structured.sessionID) ?? stringValue(props.part.state.structured.sessionId)
const sessionID =
stringValue(props.part.state.structured.sessionID) ?? stringValue(props.part.state.structured.sessionId)
return Boolean(sessionID && data.session.status(sessionID) === "running")
}
return false
@@ -95,9 +95,7 @@ test("completes exploration groups when another row follows", () => {
])
finished.finish = "stop"
const messages: SessionMessage[] = [
assistant("assistant-1", [
{ type: "tool", id: "read-1", name: "read", state: pending(), time: { created: 1 } },
]),
assistant("assistant-1", [{ type: "tool", id: "read-1", name: "read", state: pending(), time: { created: 1 } }]),
{ type: "user", id: "user-1", text: "Continue", time: { created: 2 } },
finished,
]
@@ -122,6 +120,13 @@ test("completes exploration groups when another row follows", () => {
])
})
test("adds a footer for content-filter assistant finishes without parts", () => {
const filtered = assistant("assistant-filtered", [])
filtered.finish = "content-filter"
expect(reduceSessionRows([filtered])).toEqual([{ type: "assistant-footer", messageID: "assistant-filtered" }])
})
test("hides synthetic messages without descriptions", () => {
const messages: SessionMessage[] = [
assistant("assistant-1", [{ type: "tool", id: "read-1", name: "read", state: pending(), time: { created: 1 } }]),