feat(prefs): add search_provider to preferences.md (#1001)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
TÂCHES 2026-03-17 17:59:38 -06:00 committed by GitHub
parent fe0f4f35e6
commit 01e28bc345
3 changed files with 36 additions and 3 deletions

View file

@ -80,6 +80,7 @@ const KNOWN_PREFERENCE_KEYS = new Set<string>([
"verification_commands",
"verification_auto_fix",
"verification_max_retries",
"search_provider",
]);
export interface GSDSkillRule {
@ -182,6 +183,8 @@ export interface GSDPreferences {
verification_commands?: string[];
verification_auto_fix?: boolean;
verification_max_retries?: number;
/** Search provider preference. "brave"/"tavily"/"ollama" force that backend and disable native Anthropic search. "native" forces native only. "auto" = current default behavior. */
search_provider?: "brave" | "tavily" | "ollama" | "native" | "auto";
}
export interface LoadedGSDPreferences {
@ -759,6 +762,15 @@ export function resolveInlineLevel(): InlineLevel {
}
}
/**
* Resolve the search provider preference from preferences.md.
* Returns undefined if not configured (caller falls back to existing behavior).
*/
export function resolveSearchProviderFromPreferences(): GSDPreferences["search_provider"] | undefined {
const prefs = loadEffectiveGSDPreferences();
return prefs?.preferences.search_provider;
}
function mergePreferences(base: GSDPreferences, override: GSDPreferences): GSDPreferences {
return {
version: override.version ?? base.version,
@ -801,6 +813,7 @@ function mergePreferences(base: GSDPreferences, override: GSDPreferences): GSDPr
verification_commands: mergeStringLists(base.verification_commands, override.verification_commands),
verification_auto_fix: override.verification_auto_fix ?? base.verification_auto_fix,
verification_max_retries: override.verification_max_retries ?? base.verification_max_retries,
search_provider: override.search_provider ?? base.search_provider,
};
}
@ -935,6 +948,16 @@ export function validatePreferences(preferences: GSDPreferences): {
}
}
// ─── Search Provider ─────────────────────────────────────────────
if (preferences.search_provider !== undefined) {
const validSearchProviders = new Set(["brave", "tavily", "ollama", "native", "auto"]);
if (typeof preferences.search_provider === "string" && validSearchProviders.has(preferences.search_provider)) {
validated.search_provider = preferences.search_provider as GSDPreferences["search_provider"];
} else {
errors.push(`search_provider must be one of: brave, tavily, ollama, native, auto`);
}
}
// ─── Phase Skip Preferences ─────────────────────────────────────────
if (preferences.phases !== undefined) {
if (typeof preferences.phases === "object" && preferences.phases !== null) {

View file

@ -5,6 +5,8 @@
* the heavy tool-registration modules.
*/
import { resolveSearchProviderFromPreferences } from "../gsd/preferences.js";
/** Tool names for the Brave-backed custom search tools */
export const BRAVE_TOOL_NAMES = ["search-the-web", "search_and_read"];
@ -16,6 +18,11 @@ const THINKING_TYPES = new Set(["thinking", "redacted_thinking"]);
/** When true, skip native web search injection and keep Brave/custom tools active on Anthropic. */
export function preferBraveSearch(): boolean {
// preferences.md takes priority over env var
const prefsPref = resolveSearchProviderFromPreferences();
if (prefsPref === "brave" || prefsPref === "tavily" || prefsPref === "ollama") return true;
if (prefsPref === "native") return false;
// Fall back to env var
return process.env.PREFER_BRAVE_SEARCH === "1" || process.env.PREFER_BRAVE_SEARCH === "true";
}

View file

@ -12,6 +12,7 @@
import { AuthStorage } from '@gsd/pi-coding-agent'
import { homedir } from 'os'
import { join } from 'path'
import { resolveSearchProviderFromPreferences } from '../gsd/preferences.js'
// Compute authFilePath locally instead of importing from app-paths.ts,
// because extensions are copied to ~/.gsd/agent/extensions/ at runtime
@ -94,9 +95,11 @@ export function resolveSearchProvider(overridePreference?: string): SearchProvid
if (overridePreference && VALID_PREFERENCES.has(overridePreference)) {
pref = overridePreference as SearchProviderPreference
} else {
// Invalid override or no override — read stored preference
// If overridePreference is provided but invalid, treat as 'auto'
if (overridePreference !== undefined && !VALID_PREFERENCES.has(overridePreference)) {
// preferences.md takes priority over auth.json
const mdPref = resolveSearchProviderFromPreferences()
if (mdPref && mdPref !== 'auto' && mdPref !== 'native') {
pref = mdPref as SearchProviderPreference
} else if (overridePreference !== undefined && !VALID_PREFERENCES.has(overridePreference)) {
pref = 'auto'
} else {
pref = getSearchProviderPreference()