Address Claude review follow-ups

This commit is contained in:
Tianyi Cui
2026-06-19 00:37:12 +08:00
parent 7fa113be0e
commit 4a3f3af296
8 changed files with 92 additions and 19 deletions

View File

@@ -502,6 +502,11 @@ async function runStep(
if (stepError) throw stepError
if (assembler.finish.kind === 'max-tokens') {
let message: Message = withoutToolCalls(assembler.message())
message = withoutToolCalls(await ctx.waterfall('agent/step-result', agent, turn, step, message, () => Promise.resolve(message)))
if (message.content.length > 0) {
session.append('assistant/message', { turn, step, content: message.content })
}
if (assembler.usage) {
session.append('usage', { turn, step, usage: assembler.usage })
}
@@ -566,6 +571,10 @@ async function runStep(
return { hadToolCalls: toolCalls.length > 0, finish: assembler.finish }
}
function withoutToolCalls(message: Message): Message {
return { ...message, content: message.content.filter(block => block.type !== 'tool-call') }
}
/** The last turn number in a (possibly seeded) session log, or 0. */
export function lastTurnNumber(session: Session): number {
const lastStart = session.events.findLast(event => event.type === 'turn/start')

View File

@@ -385,6 +385,10 @@ describe('agent loop', () => {
expect(steps).toBe(2)
expect(adapter.requests).toHaveLength(2)
expect(adapter.requests[1]!.messages).toEqual([
{ role: 'user', content: [{ type: 'text', text: 'go' }] },
{ role: 'assistant', content: [{ type: 'text', text: 'first half' }] },
])
expect(reasons).toEqual([{ kind: 'max-tokens' }])
})
@@ -436,10 +440,38 @@ describe('agent loop', () => {
expect(executions).toBe(0)
expect(agent.session.events.some(e => e.type === 'tool/call')).toBe(false)
expect(agent.session.deriveMessages()).toEqual([{ role: 'user', content: [{ type: 'text', text: 'go' }] }])
expect(reasons).toEqual([{ kind: 'max-tokens' }])
})
it('keeps safe max-tokens assistant content while dropping truncated tool calls', async () => {
const callId = CallId('c1')
const adapter = new MockAdapter([[
{ type: 'block-start', index: 0, blockType: 'text' },
{ type: 'text-delta', index: 0, text: 'partial text' },
{ type: 'block-end', index: 0, block: { type: 'text', text: 'partial text' } },
{ type: 'block-start', index: 1, blockType: 'tool-call' },
{ type: 'tool-call-delta', index: 1, id: callId, name: 'echo', argumentsDelta: '{"text"' },
{ type: 'finish', reason: { kind: 'max-tokens' } },
]])
const ctx = await harness(adapter)
let stepResults = 0
ctx.on('agent/step-result', async (_agent, _turn, _step, message, next) => {
stepResults += 1
expect(message.content).toEqual([{ type: 'text', text: 'partial text' }])
return next()
})
const agent = ctx.agentLoop.create('a1', { model: 'mock' })
send(agent, 'go')
await waitForIdle(ctx, agent)
expect(stepResults).toBe(1)
expect(agent.session.events.some(e => e.type === 'tool/call')).toBe(false)
expect(agent.session.deriveMessages()).toEqual([
{ role: 'user', content: [{ type: 'text', text: 'go' }] },
{ role: 'assistant', content: [{ type: 'text', text: 'partial text' }] },
])
expect(reasons).toEqual([{ kind: 'max-tokens' }])
})
it('stops the turn when agent/step-end listener failure has recorded an error', async () => {