🎖️ fix: Preserve Endpoint Approval Policy Precedence (#15604)

This commit is contained in:
Danny Avila 2026-09-04 11:29:28 -04:00 • committed by GitHub
parent 481c64bed2
commit 3255288f46
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 61 additions and 8 deletions

View file

@ -90,10 +90,22 @@ describe('resolveToolApprovalPolicy', () => {
});
});
test('keeps the BYOM bypass baseline when the endpoint normally uses default mode', () => {
test.each(['default', 'dontAsk', 'bypass'] as const)(
'preserves an enabled endpoint %s policy when BYOM is active',
(mode) => {
const endpoint: TToolApprovalPolicy = {
enabled: true,
mode,
deny: ['dangerous_tool'],
};
expect(resolveToolApprovalPolicy({ endpoint, attachedCodeEnvironment: true })).toBe(endpoint);
},
);
test('uses the BYOM bypass baseline for a configured but inactive endpoint policy', () => {
expect(
resolveToolApprovalPolicy({
endpoint: { enabled: true, mode: 'default', deny: ['dangerous_tool'] },
endpoint: { mode: 'dontAsk', deny: ['dangerous_tool'] },
attachedCodeEnvironment: true,
}),
).toEqual({

View file

@ -56,15 +56,20 @@ export interface ToolApprovalPolicyLayers {
* - `agent` overrides `mode`/`allow`/`deny`/`ask`/`reason`;
* - `skills` may only tighten (add `ask`/`deny`), never loosen.
*
* The BYOM activation adds only `enabled: true, mode: 'bypass'`; an agent-scoped
* hook supplies the risky coding decisions. This avoids prompting managed sibling
* agents in the same graph. An explicit endpoint `enabled: false` remains the
* administrator emergency override. `agent`/`skills` are accepted but not yet merged.
* When no endpoint policy is active, BYOM adds `enabled: true, mode: 'bypass'` and
* an agent-scoped hook supplies its coding decisions. An already-enabled endpoint
* policy remains the run-wide administrative baseline, including its unmatched-tool
* mode. An explicit endpoint `enabled: false` remains the administrator emergency
* override. `agent`/`skills` are accepted but not yet merged.
*/
export function resolveToolApprovalPolicy(
layers: ToolApprovalPolicyLayers,
): TToolApprovalPolicy | undefined {
if (layers.attachedCodeEnvironment === true && layers.endpoint?.enabled !== false) {
if (
layers.attachedCodeEnvironment === true &&
layers.endpoint?.enabled !== true &&
layers.endpoint?.enabled !== false
) {
return {
...layers.endpoint,
enabled: true,

View file

@ -1,5 +1,7 @@
import { HookRegistry } from '@librechat/agents';
import { HookRegistry, executeHooks } from '@librechat/agents';
import { registerToolApprovalHook, clearToolApprovalHooks } from './hooks';
import { createAttachedCodeEnvironmentPolicyHook } from './byom';
import { resolveToolApprovalPolicy } from './policy';
import { buildHITLRunWiring } from './runtime';
describe('buildHITLRunWiring', () => {
@ -53,6 +55,40 @@ describe('buildHITLRunWiring', () => {
});
expect(wiring?.hooks.getMatchers('PreToolUse')).toHaveLength(1);
});
test.each([
['default', 'ask'],
['dontAsk', 'deny'],
] as const)(
'keeps the enabled endpoint %s fallback for unrelated tools in BYOM runs',
async (mode, expectedDecision) => {
const policy = resolveToolApprovalPolicy({
endpoint: { enabled: true, mode },
attachedCodeEnvironment: true,
});
const wiring = buildHITLRunWiring(
policy,
{},
[],
[{ hook: createAttachedCodeEnvironmentPolicyHook(new Set(['attached-agent'])) }],
);
const result = await executeHooks({
registry: wiring?.hooks as HookRegistry,
matchQuery: 'mcp:github:create_issue',
input: {
hook_event_name: 'PreToolUse',
runId: 'run-byom-policy',
toolName: 'mcp:github:create_issue',
toolInput: {},
toolUseId: 'tool-unrelated',
executingAgentId: 'attached-agent',
},
});
expect(result.decision).toBe(expectedDecision);
},
);
});
describe('buildHITLRunWiring host-hook composition', () => {