fix(server): handle tool denial interruption #1070

This commit is contained in:
程序员阿江(Relakkes) 2026-07-20 20:38:25 +08:00
parent 6b725a0b6f
commit 7d272e0f96
2 changed files with 43 additions and 1 deletions

View File

@ -0,0 +1,26 @@
import { describe, expect, test } from 'bun:test'
import {
createUserInterruptionMessage,
INTERRUPT_MESSAGE_FOR_TOOL_USE,
} from './messages.js'
import { isResultSuccessful } from './queryHelpers.js'
describe('isResultSuccessful', () => {
test('treats user interruption during tool use as a normal terminal result', () => {
const interruption = createUserInterruptionMessage({ toolUse: true })
expect(isResultSuccessful(interruption, 'tool_use')).toBe(true)
})
test('does not treat an ordinary user prompt as successful after tool_use', () => {
const ordinaryPrompt = createUserInterruptionMessage({ toolUse: true })
ordinaryPrompt.message.content = [
{
type: 'text',
text: `${INTERRUPT_MESSAGE_FOR_TOOL_USE} keep going`,
},
]
expect(isResultSuccessful(ordinaryPrompt, 'tool_use')).toBe(false)
})
})

View File

@ -27,7 +27,12 @@ import {
createFileStateCacheWithSizeLimit,
type FileStateCache,
} from './fileStateCache.js'
import { isNotEmptyMessage, normalizeMessages } from './messages.js'
import {
INTERRUPT_MESSAGE,
INTERRUPT_MESSAGE_FOR_TOOL_USE,
isNotEmptyMessage,
normalizeMessages,
} from './messages.js'
import { expandPath } from './path.js'
import type {
inputSchema as permissionToolInputSchema,
@ -50,6 +55,7 @@ const ASK_READ_FILE_STATE_CACHE_SIZE = 10
* Returns true if:
* - Last message is assistant with text/thinking content
* - Last message is user with only tool_result blocks
* - Last message is a synthetic user interruption
* - Last message is the user prompt but the API completed with end_turn
* (model chose to emit no content blocks)
*/
@ -78,6 +84,16 @@ export function isResultSuccessful(
) {
return true
}
const onlyBlock = Array.isArray(content) ? content[0] : undefined
if (
content.length === 1 &&
onlyBlock?.type === 'text' &&
(onlyBlock.text === INTERRUPT_MESSAGE ||
onlyBlock.text === INTERRUPT_MESSAGE_FOR_TOOL_USE)
) {
return true
}
}
// Carve-out: API completed (message_delta set stop_reason) but yielded