fix(background-agent): handle message.part.delta for heartbeat (OpenCode >=1.2.0)
OpenCode 1.2.0+ changed reasoning-delta and text-delta to emit 'message.part.delta' instead of 'message.part.updated'. Without handling this event, lastUpdate was only refreshed at reasoning-start and reasoning-end, leaving a gap where extended thinking (>3min) could trigger stale timeout. Accept both event types as heartbeat sources for forward compatibility.
This commit is contained in:
parent
3f557e593c
commit
8a5f61724d
@ -52,7 +52,7 @@ export function handleBackgroundEvent(args: {
|
|||||||
|
|
||||||
const props = event.properties
|
const props = event.properties
|
||||||
|
|
||||||
if (event.type === "message.part.updated") {
|
if (event.type === "message.part.updated" || event.type === "message.part.delta") {
|
||||||
if (!props || !isRecord(props)) return
|
if (!props || !isRecord(props)) return
|
||||||
const sessionID = getString(props, "sessionID")
|
const sessionID = getString(props, "sessionID")
|
||||||
if (!sessionID) return
|
if (!sessionID) return
|
||||||
|
|||||||
@ -3413,4 +3413,44 @@ describe("BackgroundManager.handleEvent - non-tool event lastUpdate", () => {
|
|||||||
//#then - task should still be running (text event refreshed lastUpdate)
|
//#then - task should still be running (text event refreshed lastUpdate)
|
||||||
expect(task.status).toBe("running")
|
expect(task.status).toBe("running")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("should refresh lastUpdate on message.part.delta events (OpenCode >=1.2.0)", async () => {
|
||||||
|
//#given - a running task with stale lastUpdate
|
||||||
|
const client = {
|
||||||
|
session: {
|
||||||
|
prompt: async () => ({}),
|
||||||
|
promptAsync: async () => ({}),
|
||||||
|
abort: async () => ({}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const manager = new BackgroundManager({ client, directory: tmpdir() } as unknown as PluginInput, { staleTimeoutMs: 180_000 })
|
||||||
|
stubNotifyParentSession(manager)
|
||||||
|
|
||||||
|
const task: BackgroundTask = {
|
||||||
|
id: "task-delta-1",
|
||||||
|
sessionID: "session-delta-1",
|
||||||
|
parentSessionID: "parent-1",
|
||||||
|
parentMessageID: "msg-1",
|
||||||
|
description: "Reasoning task with delta events",
|
||||||
|
prompt: "Extended thinking",
|
||||||
|
agent: "oracle",
|
||||||
|
status: "running",
|
||||||
|
startedAt: new Date(Date.now() - 600_000),
|
||||||
|
progress: {
|
||||||
|
toolCalls: 0,
|
||||||
|
lastUpdate: new Date(Date.now() - 300_000),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
getTaskMap(manager).set(task.id, task)
|
||||||
|
|
||||||
|
//#when - a message.part.delta event arrives (reasoning-delta or text-delta in OpenCode >=1.2.0)
|
||||||
|
manager.handleEvent({
|
||||||
|
type: "message.part.delta",
|
||||||
|
properties: { sessionID: "session-delta-1", field: "text", delta: "thinking..." },
|
||||||
|
})
|
||||||
|
await manager["checkAndInterruptStaleTasks"]()
|
||||||
|
|
||||||
|
//#then - task should still be running (delta event refreshed lastUpdate)
|
||||||
|
expect(task.status).toBe("running")
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -660,7 +660,7 @@ export class BackgroundManager {
|
|||||||
handleEvent(event: Event): void {
|
handleEvent(event: Event): void {
|
||||||
const props = event.properties
|
const props = event.properties
|
||||||
|
|
||||||
if (event.type === "message.part.updated") {
|
if (event.type === "message.part.updated" || event.type === "message.part.delta") {
|
||||||
if (!props || typeof props !== "object" || !("sessionID" in props)) return
|
if (!props || typeof props !== "object" || !("sessionID" in props)) return
|
||||||
const partInfo = props as unknown as MessagePartInfo
|
const partInfo = props as unknown as MessagePartInfo
|
||||||
const sessionID = partInfo?.sessionID
|
const sessionID = partInfo?.sessionID
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user