Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions packages/opencode/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1464,7 +1464,7 @@ export const createAntigravityPlugin = (providerId: string) => async (

return {
config: async (opencodeConfig: Record<string, unknown>) => {
applyAntigravityProviderCatalog(opencodeConfig, providerId);
applyAntigravityProviderCatalog(opencodeConfig, providerId, config);
const mutableConfig = opencodeConfig as Record<string, unknown> & {
command?: Record<string, unknown>;
};
Expand Down Expand Up @@ -3851,16 +3851,29 @@ type OpencodeMutableConfig = Record<string, unknown> & {
}>;
};

function applyAntigravityProviderCatalog(config: Record<string, unknown>, providerId: string): void {
const mutableConfig = config as OpencodeMutableConfig;
function applyAntigravityProviderCatalog(
opencodeConfig: Record<string, unknown>,
providerId: string,
pluginConfig: AntigravityConfig
): void {
const mutableConfig = opencodeConfig as OpencodeMutableConfig;
mutableConfig.provider ??= {};

const providerConfig = mutableConfig.provider[providerId] ?? {};

// Merge order (lowest to highest priority):
// 1. Built-in defaults: OPENCODE_MODEL_DEFINITIONS
// 2. Decoupled models (from antigravity.json / antigravity-models.json): pluginConfig.models
// 3. User's main opencode.json models (preserves backwards compatibility / custom overrides)
providerConfig.models = {
...(providerConfig.models ?? {}),
...OPENCODE_MODEL_DEFINITIONS,
...(pluginConfig.models ?? {}),
...(providerConfig.models ?? {}),
};
providerConfig.whitelist = getAntigravityOpencodeModelIds();

// Whitelist should be the union of all registered models so they aren't pruned by OpenCode
providerConfig.whitelist = Object.keys(providerConfig.models);

mutableConfig.provider[providerId] = providerConfig;
}

Expand Down
65 changes: 59 additions & 6 deletions packages/opencode/src/plugin/config/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,56 @@ function mergeConfigs(
// Main Loader
// =============================================================================

/**
* Load the complete configuration.
*
* @param directory - The project directory (for project-level config)
* @returns Fully resolved configuration
*/
function stripJsonCommentsAndTrailingCommas(json: string): string {
return json
.replace(
/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g,
(match: string, group: string | undefined) => (group ? "" : match)
)
.replace(/,(\s*[}\]])/g, "$1");
}

function loadModelsFile(path: string): Record<string, any> | null {
try {
if (!existsSync(path)) {
return null;
}
const content = readFileSync(path, "utf-8");
const parsed = JSON.parse(stripJsonCommentsAndTrailingCommas(content));
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
return parsed;
}
return null;
} catch (error) {
log.warn("Failed to load decoupled models file", { path, error: String(error) });
return null;
}
}

export function loadDecoupledModels(directory: string): Record<string, any> {
let mergedModels: Record<string, any> = {};

const configDir = getConfigDir();
const userJsonPath = join(configDir, "antigravity-models.json");
const userJsoncPath = join(configDir, "antigravity-models.jsonc");
const projectJsonPath = join(directory, ".opencode", "antigravity-models.json");
const projectJsoncPath = join(directory, ".opencode", "antigravity-models.jsonc");

// User level (prefer jsonc if both exist)
const userModels = loadModelsFile(existsSync(userJsoncPath) ? userJsoncPath : userJsonPath);
if (userModels) {
mergedModels = { ...mergedModels, ...userModels };
}

// Project level (prefer jsonc if both exist) - overrides user level
const projectModels = loadModelsFile(existsSync(projectJsoncPath) ? projectJsoncPath : projectJsonPath);
if (projectModels) {
mergedModels = { ...mergedModels, ...projectModels };
}

return mergedModels;
}

export function loadConfig(directory: string): AntigravityConfig {
// Start with defaults
let config: AntigravityConfig = { ...DEFAULT_CONFIG };
Expand All @@ -136,6 +180,15 @@ export function loadConfig(directory: string): AntigravityConfig {
config = mergeConfigs(config, projectConfig);
}

// Load decoupled models from antigravity-models.json(c) and merge into config.models
const decoupledModels = loadDecoupledModels(directory);
if (Object.keys(decoupledModels).length > 0) {
config.models = {
...(config.models ?? {}),
...decoupledModels,
};
}

return config;
}

Expand Down
5 changes: 5 additions & 0 deletions packages/opencode/src/plugin/config/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,11 @@ export const AntigravityConfigSchema = z.object({
*/
auto_update: z.boolean().default(true),

/**
* Decoupled model definitions to inject.
*/
models: z.record(z.string(), z.any()).optional(),

});

export type AntigravityConfig = z.infer<typeof AntigravityConfigSchema>;
Expand Down