fix: preserve failed PTC image result forwarding semantics

This commit is contained in:
creatixchu committed 2026-09-21 17:17:03 +08:00
1 parent 14c1fec958
commit b0be6e79c2
2 files changed
+19 -4

No files matched your search

+1 -1
View File
@@ -140,7 +140,7 @@ export function apply(ctx: Context, config: Config): void {
const retained = await bound(exec, content, exec.name, exec.callId, exec.parent === undefined ? 'result' : 'dispatch')
if (retained === undefined) return decision
const additionalContexts = [...decision.additionalContexts ?? []]
if (exec.parent !== undefined && hasImages && !retained.some(block => block.type === 'image')) {
if (exec.parent !== undefined && !result.isError && hasImages && !retained.some(block => block.type === 'image')) {
additionalContexts.push(createUserMessage({ content: retained, source: { kind: 'ptc-mode' } }))
}
return {
@@ -176,9 +176,20 @@ describe('multimodal spill', () => {
expect(warning).toHaveBeenCalledWith(expect.stringContaining('inconsistent occurrence count'))
})
it('preserves the complete PTC value and forwards recovery text when every image is omitted', async () => {
it.each([false, true])('forwards omitted-image recovery only for successful PTC results (failure: %s)', async (failure) => {
const original = [text('A'.repeat(4000)), image(), text('C'.repeat(4000))]
const { ctx, execute, session } = await setup(original, 200)
if (failure) {
const attachment = await ctx.attachments.saveImage({ data: PNG, mediaType: 'image/png' })
ctx.on('tools/execute', async (exec, next) => {
if (exec.name !== 'inspect') return next()
await next()
return { isError: true, error: { message: 'capture failed' }, content: [
{ type: 'text', text: 'A'.repeat(4000) }, { type: 'image', attachment },
{ type: 'text', text: 'C'.repeat(4000) },
] }
})
}
class BindingRuntime extends PtcRuntime {
readonly language = 'typescript'
readonly isolation = 'fixture'
@@ -186,14 +197,18 @@ describe('multimodal spill', () => {
async run(spec: PtcRunSpec): Promise<PtcRunResult> {
const binding = spec.bindings.find(item => item.global === 'tools')?.functions.inspect
if (binding === undefined) throw new Error('missing inspect binding')
const value = await binding({})
expect(value).toEqual({ content: original })
if (failure) await expect(binding({})).rejects.toThrow('capture failed')
else expect(await binding({})).toEqual({ content: original })
return { logs: [], value: 'complete' }
}
}
await ctx.plugin(BindingRuntime)
const result = await execute('run_code')
expect(result.isError).toBe(false)
if (failure) {
expect(result.additionalContexts).toBeUndefined()
return
}
expect(result.additionalContexts).toHaveLength(1)
const context = result.additionalContexts?.[0]
expect(context?.content.every(block => block.type === 'text')).toBe(true)