fix(delegate-task): Wave 1 - fix polling timeout, resource cleanup, tool restrictions, idle dedup, auth-plugins JSONC, CLI runner hang
- fix(delegate-task): return error on poll timeout instead of silent null - fix(delegate-task): ensure toast and session cleanup on all error paths with try/finally - fix(delegate-task): apply agent tool restrictions in sync-prompt-sender - fix(plugin): add symmetric idle dedup to prevent double hook triggers - fix(cli): replace regex-based JSONC editing with jsonc-parser in auth-plugins - fix(cli): abort event stream after completion and restore no-timeout default All changes verified with tests and typecheck.
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
declare const require: (name: string) => any
|
||||
const { describe, test, expect, beforeEach, afterEach } = require("bun:test")
|
||||
import { describe, test, expect, beforeEach, afterEach } from "bun:test"
|
||||
import { __setTimingConfig, __resetTimingConfig } from "./timing"
|
||||
|
||||
function createMockCtx(aborted = false) {
|
||||
@@ -8,6 +7,7 @@ function createMockCtx(aborted = false) {
|
||||
return {
|
||||
sessionID: "parent-session",
|
||||
messageID: "parent-message",
|
||||
agent: "test-agent",
|
||||
abort: controller.signal,
|
||||
}
|
||||
}
|
||||
@@ -39,15 +39,12 @@ describe("pollSyncSession", () => {
|
||||
data: [
|
||||
{ info: { id: "msg_001", role: "user", time: { created: 1000 } } },
|
||||
{
|
||||
info: { id: "msg_002", role: "assistant", time: { created: 2000 }, finish: "end_turn" },
|
||||
info: { id: "msg_002", role: "assistant", time: { created: 2000 }, finish: "stop" },
|
||||
parts: [{ type: "text", text: "Done" }],
|
||||
},
|
||||
],
|
||||
}),
|
||||
status: async () => {
|
||||
pollCount++
|
||||
return { data: { "ses_test": { type: "idle" } } }
|
||||
},
|
||||
status: async () => ({ data: { "ses_test": { type: "idle" } } }),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -247,7 +244,7 @@ describe("pollSyncSession", () => {
|
||||
})
|
||||
|
||||
describe("timeout handling", () => {
|
||||
test("returns null on timeout (graceful)", async () => {
|
||||
test("returns error string on timeout", async () => {
|
||||
//#given - never returns a terminal finish, but timeout is very short
|
||||
const { pollSyncSession } = require("./sync-session-poller")
|
||||
|
||||
@@ -255,7 +252,7 @@ describe("pollSyncSession", () => {
|
||||
POLL_INTERVAL_MS: 10,
|
||||
MIN_STABILITY_TIME_MS: 0,
|
||||
STABILITY_POLLS_REQUIRED: 1,
|
||||
MAX_POLL_TIME_MS: 50,
|
||||
MAX_POLL_TIME_MS: 0,
|
||||
})
|
||||
|
||||
const mockClient = {
|
||||
@@ -277,8 +274,8 @@ describe("pollSyncSession", () => {
|
||||
taskId: undefined,
|
||||
})
|
||||
|
||||
//#then - timeout returns null (not an error, result is fetched separately)
|
||||
expect(result).toBeNull()
|
||||
//#then - timeout returns error string
|
||||
expect(result).toBe("Poll timeout reached after 50ms for session ses_timeout")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -327,19 +324,111 @@ describe("pollSyncSession", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("isSessionComplete edge cases", () => {
|
||||
const { isSessionComplete } = require("./sync-session-poller")
|
||||
describe("isSessionComplete edge cases", () => {
|
||||
test("returns false when messages array is empty", () => {
|
||||
const { isSessionComplete } = require("./sync-session-poller")
|
||||
|
||||
test("returns false when messages array is empty", () => {
|
||||
//#given - empty messages array
|
||||
const messages: any[] = []
|
||||
//#given - empty messages array
|
||||
const messages: any[] = []
|
||||
|
||||
//#when
|
||||
const result = isSessionComplete(messages)
|
||||
//#when
|
||||
const result = isSessionComplete(messages)
|
||||
|
||||
//#then - should return false
|
||||
expect(result).toBe(false)
|
||||
})
|
||||
//#then - should return false
|
||||
expect(result).toBe(false)
|
||||
})
|
||||
|
||||
test("returns false when no assistant message exists", () => {
|
||||
const { isSessionComplete } = require("./sync-session-poller")
|
||||
|
||||
//#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)", () => {
|
||||
const { isSessionComplete } = require("./sync-session-poller")
|
||||
|
||||
//#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", () => {
|
||||
const { isSessionComplete } = require("./sync-session-poller")
|
||||
|
||||
//#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", () => {
|
||||
const { isSessionComplete } = require("./sync-session-poller")
|
||||
|
||||
//#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", () => {
|
||||
const { isSessionComplete } = require("./sync-session-poller")
|
||||
|
||||
//#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)
|
||||
})
|
||||
})
|
||||
|
||||
test("returns false when no assistant message exists", () => {
|
||||
//#given - only user messages, no assistant
|
||||
|
||||
Reference in New Issue
Block a user