fix(prompt-gate): detect finish-only tool waits
This commit is contained in:
@@ -356,6 +356,86 @@ describe("dispatchInternalPrompt shared gate behavior", () => {
|
||||
expect(promptCalls).toBe(0)
|
||||
})
|
||||
|
||||
test("#given latest assistant turn has a tool-calls finish without pending part state #when an internal promptAsync is requested #then no prompt is sent", async () => {
|
||||
// given
|
||||
let promptCalls = 0
|
||||
const client = {
|
||||
session: {
|
||||
status: async () => ({ data: { ses_finish_waiting_tools: { type: "idle" } } }),
|
||||
messages: async () => ({
|
||||
data: [
|
||||
{
|
||||
info: { id: "msg_user", role: "user" },
|
||||
parts: [{ type: "text", text: "run work" }],
|
||||
},
|
||||
{
|
||||
info: { id: "msg_assistant", role: "assistant", finish: "tool-calls" },
|
||||
parts: [{ type: "tool_use", id: "toolu_pending" }],
|
||||
},
|
||||
],
|
||||
}),
|
||||
promptAsync: async () => {
|
||||
promptCalls += 1
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// when
|
||||
const result = await dispatchInternalPrompt({
|
||||
mode: "async",
|
||||
client,
|
||||
sessionID: "ses_finish_waiting_tools",
|
||||
input: { path: { id: "ses_finish_waiting_tools" }, body: { parts: [] } },
|
||||
source: "test:finish-waiting-tools",
|
||||
settleMs: 0,
|
||||
postDispatchHoldMs: 0,
|
||||
})
|
||||
|
||||
// then
|
||||
expect(result.status).toBe("active")
|
||||
expect(promptCalls).toBe(0)
|
||||
})
|
||||
|
||||
test("#given latest assistant turn has a running tool-call part #when an internal promptAsync is requested #then no prompt is sent", async () => {
|
||||
// given
|
||||
let promptCalls = 0
|
||||
const client = {
|
||||
session: {
|
||||
status: async () => ({ data: { ses_tool_call_part: { type: "idle" } } }),
|
||||
messages: async () => ({
|
||||
data: [
|
||||
{
|
||||
info: { id: "msg_user", role: "user" },
|
||||
parts: [{ type: "text", text: "run work" }],
|
||||
},
|
||||
{
|
||||
info: { id: "msg_assistant", role: "assistant" },
|
||||
parts: [{ type: "tool-call", id: "call_pending", state: { status: "running" } }],
|
||||
},
|
||||
],
|
||||
}),
|
||||
promptAsync: async () => {
|
||||
promptCalls += 1
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// when
|
||||
const result = await dispatchInternalPrompt({
|
||||
mode: "async",
|
||||
client,
|
||||
sessionID: "ses_tool_call_part",
|
||||
input: { path: { id: "ses_tool_call_part" }, body: { parts: [] } },
|
||||
source: "test:tool-call-part",
|
||||
settleMs: 0,
|
||||
postDispatchHoldMs: 0,
|
||||
})
|
||||
|
||||
// then
|
||||
expect(result.status).toBe("active")
|
||||
expect(promptCalls).toBe(0)
|
||||
})
|
||||
|
||||
test("#given internal user tail follows an assistant waiting on tools #when an internal promptAsync is requested #then no prompt is sent", async () => {
|
||||
// given
|
||||
let promptCalls = 0
|
||||
|
||||
@@ -203,6 +203,17 @@ function messageRole(message: unknown): string | undefined {
|
||||
return typeof message.role === "string" ? message.role : undefined
|
||||
}
|
||||
|
||||
function messageFinish(message: unknown): string | undefined {
|
||||
if (!isRecord(message)) {
|
||||
return undefined
|
||||
}
|
||||
const info = message.info
|
||||
if (isRecord(info) && typeof info.finish === "string") {
|
||||
return info.finish
|
||||
}
|
||||
return typeof message.finish === "string" ? message.finish : undefined
|
||||
}
|
||||
|
||||
function toInternalInitiatorTextPartLike(part: unknown): InternalInitiatorTextPartLike {
|
||||
const result: InternalInitiatorTextPartLike = {}
|
||||
if (!isRecord(part)) {
|
||||
@@ -249,7 +260,12 @@ function partIsWaitingOnTool(part: unknown): boolean {
|
||||
if (!isRecord(part)) {
|
||||
return false
|
||||
}
|
||||
if (part.type !== "tool" && part.type !== "tool_use") {
|
||||
if (
|
||||
part.type !== "tool"
|
||||
&& part.type !== "tool_use"
|
||||
&& part.type !== "tool-call"
|
||||
&& part.type !== "tool-invocation"
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -266,9 +282,9 @@ function latestAssistantTurnIsWaitingOnTools(messages: unknown[]): boolean {
|
||||
const role = messageRole(message)
|
||||
if (role === "assistant") {
|
||||
if (!isRecord(message) || !Array.isArray(message.parts)) {
|
||||
return false
|
||||
return messageFinish(message) === "tool-calls"
|
||||
}
|
||||
return message.parts.some(partIsWaitingOnTool)
|
||||
return messageFinish(message) === "tool-calls" || message.parts.some(partIsWaitingOnTool)
|
||||
}
|
||||
if (role === "user") {
|
||||
if (messageIsSyntheticOrInternalUser(message)) {
|
||||
|
||||
Reference in New Issue
Block a user