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.
30 lines
2.4 KiB
TypeScript
30 lines
2.4 KiB
TypeScript
import type { Part } from "@opencode-ai/sdk/v2";
|
|
import { PRODUCT_MCP_TOOL_IDS, queryMcpToolId } from "../runtime/product-mcp-tools.js";
|
|
|
|
const labels = { reference: "查找所需资料", data: "获取业务数据", analysis: "分析与核查", visualization: "更新地图与图表", other: "执行操作" };
|
|
const classify = (tool: string): keyof typeof labels => {
|
|
if (["skill", "read", "glob", "grep", queryMcpToolId("session_search"), "session_search", "memory_manager", "skill_manager"].includes(tool)) return "reference";
|
|
if ([...PRODUCT_MCP_TOOL_IDS, "websearch", queryMcpToolId("geocode"), queryMcpToolId("list_investigation_tasks"), queryMcpToolId("get_investigation_task"), queryMcpToolId("read_agent_messages"), "query_project_data", "query_operational_task_data", "get_operational_task_context"].includes(tool)) return "data";
|
|
if (["render_scada_analysis", "clear_scada_analysis", "locate_features", "zoom_to_map", "show_chart", "view_history", "publish_task_presentation"].includes(tool)) return "visualization";
|
|
if (["publish_execution_plan", "publish_task_report", "publish_task_progress", "propose_task_action", "add_task_human_evidence"].includes(tool)) return "analysis";
|
|
return "other";
|
|
};
|
|
export const createBusinessStageTracker = (write: (event: string, data: Record<string, unknown>) => void) => {
|
|
const stages = new Map<string, { startedAt: number; actions: Map<string, string> }>();
|
|
return (part: Extract<Part, { type: "tool" }>) => {
|
|
if (["final_answer", "todowrite", "todoread", queryMcpToolId("todoread"), "question"].includes(part.tool)) return;
|
|
const kind = classify(part.tool);
|
|
const stage = stages.get(kind) ?? { startedAt: Date.now(), actions: new Map() };
|
|
stage.actions.set(part.id, part.state.status);
|
|
stages.set(kind, stage);
|
|
const statuses = [...stage.actions.values()];
|
|
const running = statuses.some((status) => status === "pending" || status === "running");
|
|
const status = running ? "running" : statuses.includes("error") ? "error" : "completed";
|
|
write("progress", {
|
|
id: `business-${kind}`, phase: kind, status, title: labels[kind],
|
|
detail: `${statuses.filter((s) => s === "completed").length}/${statuses.length} 项操作已完成`,
|
|
started_at: stage.startedAt, ...(running ? { elapsed_ms: Date.now() - stage.startedAt } : { ended_at: Date.now(), duration_ms: Date.now() - stage.startedAt }),
|
|
});
|
|
};
|
|
};
|