mirror of
https://github.com/anomalyco/opencode.git
synced 2026-07-10 04:38:31 +00:00
fix(tui): clear stale tool preparation state
This commit is contained in:
parent
f9d1d3b259
commit
87eb062238
2 changed files with 112 additions and 1 deletions
|
|
@ -704,7 +704,9 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
|
|||
const messages = [
|
||||
...loaded.map((message) => {
|
||||
if (message.type === "user") return message
|
||||
return liveByID.get(message.id) ?? message
|
||||
const live = liveByID.get(message.id)
|
||||
if (!live || ("completed" in message.time && message.time.completed !== undefined)) return message
|
||||
return live
|
||||
}),
|
||||
...live.filter((message) => !loadedIDs.has(message.id)),
|
||||
].toSorted((a, b) => a.time.created - b.time.created)
|
||||
|
|
|
|||
|
|
@ -108,6 +108,115 @@ test("refreshes resources into reactive getters", async () => {
|
|||
}
|
||||
})
|
||||
|
||||
test("refresh replaces stale completed messages with the terminal server projection", async () => {
|
||||
const events = createEventStream()
|
||||
const sessionID = "session-stale-tool"
|
||||
const messageID = "message-stale-tool"
|
||||
const calls = createFetch((url) => {
|
||||
if (url.pathname === `/api/session/${sessionID}/message`)
|
||||
return json({
|
||||
data: [
|
||||
{
|
||||
id: messageID,
|
||||
type: "assistant",
|
||||
agent: "build",
|
||||
model: { id: "model", providerID: "provider" },
|
||||
content: [
|
||||
{
|
||||
type: "tool",
|
||||
id: "call-write",
|
||||
name: "write",
|
||||
provider: { executed: false },
|
||||
state: {
|
||||
status: "error",
|
||||
input: {},
|
||||
content: [],
|
||||
structured: {},
|
||||
error: { type: "unknown", message: "Tool execution interrupted" },
|
||||
},
|
||||
time: { created: 1, completed: 3 },
|
||||
},
|
||||
],
|
||||
finish: "error",
|
||||
error: { type: "unknown", message: "Step interrupted" },
|
||||
time: { created: 1, completed: 3 },
|
||||
},
|
||||
],
|
||||
cursor: {},
|
||||
})
|
||||
return undefined
|
||||
}, events)
|
||||
let data!: ReturnType<typeof useData>
|
||||
|
||||
function Probe() {
|
||||
data = useData()
|
||||
return <box />
|
||||
}
|
||||
|
||||
const app = await testRender(() => (
|
||||
<TestTuiContexts>
|
||||
<SDKProvider client={createClient(calls.fetch)} api={createApi(calls.fetch)}>
|
||||
<ProjectProvider>
|
||||
<DataProvider>
|
||||
<Probe />
|
||||
</DataProvider>
|
||||
</ProjectProvider>
|
||||
</SDKProvider>
|
||||
</TestTuiContexts>
|
||||
))
|
||||
|
||||
try {
|
||||
emitEvent(events, {
|
||||
id: "evt_step_started_stale_tool",
|
||||
created: 1,
|
||||
type: "session.step.started",
|
||||
durable: durable(sessionID),
|
||||
data: {
|
||||
sessionID,
|
||||
assistantMessageID: messageID,
|
||||
agent: "build",
|
||||
model: { id: "model", providerID: "provider" },
|
||||
},
|
||||
})
|
||||
emitEvent(events, {
|
||||
id: "evt_tool_started_stale_tool",
|
||||
created: 2,
|
||||
type: "session.tool.input.started",
|
||||
durable: durable(sessionID, 1),
|
||||
data: {
|
||||
sessionID,
|
||||
assistantMessageID: messageID,
|
||||
callID: "call-write",
|
||||
name: "write",
|
||||
},
|
||||
})
|
||||
emitEvent(events, {
|
||||
id: "evt_step_failed_stale_tool",
|
||||
created: 3,
|
||||
type: "session.step.failed",
|
||||
durable: durable(sessionID, 2),
|
||||
data: {
|
||||
sessionID,
|
||||
assistantMessageID: messageID,
|
||||
error: { type: "unknown", message: "Step interrupted" },
|
||||
},
|
||||
})
|
||||
|
||||
await wait(() => {
|
||||
const message = data.session.message.get(sessionID, messageID)
|
||||
return message?.type === "assistant" && message.time.completed === 3
|
||||
})
|
||||
await data.session.message.refresh(sessionID)
|
||||
|
||||
const message = data.session.message.get(sessionID, messageID)
|
||||
expect(message?.type).toBe("assistant")
|
||||
if (message?.type !== "assistant") return
|
||||
expect(message.content[0]).toMatchObject({ type: "tool", state: { status: "error" } })
|
||||
} finally {
|
||||
app.renderer.destroy()
|
||||
}
|
||||
})
|
||||
|
||||
test("reconnects the event stream and bootstraps fresh data", async () => {
|
||||
const events = createEventStream()
|
||||
const requests = { active: 0, event: 0, model: 0 }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue