mirror of
https://gitee.com/zhangsken/opencode.git
synced 2026-10-16 10:30:01 +08:00
fix(app): keep Working during reasoning-only turns
This commit is contained in:
@@ -51,6 +51,30 @@ for (const width of [1400, 390]) {
|
||||
})
|
||||
}
|
||||
|
||||
for (const width of [1400, 390]) {
|
||||
test(`keeps Working instead of Used 1 Thought while reasoning is the only activity at ${width}px`, async ({ page }) => {
|
||||
const timeline = await setupTimeline(page, {
|
||||
messages: [
|
||||
userMessage(),
|
||||
assistantMessage([reasoningPart("prt_only_thought", "Still considering the answer.")], { completed: false }),
|
||||
],
|
||||
settings: { timelineDetail: timelinePresets[2].value },
|
||||
viewport: { width, height: 900 },
|
||||
})
|
||||
const working = page.locator('[data-component="session-working"]')
|
||||
const group = page.locator('[data-component="collapsed-tool-group"]')
|
||||
await expect(working).toBeVisible()
|
||||
await expect(group).toHaveCount(0)
|
||||
await expect(page.getByRole("button", { name: "Used 1 Thought" })).toHaveCount(0)
|
||||
|
||||
await timeline.send(partUpdated(toolPart("prt_after_thought", "read", "running", { filePath: "package.json" })))
|
||||
if (width === 390) await page.getByRole("button", { name: "Jump to latest" }).click()
|
||||
await expect(group).toBeVisible()
|
||||
await expect(group).toHaveAttribute("data-timeline-part-ids", `${assistantID}:reasoning:0,prt_after_thought`)
|
||||
await expect(working).toBeVisible()
|
||||
})
|
||||
}
|
||||
|
||||
for (const name of ["shell", "patch", "subagent"] as const) {
|
||||
test(`hides Working during ${name} input and execution, then restores it on completion`, async ({ page }) => {
|
||||
const timeline = await setupTimeline(page, {
|
||||
|
||||
@@ -307,6 +307,16 @@ export namespace Timeline {
|
||||
!lastAssistant?.retry &&
|
||||
lastContent?.type === "reasoning" &&
|
||||
lastContent.time?.completed === undefined
|
||||
const onlyThinking =
|
||||
detail?.thinking.placement === "grouped" &&
|
||||
isActive &&
|
||||
status.type === "busy" &&
|
||||
!!lastAssistant &&
|
||||
!lastAssistant.error &&
|
||||
!lastAssistant.retry &&
|
||||
entries.every(
|
||||
(entry) => entry.type === "assistant" && entry.message.content.every((content) => content.type === "reasoning"),
|
||||
)
|
||||
|
||||
if (previousUserMessage) rows.push(new TimelineRow.TurnGap({ userMessageID: turnID }))
|
||||
if (userMessage) rows.push(new TimelineRow.UserMessage({ userMessageID: turnID }))
|
||||
@@ -320,7 +330,9 @@ export namespace Timeline {
|
||||
contentEntries(message)
|
||||
.filter(
|
||||
(entry) =>
|
||||
isRenderable(entry.content, showReasoning, detail) && !(thinking && entry.content === lastContent),
|
||||
isRenderable(entry.content, showReasoning, detail) &&
|
||||
!(thinking && entry.content === lastContent) &&
|
||||
!(onlyThinking && entry.content.type === "reasoning"),
|
||||
)
|
||||
.map((entry) => ({ messageID: message.id, messageIndex, partID: entry.id, content: entry.content })),
|
||||
)
|
||||
|
||||
@@ -5,6 +5,7 @@ import type {
|
||||
SessionMessageInfo,
|
||||
} from "@opencode/client/promise"
|
||||
import { storyDocument, storyTool } from "../storybook/current-session-scenarios"
|
||||
import { timelinePresets } from "./detail"
|
||||
import { createTimelineProjection, Timeline, TimelineRow } from "./projection"
|
||||
|
||||
describe("current session timeline rows", () => {
|
||||
@@ -267,6 +268,29 @@ describe("current session timeline rows", () => {
|
||||
})
|
||||
})
|
||||
|
||||
test("omits a grouped reasoning-only row while busy until other content or completion", () => {
|
||||
const thought = { type: "reasoning", text: "Thinking", time: { created: 1, completed: 2 } } as const
|
||||
const rows = (content: SessionMessageAssistant["content"], busy: boolean) =>
|
||||
createTimelineProjection({
|
||||
sessionMessages: storyDocument(content, busy).messages,
|
||||
status: { type: busy ? "busy" : "idle" },
|
||||
reasoningMode: "compact",
|
||||
timelineDetail: timelinePresets[2].value,
|
||||
}).rows
|
||||
|
||||
expect(rows([{ ...thought, time: { created: 1 } }], true).map((row) => row._tag)).toEqual(["UserMessage"])
|
||||
expect(rows([thought], true).map((row) => row._tag)).toEqual(["UserMessage"])
|
||||
expect(rows([thought, { type: "text", text: "Answer" }], true).map((row) => row._tag)).toEqual([
|
||||
"UserMessage",
|
||||
"AssistantPart",
|
||||
"AssistantPart",
|
||||
])
|
||||
expect(
|
||||
rows([thought, storyTool("read", "read", "running", { filePath: "package.json" })], true).map((row) => row._tag),
|
||||
).toEqual(["UserMessage", "AssistantPart"])
|
||||
expect(rows([thought], false).map((row) => row._tag)).toEqual(["UserMessage", "AssistantPart"])
|
||||
})
|
||||
|
||||
test("stops thinking on idle, message completion, errors and retries", () => {
|
||||
const document = storyDocument([{ type: "reasoning", text: "Current thought" }], true)
|
||||
expect(
|
||||
|
||||
Reference in New Issue
Block a user