feat(agent): unify models and validate OpenCode providers
Generate chat, task and learning models from agent-models.json. Use WaterNetwork fast/expert models with 272k context and validate upstream names before readiness. Add an isolated model check command with signal-safe cleanup and plugin, Skill and MCP management docs. Verified in isolation: type checks, 26 focused tests and both model catalog checks; no model inference requests.
This commit is contained in:
+13
-7
@@ -8,6 +8,8 @@ import { realpath, rm } from "node:fs/promises";
|
||||
import { createConversationWorkspace, resolveConversationWorkspace } from "./conversationWorkspace.js";
|
||||
import { serviceOpencodeConfig } from "./opencode2-config.js";
|
||||
import { mcpReadonlyTools, mcpRuntimeReportSchema } from "./mcp-config.js";
|
||||
import { parseModelReference } from "../chat/modelConfig.js";
|
||||
import { assertModelCheckPassed, modelCheckReportSchema, modelCheckTargets } from "./model-check.js";
|
||||
|
||||
import { config } from "../config.js";
|
||||
import { logger } from "../logger.js";
|
||||
@@ -127,6 +129,14 @@ export class OpencodeRuntimeAdapter {
|
||||
return selected;
|
||||
}
|
||||
|
||||
async checkModels(directory = process.cwd()) {
|
||||
const client = await this.ensureClient();
|
||||
const response = await client.rpc.call({
|
||||
rpcID: "tjwater.tools", method: "check_models", input: modelCheckTargets(config.MODEL_CONFIG), location: { directory },
|
||||
}, { signal: AbortSignal.timeout(30_000) });
|
||||
return modelCheckReportSchema.parse(response.output);
|
||||
}
|
||||
|
||||
async warmup(): Promise<void> {
|
||||
const client = await this.ensureClient();
|
||||
const health = await this.health();
|
||||
@@ -148,6 +158,7 @@ export class OpencodeRuntimeAdapter {
|
||||
if (!actualRoot || await realpath(actualRoot) !== await realpath(config.CONVERSATION_WORKSPACE_ROOT)) throw new Error("OpenCode and Agent conversation workspace roots do not match");
|
||||
const mcp = await this.readMcpReport(client, session.location.directory);
|
||||
if (mcp.configuration_errors.length) throw new Error("MCP configuration invalid");
|
||||
assertModelCheckPassed(await this.checkModels(session.location.directory));
|
||||
const models = await client.model.list({ location: { directory: session.location.directory } });
|
||||
const model = models.data.find((model) => `${model.providerID}/${model.id}` === config.OPENCODE_MODEL);
|
||||
if (!model?.enabled || !model.capabilities.tools) {
|
||||
@@ -162,7 +173,7 @@ export class OpencodeRuntimeAdapter {
|
||||
|
||||
async createSession(title?: string) {
|
||||
const client = await this.ensureClient();
|
||||
const [providerID, id] = config.OPENCODE_MODEL.split("/");
|
||||
const { providerID, modelID: id } = parseModelReference(config.OPENCODE_MODEL);
|
||||
const directory = await createConversationWorkspace(config.CONVERSATION_WORKSPACE_ROOT);
|
||||
try {
|
||||
return await client.session.create({ title, agent: "instruction", model: { providerID, id }, location: { directory } });
|
||||
@@ -468,12 +479,7 @@ export class OpencodeRuntimeAdapter {
|
||||
export const opencodeRuntime = new OpencodeRuntimeAdapter();
|
||||
|
||||
function buildOpencodeConfig(): Record<string, unknown> {
|
||||
return serviceOpencodeConfig(deepMerge(
|
||||
deepMerge(readProjectOpencodeConfig(), readEnvOpencodeConfig()),
|
||||
{
|
||||
model: config.OPENCODE_MODEL,
|
||||
},
|
||||
));
|
||||
return serviceOpencodeConfig(deepMerge(readProjectOpencodeConfig(), readEnvOpencodeConfig()), config.MODEL_CONFIG);
|
||||
}
|
||||
|
||||
function readProjectOpencodeConfig(): Record<string, unknown> {
|
||||
|
||||
Reference in New Issue
Block a user