fix(tool-goal): scope the wrap-up no-more-tools clause to the current run

The injected instruction persists as a durable user message, so an
unscoped 'Do not call any more tools.' stays in every later request's
history. Scope it: '...in this run; further work waits for the user's
next instruction.' A/B probes on deepseek-v4-pro show the scoped wording
is non-inferior in-turn (4/4 zero tool calls, closing quality unchanged)
and next-turn tool use is unaffected under both wordings; the scoped
form states the instruction's actual lifetime.
This commit is contained in:
ZiyaZhang
2026-08-02 07:29:18 -07:00
parent 820a5a97f1
commit e72978ba98
3 changed files with 6 additions and 5 deletions

View File

@@ -24,7 +24,7 @@ export function renderWrapupContext(objective: string, blockedReason?: string):
+ 'verified, and point to the concrete results (files, commits, or other artifacts). '
+ GROUNDING
+ 'Note anything the user should review or do next. Address the user directly. Do not '
+ 'call any more tools.\n'
+ "call any more tools in this run; further work waits for the user's next instruction.\n"
+ '</goal_complete>'
: '<goal_blocked>\n'
+ heading
@@ -34,7 +34,8 @@ export function renderWrapupContext(objective: string, blockedReason?: string):
+ 'blocking condition and what you tried, and say exactly what you need from the user to '
+ 'continue. '
+ GROUNDING
+ 'Address the user directly. Do not call any more tools.\n'
+ 'Address the user directly. Do not call any more tools in this run; further work '
+ "waits for the user's next instruction.\n"
+ '</goal_blocked>'
return [{ type: 'text', text }]
}

View File

@@ -377,7 +377,7 @@ describe('goal tool state transitions', () => {
if (block?.type !== 'text') throw new Error('expected one text wrap-up block')
expect(block.text).toContain('<goal_complete>')
expect(block.text).toContain('"pause cleanly"')
expect(block.text).toContain('Do not call any more tools.')
expect(block.text).toContain("Do not call any more tools in this run; further work waits for the user's next instruction.")
})
it('completes without a wrap-up instruction under direct human authority', async () => {
@@ -578,7 +578,7 @@ describe('goal tool state transitions', () => {
if (block?.type !== 'text') throw new Error('expected one text wrap-up block')
expect(block.text).toContain('<goal_blocked>')
expect(block.text).toContain('The required credential is still unavailable.')
expect(block.text).toContain('Do not call any more tools.')
expect(block.text).toContain("Do not call any more tools in this run; further work waits for the user's next instruction.")
})
it('lets direct human authority block before the model threshold', async () => {