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:
@@ -8,6 +8,8 @@ import {
|
||||
attachTrustedAgentContext,
|
||||
getLocalAgentContext,
|
||||
ProductContextError,
|
||||
getTrustedProductCredential,
|
||||
isTrustedTaskDispatchRequest,
|
||||
} from "../../src/context/localContext.js";
|
||||
import { SessionMetadataStore } from "../../src/sessions/metadataStore.js";
|
||||
|
||||
@@ -35,6 +37,43 @@ const authResponse = (userId: string, projectId: string) => ({
|
||||
});
|
||||
|
||||
describe("trusted product chat context", () => {
|
||||
it("authorizes message dispatch only for verified service accounts with execution permission", async () => {
|
||||
const request = requestWithHeaders({ authorization: "Bearer verified", "x-project-id": "project-a" });
|
||||
for (const [role, permissions, allowed] of [
|
||||
["user", ["operational_task.execute"], false],
|
||||
["service", ["operational_task.view"], false],
|
||||
["service", ["operational_task.execute"], true],
|
||||
["user", [], false],
|
||||
] as const) {
|
||||
globalThis.fetch = (async () => Response.json({ ...authResponse("actor", "project-a"), role, permissions })) as unknown as typeof fetch;
|
||||
await attachTrustedAgentContext(request);
|
||||
expect(isTrustedTaskDispatchRequest(request)).toBe(allowed);
|
||||
}
|
||||
});
|
||||
|
||||
it("verifies personal chats by Bearer without assigning a shared project or business credential", async () => {
|
||||
const requests: Array<{ url: string; headers: Headers }> = [];
|
||||
globalThis.fetch = (async (input: RequestInfo | URL, init?: RequestInit) => {
|
||||
requests.push({ url: String(input), headers: new Headers(init?.headers) });
|
||||
return Response.json({ user_id: requests.length === 1 ? "user-a" : "user-b" });
|
||||
}) as unknown as typeof fetch;
|
||||
const first = requestWithHeaders({ authorization: "Bearer personal-a", "x-agent-local-user": "spoofed" });
|
||||
const second = requestWithHeaders({ authorization: "Bearer personal-b" });
|
||||
await attachTrustedAgentContext(first);
|
||||
await attachTrustedAgentContext(second);
|
||||
expect(requests[0]!.url).toEndWith("/api/v1/access-context");
|
||||
expect(requests[0]!.headers.has("X-Project-Id")).toBeFalse();
|
||||
expect(getLocalAgentContext(first).userId).toBe("user-a");
|
||||
expect(getLocalAgentContext(first).projectId).toBeUndefined();
|
||||
expect(getLocalAgentContext(first).projectKey).not.toBe(getLocalAgentContext(second).projectKey);
|
||||
expect(getTrustedProductCredential(first)).toBeNull();
|
||||
});
|
||||
|
||||
it("does not turn a project header or expired personal token into anonymous access", async () => {
|
||||
globalThis.fetch = (async () => Response.json({ detail: "expired" }, { status: 401 })) as unknown as typeof fetch;
|
||||
await expect(attachTrustedAgentContext(requestWithHeaders({ "x-project-id": "project-a" }))).rejects.toBeInstanceOf(ProductContextError);
|
||||
await expect(attachTrustedAgentContext(requestWithHeaders({ authorization: "Bearer expired-personal" }))).rejects.toBeInstanceOf(ProductContextError);
|
||||
});
|
||||
it("uses verified product users and projects instead of spoofable local headers", async () => {
|
||||
const identities = [authResponse("product-user-1", "project-1"), authResponse("product-user-2", "project-2")];
|
||||
globalThis.fetch = (async () => Response.json(identities.shift())) as unknown as typeof fetch;
|
||||
|
||||
Reference in New Issue
Block a user