test(runtime-fallback,prompt-gate): add red tests for event-shape and finish-marker blind spots (BUG-C+D)
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -149,6 +149,70 @@ describe("first-prompt-watchdog", () => {
|
|||||||
watchdog.dispose()
|
watchdog.dispose()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("#given session emits message.part.updated with sessionID under properties.part #when watchdog tracks #then the watchdog recognizes progress and resets the silence timer", async () => {
|
||||||
|
// given
|
||||||
|
const sessionID = "session-nested-part-progress"
|
||||||
|
subagentSessions.add(sessionID)
|
||||||
|
const deps = createDeps(PLUGIN_CONFIG_WITH_FALLBACK)
|
||||||
|
const calls: RecordedCalls = { abort: [], autoRetry: [] }
|
||||||
|
const helpers = createHelpers(calls, AGENT)
|
||||||
|
const watchdog = createFirstPromptWatchdog(deps, helpers, WATCHDOG_MS)
|
||||||
|
|
||||||
|
// when
|
||||||
|
watchdog.onUserMessage(sessionID, PRIMARY_MODEL, AGENT)
|
||||||
|
await wait(SAFE_WAIT_BEFORE_FIRE_MS)
|
||||||
|
observeEventForWatchdog(
|
||||||
|
{
|
||||||
|
type: "message.part.updated",
|
||||||
|
properties: {
|
||||||
|
part: {
|
||||||
|
id: "part-1",
|
||||||
|
messageID: "msg-1",
|
||||||
|
sessionID,
|
||||||
|
type: "text",
|
||||||
|
text: "still working",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watchdog,
|
||||||
|
)
|
||||||
|
await wait(SAFE_WAIT_AFTER_FIRE_MS)
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(calls.abort).toEqual([])
|
||||||
|
expect(calls.autoRetry).toEqual([])
|
||||||
|
|
||||||
|
watchdog.dispose()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("#given session emits message.part.delta with field/delta but no part.type #when watchdog tracks #then the watchdog recognizes progress", async () => {
|
||||||
|
// given
|
||||||
|
const sessionID = "session-delta-progress"
|
||||||
|
subagentSessions.add(sessionID)
|
||||||
|
const deps = createDeps(PLUGIN_CONFIG_WITH_FALLBACK)
|
||||||
|
const calls: RecordedCalls = { abort: [], autoRetry: [] }
|
||||||
|
const helpers = createHelpers(calls, AGENT)
|
||||||
|
const watchdog = createFirstPromptWatchdog(deps, helpers, WATCHDOG_MS)
|
||||||
|
|
||||||
|
// when
|
||||||
|
watchdog.onUserMessage(sessionID, PRIMARY_MODEL, AGENT)
|
||||||
|
await wait(SAFE_WAIT_BEFORE_FIRE_MS)
|
||||||
|
observeEventForWatchdog(
|
||||||
|
{
|
||||||
|
type: "message.part.delta",
|
||||||
|
properties: { sessionID, field: "text", delta: "x" },
|
||||||
|
},
|
||||||
|
watchdog,
|
||||||
|
)
|
||||||
|
await wait(SAFE_WAIT_AFTER_FIRE_MS)
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(calls.abort).toEqual([])
|
||||||
|
expect(calls.autoRetry).toEqual([])
|
||||||
|
|
||||||
|
watchdog.dispose()
|
||||||
|
})
|
||||||
|
|
||||||
it("#given the session is not a subagent #when a user message is observed #then the watchdog never arms and nothing fires", async () => {
|
it("#given the session is not a subagent #when a user message is observed #then the watchdog never arms and nothing fires", async () => {
|
||||||
// given
|
// given
|
||||||
const sessionID = "session-not-a-subagent"
|
const sessionID = "session-not-a-subagent"
|
||||||
|
|||||||
@@ -751,6 +751,86 @@ describe("dispatchInternalPrompt shared gate behavior", () => {
|
|||||||
expect(promptCalls).toBe(0)
|
expect(promptCalls).toBe(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("#given latest assistant turn has boolean finish === true #when checking blocks #then it is treated as terminal (NOT blocking)", async () => {
|
||||||
|
// given
|
||||||
|
let promptCalls = 0
|
||||||
|
const client = {
|
||||||
|
session: {
|
||||||
|
status: async () => ({ data: { ses_boolean_finish: { type: "idle" } } }),
|
||||||
|
messages: async () => ({
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
info: { id: "msg_user", role: "user" },
|
||||||
|
parts: [{ type: "text", text: "run work" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
info: { id: "msg_assistant", role: "assistant", finish: true },
|
||||||
|
parts: [{ type: "text", text: "done" }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
promptAsync: async () => {
|
||||||
|
promptCalls += 1
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = await dispatchInternalPrompt({
|
||||||
|
mode: "async",
|
||||||
|
client,
|
||||||
|
sessionID: "ses_boolean_finish",
|
||||||
|
input: { path: { id: "ses_boolean_finish" }, body: { parts: [] } },
|
||||||
|
source: "test:boolean-finish",
|
||||||
|
settleMs: 0,
|
||||||
|
postDispatchHoldMs: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result.status).toBe("dispatched")
|
||||||
|
expect(promptCalls).toBe(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("#given latest assistant turn has info.time.completed but no finish field #when checking blocks #then it is treated as terminal", async () => {
|
||||||
|
// given
|
||||||
|
let promptCalls = 0
|
||||||
|
const client = {
|
||||||
|
session: {
|
||||||
|
status: async () => ({ data: { ses_completed_time: { type: "idle" } } }),
|
||||||
|
messages: async () => ({
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
info: { id: "msg_user", role: "user" },
|
||||||
|
parts: [{ type: "text", text: "run work" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
info: { id: "msg_assistant", role: "assistant", time: { completed: 1_762_000_000_000 } },
|
||||||
|
parts: [{ type: "text", text: "done" }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
promptAsync: async () => {
|
||||||
|
promptCalls += 1
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = await dispatchInternalPrompt({
|
||||||
|
mode: "async",
|
||||||
|
client,
|
||||||
|
sessionID: "ses_completed_time",
|
||||||
|
input: { path: { id: "ses_completed_time" }, body: { parts: [] } },
|
||||||
|
source: "test:completed-time",
|
||||||
|
settleMs: 0,
|
||||||
|
postDispatchHoldMs: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result.status).toBe("dispatched")
|
||||||
|
expect(promptCalls).toBe(1)
|
||||||
|
})
|
||||||
|
|
||||||
test("#given internal user tail follows an assistant waiting on tools #when an internal promptAsync is requested #then no prompt is sent", async () => {
|
test("#given internal user tail follows an assistant waiting on tools #when an internal promptAsync is requested #then no prompt is sent", async () => {
|
||||||
// given
|
// given
|
||||||
let promptCalls = 0
|
let promptCalls = 0
|
||||||
|
|||||||
Reference in New Issue
Block a user