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
+7
View File
@@ -1,6 +1,7 @@
import { randomUUID } from "node:crypto";
import { type ProductCredential } from "./server-auth-client.js";
import { getRuntimeSessionContext } from "../runtime/sessionContext.js";
export type CredentialRefreshReason =
| "access_token_expired"
@@ -245,7 +246,10 @@ export const credentialRefreshCoordinator = new CredentialRefreshCoordinator(45_
export const bindSessionCredential = (sessionId: string, credential: ProductCredential | null) => {
if (credential) { credentialVault.set(sessionId, { ...credential }); credentialFailures.delete(sessionId); }
};
const credentialReleaseListeners = new Set<(id: string) => void>();
export const onSessionCredentialReleased = (listener: (id: string) => void) => { credentialReleaseListeners.add(listener); return () => credentialReleaseListeners.delete(listener); };
export const releaseSessionCredential = (sessionId: string) => {
for (const listener of credentialReleaseListeners) listener(sessionId);
credentialRefreshCoordinator.cancelSession(sessionId);
credentialRefreshCoordinator.disableBackground(sessionId);
credentialVault.delete(sessionId);
@@ -264,6 +268,9 @@ export const requestWithSessionCredential = async (
sessionId: string, execute: (credential: ProductCredential) => Promise<Response>,
) => {
const credential = credentialVault.get(sessionId);
if (!credential && getRuntimeSessionContext(sessionId)?.projectKey.startsWith("personal-")) {
throw Object.assign(new Error("个人会话未关联项目,请选择项目会话后访问业务数据"), { status: 403, code: "PROJECT_CONTEXT_REQUIRED" });
}
if (!credential) throw Object.assign(new Error("task access requires a current product credential"), { status: 401 });
if (credentialFailures.has(sessionId)) throw Object.assign(new CredentialRefreshError("credential renewal failed"), { status: 401 });
try {