-
Notifications
You must be signed in to change notification settings - Fork 9
fix(agent): enforce approval gate on allowFinalResponse path and normalize approval-predicate args #55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
fix(agent): enforce approval gate on allowFinalResponse path and normalize approval-predicate args #55
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@openrouter/agent": patch | ||
| --- | ||
|
|
||
| Run the approval check before executing pending tool calls on the `allowFinalResponse` path, and evaluate function-form `requireApproval` predicates against schema-normalized arguments so the approval decision matches the executed input. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -308,6 +308,36 @@ describe('Conversation State Utilities', () => { | |
| ).toBe(true); | ||
| }); | ||
|
|
||
| it('should apply schema defaults before function-based approval checks', async () => { | ||
| const toolWithDefaultedApproval = tool({ | ||
| name: 'defaulted_action', | ||
| inputSchema: z.object({ | ||
| destructive: z.boolean().default(true), | ||
| }), | ||
| requireApproval: (params) => params.destructive === true, | ||
| execute: async () => ({}), | ||
| }); | ||
|
|
||
| const toolCall = { | ||
| id: '1', | ||
| name: 'defaulted_action', | ||
| arguments: {}, | ||
| }; | ||
|
|
||
| expect( | ||
| await toolRequiresApproval( | ||
| toolCall, | ||
| [ | ||
| toolWithDefaultedApproval, | ||
| ], | ||
| context, | ||
| ), | ||
| ).toBe(true); | ||
| expect(toolCall.arguments).toEqual({ | ||
| destructive: true, | ||
| }); | ||
| }); | ||
|
|
||
| it('should support async function-based tool-level requireApproval', async () => { | ||
| // Tool with async function-based approval | ||
| const toolWithAsyncApproval = tool({ | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [suggestion] DetailsWhy: This PR moves Fix: Add a test case in the it('should apply schema defaults before call-level requireApproval', async () => {
const tool = ...({ inputSchema: z.object({ flag: z.boolean().default(true) }) });
const seenArgs: unknown[] = [];
const toolCall = { id: '1', name: 'x', arguments: {} };
await toolRequiresApproval(toolCall, [tool], context, (tc) => {
seenArgs.push(tc.arguments);
return false;
});
expect(seenArgs[0]).toEqual({ flag: true }); // schema default applied
});Prompt for agentsReviewed at |
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[suggestion]
conversation-state.ts:165— comment overstates thealways-a-recordguarantee afternormalizeToolCallArgumentsDetails
Why: The comment says "Arguments have already been parsed and validated against the tool's Zod inputSchema (a ZodObject), so the runtime shape is always a record here." Before this PR that was true because
execute()ranz4.parse(which throws on failure) beforetoolRequiresApprovalwas ever called. NownormalizeToolCallArgumentsusesz4.safeParse, which silently preserves the original arguments on failure. If the model emits arguments that don't satisfy the schema,toolCall.argumentsat this point could still be the raw model output — not necessarily an object. TheisRecordguard immediately below catches this, so correctness is unaffected, but the comment's "always" claim is now misleading to future readers.Fix: Soften the comment to reflect the new reality, e.g.:
Prompt for agents
Reviewed at
d3569e7