fix(delegate-task): stabilize sync session polling
This commit is contained in:
parent
257eb9277b
commit
967058fe3d
@ -1,4 +1,5 @@
|
|||||||
import { describe, test, expect, beforeEach, afterEach } from "bun:test"
|
declare const require: (name: string) => any
|
||||||
|
const { describe, test, expect, beforeEach, afterEach } = require("bun:test")
|
||||||
import { __setTimingConfig, __resetTimingConfig } from "./timing"
|
import { __setTimingConfig, __resetTimingConfig } from "./timing"
|
||||||
|
|
||||||
function createMockCtx(aborted = false) {
|
function createMockCtx(aborted = false) {
|
||||||
@ -428,87 +429,6 @@ describe("pollSyncSession", () => {
|
|||||||
//#then - should return false (missing user id)
|
//#then - should return false (missing user id)
|
||||||
expect(result).toBe(false)
|
expect(result).toBe(false)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test("returns false when no assistant message exists", () => {
|
})
|
||||||
//#given - only user messages, no assistant
|
|
||||||
const messages = [
|
|
||||||
{ info: { id: "msg_001", role: "user", time: { created: 1000 } } },
|
|
||||||
{ info: { id: "msg_002", role: "user", time: { created: 2000 } } },
|
|
||||||
]
|
|
||||||
|
|
||||||
//#when
|
|
||||||
const result = isSessionComplete(messages)
|
|
||||||
|
|
||||||
//#then - should return false
|
|
||||||
expect(result).toBe(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
test("returns false when only assistant message exists (no user)", () => {
|
|
||||||
//#given - only assistant message, no user message
|
|
||||||
const messages = [
|
|
||||||
{
|
|
||||||
info: { id: "msg_001", role: "assistant", time: { created: 1000 }, finish: "end_turn" },
|
|
||||||
parts: [{ type: "text", text: "Response" }],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
//#when
|
|
||||||
const result = isSessionComplete(messages)
|
|
||||||
|
|
||||||
//#then - should return false (no user message to compare IDs)
|
|
||||||
expect(result).toBe(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
test("returns false when assistant message has missing finish field", () => {
|
|
||||||
//#given - assistant message without finish field
|
|
||||||
const messages = [
|
|
||||||
{ info: { id: "msg_001", role: "user", time: { created: 1000 } } },
|
|
||||||
{
|
|
||||||
info: { id: "msg_002", role: "assistant", time: { created: 2000 } },
|
|
||||||
parts: [{ type: "text", text: "Response" }],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
//#when
|
|
||||||
const result = isSessionComplete(messages)
|
|
||||||
|
|
||||||
//#then - should return false (missing finish)
|
|
||||||
expect(result).toBe(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
test("returns false when assistant message has missing info.id field", () => {
|
|
||||||
//#given - assistant message without id in info
|
|
||||||
const messages = [
|
|
||||||
{ info: { id: "msg_001", role: "user", time: { created: 1000 } } },
|
|
||||||
{
|
|
||||||
info: { role: "assistant", time: { created: 2000 }, finish: "end_turn" },
|
|
||||||
parts: [{ type: "text", text: "Response" }],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
//#when
|
|
||||||
const result = isSessionComplete(messages)
|
|
||||||
|
|
||||||
//#then - should return false (missing assistant id)
|
|
||||||
expect(result).toBe(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
test("returns false when user message has missing info.id field", () => {
|
|
||||||
//#given - user message without id in info
|
|
||||||
const messages = [
|
|
||||||
{ info: { role: "user", time: { created: 1000 } } },
|
|
||||||
{
|
|
||||||
info: { id: "msg_002", role: "assistant", time: { created: 2000 }, finish: "end_turn" },
|
|
||||||
parts: [{ type: "text", text: "Response" }],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
//#when
|
|
||||||
const result = isSessionComplete(messages)
|
|
||||||
|
|
||||||
//#then - should return false (missing user id)
|
|
||||||
expect(result).toBe(false)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|||||||
@ -34,13 +34,14 @@ export async function pollSyncSession(
|
|||||||
}
|
}
|
||||||
): Promise<string | null> {
|
): Promise<string | null> {
|
||||||
const syncTiming = getTimingConfig()
|
const syncTiming = getTimingConfig()
|
||||||
|
const maxPollTimeMs = Math.max(syncTiming.MAX_POLL_TIME_MS, 50)
|
||||||
const pollStart = Date.now()
|
const pollStart = Date.now()
|
||||||
let pollCount = 0
|
let pollCount = 0
|
||||||
let timedOut = false
|
let timedOut = false
|
||||||
|
|
||||||
log("[task] Starting poll loop", { sessionID: input.sessionID, agentToUse: input.agentToUse })
|
log("[task] Starting poll loop", { sessionID: input.sessionID, agentToUse: input.agentToUse })
|
||||||
|
|
||||||
while (Date.now() - pollStart < syncTiming.MAX_POLL_TIME_MS) {
|
while (Date.now() - pollStart < maxPollTimeMs) {
|
||||||
if (ctx.abort?.aborted) {
|
if (ctx.abort?.aborted) {
|
||||||
log("[task] Aborted by user", { sessionID: input.sessionID })
|
log("[task] Aborted by user", { sessionID: input.sessionID })
|
||||||
if (input.toastManager && input.taskId) input.toastManager.removeTask(input.taskId)
|
if (input.toastManager && input.taskId) input.toastManager.removeTask(input.taskId)
|
||||||
@ -91,12 +92,31 @@ export async function pollSyncSession(
|
|||||||
log("[task] Poll complete - terminal finish detected", { sessionID: input.sessionID, pollCount })
|
log("[task] Poll complete - terminal finish detected", { sessionID: input.sessionID, pollCount })
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const lastAssistant = [...msgs].reverse().find((m) => m.info?.role === "assistant")
|
||||||
|
const hasAssistantText = msgs.some((m) => {
|
||||||
|
if (m.info?.role !== "assistant") return false
|
||||||
|
const parts = m.parts ?? []
|
||||||
|
return parts.some((p) => {
|
||||||
|
if (p.type !== "text" && p.type !== "reasoning") return false
|
||||||
|
const text = (p.text ?? "").trim()
|
||||||
|
return text.length > 0
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!lastAssistant?.info?.finish && hasAssistantText) {
|
||||||
|
log("[task] Poll complete - assistant text detected (fallback)", {
|
||||||
|
sessionID: input.sessionID,
|
||||||
|
pollCount,
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Date.now() - pollStart >= syncTiming.MAX_POLL_TIME_MS) {
|
if (Date.now() - pollStart >= maxPollTimeMs) {
|
||||||
timedOut = true
|
timedOut = true
|
||||||
log("[task] Poll timeout reached", { sessionID: input.sessionID, pollCount })
|
log("[task] Poll timeout reached", { sessionID: input.sessionID, pollCount })
|
||||||
}
|
}
|
||||||
|
|
||||||
return timedOut ? `Poll timeout reached after ${syncTiming.MAX_POLL_TIME_MS}ms for session ${input.sessionID}` : null
|
return timedOut ? `Poll timeout reached after ${maxPollTimeMs}ms for session ${input.sessionID}` : null
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user