feat(agent): unify MCP tools and adopt Responses delivery

Move project and host queries into native MCP with host-owned OAuth/context,
while retaining memory, skills, actions and publication plugins. Add nearby-model
queries and explicit chat/task/learning model defaults.

Adopt Responses HTTP/SSE and native steer/queue inbox delivery, preserving scoped
session ownership, pending input edits and investigation communication.

Disable local-provider discovery: Server /health allowed native vLLM to probe
/v1/models on port 8000. A native before/after regression prevents recurrence.

Validation: 170 focused Bun tests; service/plugin typechecks; native discovery,
provider-discovery and input-inbox smoke checks; no paid model inference.
This commit is contained in:
2026-10-19 18:46:58 +08:00
parent f5c3cde2c2
commit 6a49d2209e
164 changed files with 12286 additions and 2368 deletions
+10 -6
View File
@@ -17,7 +17,11 @@ const modelSchema = z.object({
compatibility: z.record(z.unknown()).optional(),
}).strict();
const modelConfigSchema = z.object({
default_model: text, task_model: text.optional(), learning_gate_model: text.optional(),
defaults: z.object({
chat: text,
task: text,
learning_gate: text,
}).strict(),
providers: z.record(z.object({
name: text, api_url: text, api_key: text.optional(),
package: text.default("@opencode/ai/providers/openai-compatible"),
@@ -55,7 +59,7 @@ export const getAgentModelIds = (options: AgentModelOption[]) => options.map((mo
export function parseAgentModelConfig(input: unknown): AgentModelConfig {
const parsed = modelConfigSchema.safeParse(input);
// Do not echo invalid values: provider configuration can contain credentials.
if (!parsed.success) throw new Error("invalid agent model configuration structure");
if (!parsed.success) throw new Error("invalid agent model configuration structure; configure role models under defaults.chat, defaults.task and defaults.learning_gate");
const configuration = parsed.data;
for (const [id, provider] of Object.entries(configuration.providers)) {
if (!/^[A-Za-z0-9_-]+$/.test(id)) throw new Error("provider ID must contain only letters, digits, _ or -");
@@ -72,10 +76,10 @@ export function parseAgentModelConfig(input: unknown): AgentModelConfig {
for (const modelID of Object.keys(provider.models)) parseModelReference(`${id}/${modelID}`);
}
const allowed = getAgentModelIds(configuredToolModels(configuration));
for (const role of ["default_model", "task_model", "learning_gate_model"] as const) {
if (configuration[role] && !allowed.includes(configuration[role]!)) throw new Error(`${role} must name an enabled configured text model with tool support`);
for (const role of ["chat", "task", "learning_gate"] as const) {
if (!allowed.includes(configuration.defaults[role])) throw new Error(`defaults.${role} must name an enabled configured text model with tool support`);
}
if (!getAgentModelIds(getAgentModelOptions(configuration)).includes(configuration.default_model)) throw new Error("default_model must be selectable in the chat catalog");
if (!getAgentModelIds(getAgentModelOptions(configuration)).includes(configuration.defaults.chat)) throw new Error("defaults.chat must be selectable in the chat catalog");
return configuration;
}
@@ -101,5 +105,5 @@ export function compileOpencodeModels(configuration: AgentModelConfig) {
}])),
}];
}));
return { model: configuration.default_model, providers };
return { model: configuration.defaults.chat, providers };
}