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:
+16
-22
@@ -2,6 +2,7 @@ import dotenv from "dotenv";
|
||||
import { z } from "zod";
|
||||
|
||||
import { loadAgentModelConfig, type AgentModelConfig } from "./chat/modelConfig.js";
|
||||
import { DEFAULT_SESSION_WORKSPACE_ROOT } from "./runtime/conversationWorkspace.js";
|
||||
|
||||
// 本地开发可在项目根目录放 .local.env;已存在的系统环境变量优先级更高。
|
||||
dotenv.config({ path: ".local.env", override: false });
|
||||
@@ -46,6 +47,13 @@ const envSchema = z
|
||||
TJWATER_SERVER_BASE_URL: z.string().url().default("http://127.0.0.1:8000"),
|
||||
// 工况任务调用 Server 的最长等待时间,必须短于两次租约心跳之间的间隔。
|
||||
TJWATER_SERVER_TIMEOUT_MS: z.coerce.number().int().positive().max(30_000).default(10_000),
|
||||
TJWATER_MCP_ENABLED: optionalBoolean(false),
|
||||
TJWATER_MCP_ISSUER_URI: optionalString(),
|
||||
TJWATER_MCP_DEV_HTTP_ISSUER_URI: optionalString(),
|
||||
TJWATER_MCP_RESOURCE_URI: optionalString(),
|
||||
TJWATER_MCP_AUDIENCE: z.string().default("tjwater-mcp"),
|
||||
TJWATER_MCP_CLIENT_ID: z.string().default("tjwater-agent-mcp"),
|
||||
TJWATER_MCP_CLIENT_SECRET: optionalString(),
|
||||
// Pino 日志级别。
|
||||
LOG_LEVEL: z.string().default("info"),
|
||||
// LLM 工具/技能调用审计日志路径。
|
||||
@@ -54,8 +62,8 @@ const envSchema = z
|
||||
.default("./logs/llm-request-audit.log"),
|
||||
// 内部工具桥鉴权 token;embedded 可自动生成,client 模式必须与外部 OpenCode 显式共享。
|
||||
AGENT_INTERNAL_TOKEN: optionalString(),
|
||||
// 每个 OpenCode 会话独占的 Shell 工作目录;与受保护的业务 data/logs 分开。
|
||||
CONVERSATION_WORKSPACE_ROOT: z.string().default("./.agent-workspaces"),
|
||||
// 每个 OpenCode 会话独占的文件工作目录;其他 data/logs 仍受保护。
|
||||
CONVERSATION_WORKSPACE_ROOT: z.string().default(DEFAULT_SESSION_WORKSPACE_ROOT),
|
||||
SANDBOX_MAX_OUTPUT_BYTES: z.coerce.number().int().positive().max(16 * 1024 * 1024).default(1024 * 1024),
|
||||
// OpenCode 2 CLI 命令或绝对路径。
|
||||
OPENCODE_COMMAND: z.string().default("opencode2"),
|
||||
@@ -87,21 +95,17 @@ const envSchema = z
|
||||
OPENCODE_CLIENT_BASE_URL: z.string().url().optional(),
|
||||
// 旧版 client 模式环境变量名,保留兼容,解析时会映射到 OPENCODE_CLIENT_BASE_URL。
|
||||
OPENCODE_BASE_URL: z.string().url().optional(),
|
||||
// 后端结果在直接内联返回给模型前允许的最大字节数。
|
||||
MAX_INLINE_RESULT_BYTES: z.coerce.number().int().positive().default(12000),
|
||||
// 生成结果 preview 时最多抽样的条目数。
|
||||
MAX_PREVIEW_SAMPLE_ITEMS: z.coerce.number().int().positive().default(3),
|
||||
// memory 持久化存储目录。
|
||||
MEMORY_STORAGE_DIR: z.string().default("./data/memory"),
|
||||
// 持久化文件写入前保留备份版本的目录。
|
||||
PERSISTENCE_BACKUP_DIR: z.string().default("./data/backup"),
|
||||
// 注入到 prompt 的 memory 快照最大字符数,避免上下文过大。
|
||||
MEMORY_MAX_PROMPT_CHARS: z.coerce.number().int().positive().default(1800),
|
||||
// session transcript 持久化目录。
|
||||
// 旧 transcript 迁移来源;新记录位于用户会话工作空间的 chat/。
|
||||
SESSION_TRANSCRIPT_STORAGE_DIR: z.string().default("./data/session-transcripts"),
|
||||
// session metadata 持久化目录。
|
||||
SESSION_METADATA_STORAGE_DIR: z.string().default("./data/session-metadata"),
|
||||
// session UI state 持久化目录。
|
||||
// 旧 UI state 迁移来源;新完整聊天状态位于会话工作空间的 chat/。
|
||||
SESSION_UI_STATE_STORAGE_DIR: z.string().default("./data/session-ui-states"),
|
||||
// 每个会话最多保留多少轮 transcript,超过后裁剪旧记录。
|
||||
SESSION_TRANSCRIPT_MAX_TURNS_PER_SESSION: z.coerce
|
||||
@@ -113,7 +117,7 @@ const envSchema = z
|
||||
SESSION_SEARCH_MAX_RESULTS: z.coerce.number().int().positive().default(8),
|
||||
// session_search 查询文本最大长度。
|
||||
SESSION_SEARCH_MAX_QUERY_CHARS: z.coerce.number().int().positive().default(240),
|
||||
// 当前 session 的 learning 进度状态目录。
|
||||
// 旧 learning 进度迁移来源;新进度位于会话 chat/learning.json。
|
||||
SESSION_LEARNING_STATE_STORAGE_DIR: z.string().default("./data/session-learning-state"),
|
||||
// learning audit 日志路径。
|
||||
LEARNING_AUDIT_LOG_PATH: z
|
||||
@@ -127,16 +131,6 @@ const envSchema = z
|
||||
LEARNING_REVIEW_MAX_RECENT_TURNS: z.coerce.number().int().positive().default(8),
|
||||
// review proposal 的最低置信度阈值。
|
||||
LEARNING_MIN_PROPOSAL_CONFIDENCE: z.coerce.number().min(0).max(1).default(0.8),
|
||||
// result_ref 持久化存储目录。
|
||||
RESULT_REF_STORAGE_DIR: z.string().default("./data/result-refs"),
|
||||
// result_ref 保留时长(小时)。
|
||||
RESULT_REF_TTL_HOURS: z.coerce.number().int().positive().default(168),
|
||||
// 定时清理过期 result_ref 的扫描周期(毫秒)。
|
||||
RESULT_REF_CLEANUP_INTERVAL_MS: z.coerce
|
||||
.number()
|
||||
.int()
|
||||
.positive()
|
||||
.default(3600000),
|
||||
})
|
||||
.superRefine((env, ctx) => {
|
||||
if (env.OPENCODE_MODE === "client" && !env.OPENCODE_CLIENT_BASE_URL) {
|
||||
@@ -177,7 +171,7 @@ const environment = envSchema.parse(normalizedEnv);
|
||||
const modelConfiguration = loadAgentModelConfig(environment.AGENT_MODEL_CONFIG_PATH);
|
||||
export const config: AppConfig = {
|
||||
...environment, MODEL_CONFIG: modelConfiguration,
|
||||
OPENCODE_MODEL: modelConfiguration.default_model,
|
||||
OPERATIONAL_TASK_MODEL: modelConfiguration.task_model ?? modelConfiguration.default_model,
|
||||
LEARNING_GATE_MODEL: modelConfiguration.learning_gate_model ?? modelConfiguration.default_model,
|
||||
OPENCODE_MODEL: modelConfiguration.defaults.chat,
|
||||
OPERATIONAL_TASK_MODEL: modelConfiguration.defaults.task,
|
||||
LEARNING_GATE_MODEL: modelConfiguration.defaults.learning_gate,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user