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:
@@ -52,7 +52,7 @@ export function handleBackgroundEvent(args: {
|
||||
|
||||
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
|
||||
const sessionID = getString(props, "sessionID")
|
||||
if (!sessionID) return
|
||||
|
||||
@@ -3413,4 +3413,44 @@ describe("BackgroundManager.handleEvent - non-tool event lastUpdate", () => {
|
||||
//#then - task should still be running (text event refreshed lastUpdate)
|
||||
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 {
|
||||
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
|
||||
const partInfo = props as unknown as MessagePartInfo
|
||||
const sessionID = partInfo?.sessionID
|
||||
|
||||
Reference in New Issue
Block a user