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
+30 -43
View File
@@ -166,6 +166,36 @@ describe("OperationalTaskClient", () => {
expect(JSON.stringify(body)).not.toContain("transient-secret");
});
it("publishes complete progress and presentation with the claimed run without browser receipts", async () => {
const requests: Request[] = [];
globalThis.fetch = (async (input, init) => {
const request = new Request(input, init);
requests.push(request);
if (request.url.endsWith("/claim")) return Response.json(run);
if (request.url.endsWith("/presentation-events")) return Response.json({
recorded: true, event_seq: 12, run_id: runId, execution_generation: 1,
});
return Response.json({ task, plan_revision: null, report_revision: null, progress_revision: 3 });
}) as typeof fetch;
bindOperationalTaskCredential("runtime-1", { authorization: "Bearer transient-secret", projectId: "project-1" });
const client = new OperationalTaskClient("http://server.test");
await client.claimTask("runtime-1", taskId, runId, {
requestId: executionRequestId, expectedRevision: 1, sourceSessionId: "session-1",
});
const progress = { summary: "正在查询投诉时段", diagram: null };
expect(await client.publishArtifacts("runtime-1", taskId, {
requestId: executionRequestId, sourceSessionId: "session-1", progress,
})).toMatchObject({ progress_revision: 3 });
expect(await requests[1]!.json()).toMatchObject({ progress, run_id: runId, source_session_id: "session-1" });
const command = { action: "set_overlay" as const, overlay_id: "complaints", title: "投诉位置", mode: "geometry" as const, evidence_id: "query-1" };
expect(await client.publishPresentation("runtime-1", taskId, {
requestId: executionRequestId, sourceSessionId: "session-1", command,
})).toEqual({ recorded: true, event_seq: 12, run_id: runId, execution_generation: 1 });
expect(await requests[2]!.json()).toEqual({ request_id: executionRequestId, run_id: runId, source_session_id: "session-1", command });
expect(requests).toHaveLength(3);
expect(requests[2]!.headers.get("authorization")).toBe("Bearer transient-secret");
});
it("fails closed without a current product credential", async () => {
const client = new OperationalTaskClient("http://server.test");
expect(client.getAgentContext("runtime-1", "50000000-0000-4000-8000-000000000001"))
@@ -217,49 +247,6 @@ describe("OperationalTaskClient", () => {
});
});
it("returns the persisted bounded data snapshot with its evidence id", async () => {
const urls: string[] = [];
globalThis.fetch = (async (input) => {
const url = String(input);
urls.push(url);
return urls.length === 1
? Response.json({
evidence_id: "scada_readings:sha256:abc",
kind: "system_data",
summary: "scada_readings 数据快照",
source_label: "scada_readings",
source_ref: "scada_readings:sha256:abc",
observed_at: "2026-09-23T01:00:00Z",
})
: Response.json({
source: "scada_readings",
source_ref: "scada_readings:sha256:abc",
queried_at: "2026-09-23T01:00:00Z",
origin: "system_data",
data: { items: [{ value: 0.42 }] },
truncated: false,
});
}) as typeof fetch;
bindOperationalTaskCredential("runtime-1", {
authorization: "Bearer transient-secret",
projectId: "project-1",
});
const client = new OperationalTaskClient("http://server.test");
const result = await client.queryData("runtime-1", "50000000-0000-4000-8000-000000000001", {
source: "scada_readings",
device_ids: ["device-1"],
start: "2026-09-23T00:00:00Z",
end: "2026-09-23T01:00:00Z",
});
expect(result).toMatchObject({
evidence: { evidence_id: "scada_readings:sha256:abc" },
snapshot: { origin: "system_data", truncated: false, data: { items: [{ value: 0.42 }] } },
});
expect(urls[1]).toContain("/evidence/scada_readings%3Asha256%3Aabc/snapshot");
});
it("keeps the current task revision for heartbeats after an action-only response", async () => {
const requestBodies: Record<string, unknown>[] = [];
let call = 0;