fix(core): keep a failed web_search backend derivation from breaking the tool registry

Reading the generation config to derive the search backend assumed a complete Config surface, so a test double that stubs getModelsConfig() without getGenerationConfig() threw while the registry was being built and took every other tool down with it. Derivation is opportunistic: an unexpected Config shape should cost web search alone, so catch and fall back to the silent no-backend result. Give the subagent-manager double the accessor it was missing.
This commit is contained in:
qqqys committed 2026-09-08 18:44:17 +08:00
1 parent d6eaaf7617
commit 0149317b95
3 files changed
+36 -1

No files matched your search

@@ -2307,6 +2307,7 @@ bad`);
});
vi.spyOn(mockConfig, 'getModelsConfig').mockReturnValue({
getResolvedModel: vi.fn().mockReturnValue(undefined),
getGenerationConfig: vi.fn().mockReturnValue({}),
} as unknown as ReturnType<Config['getModelsConfig']>);
});
@@ -837,6 +837,25 @@ describe('evaluateWebSearchGate auto derivation', () => {
}
});
it('stays silently off instead of throwing when the config surface is incomplete', () => {
// The gate runs while the tool registry is being built, for whatever
// Config shape the caller has. A missing accessor must cost web search,
// not every other tool in the registry.
const broken = {
getWebSearchSettings: () => undefined,
getModel: () => 'some-model',
getCurrentAuthType: () => 'openai',
getCurrentModelRegistryBaseUrl: () => undefined,
getAllConfiguredModels: () => [],
getModelsConfig: () => ({}),
} as unknown as Config;
const gate = evaluateWebSearchGate(broken);
expect(gate.ok).toBe(false);
if (!gate.ok) {
expect(gate.silent).toBe(true);
}
});
it('prefers an explicitly configured search model over derivation', () => {
vi.stubEnv(STANDARD.envKey, 'sk-standard');
const gate = evaluateWebSearchGate(
+16 -1
View File
@@ -405,7 +405,22 @@ export function evaluateWebSearchGate(config: Config): WebSearchGateResult {
// model's provider. An explicit `enabled: true`, or an env-declared
// backend missing its model, still names the missing piece instead.
if (settings?.enabled !== true && !settings?.baseUrl) {
return resolveAutoBackend(config, settings);
try {
return resolveAutoBackend(config, settings);
} catch (e) {
// Derivation is opportunistic and runs while the tool registry is
// being built: an unexpected Config shape must cost the user web
// search, not every other tool in the registry.
gateDebugLogger.debug(
`[WebSearch] auto backend derivation threw: ${e instanceof Error ? e.message : String(e)}`,
);
return {
ok: false,
silent: true,
notice:
'WebSearch is not configured and no backend could be derived automatically.',
};
}
}
return {
ok: false,