feat(h5): web 联网能力、实时查询路由与 session Finish 对齐
- 新增 web 能力并挂载 platform/web(web_search/fetch_url) - 实时查询强制 web skill,router fallback 与 await session Finish - Session Broker 覆盖率/指标、stream replay 与相关单测/E2E 脚本 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+35
-5
@@ -5,6 +5,8 @@
|
||||
* See docs/h5-session-architecture-20260706.md Patch 1.
|
||||
*/
|
||||
|
||||
import { createSessionBrokerMetrics, isSessionBrokerMetricsEnabled } from './session-broker-metrics.mjs';
|
||||
|
||||
function envFlag(value, fallback = false) {
|
||||
const raw = String(value ?? '').trim().toLowerCase();
|
||||
if (!raw) return fallback;
|
||||
@@ -21,21 +23,33 @@ function requireUserAuthFn(userAuth, name) {
|
||||
}
|
||||
}
|
||||
|
||||
export function createSessionBroker({ userAuth } = {}) {
|
||||
export function createSessionBroker({
|
||||
userAuth,
|
||||
logger = console,
|
||||
metricsEnabled = isSessionBrokerMetricsEnabled(),
|
||||
} = {}) {
|
||||
requireUserAuthFn(userAuth, 'registerAgentSession');
|
||||
requireUserAuthFn(userAuth, 'getSessionTarget');
|
||||
requireUserAuthFn(userAuth, 'ownsSession');
|
||||
requireUserAuthFn(userAuth, 'unregisterAgentSession');
|
||||
const metrics = createSessionBrokerMetrics({ logger, enabled: metricsEnabled });
|
||||
|
||||
async function validateOwnership(userId, sessionId) {
|
||||
if (!userId || !sessionId) return false;
|
||||
return userAuth.ownsSession(userId, sessionId);
|
||||
const owned = await userAuth.ownsSession(userId, sessionId);
|
||||
if (!owned) {
|
||||
metrics.ownershipDenied({ userId, sessionId });
|
||||
}
|
||||
return owned;
|
||||
}
|
||||
|
||||
async function registerSession({ userId, sessionId, target = 0, origin = 'h5' } = {}) {
|
||||
if (!userId || !sessionId) {
|
||||
throw new Error('registerSession requires userId and sessionId');
|
||||
}
|
||||
if (await userAuth.ownsSession(userId, sessionId)) {
|
||||
metrics.registerDuplicate({ userId, sessionId, target, origin });
|
||||
}
|
||||
await userAuth.registerAgentSession(userId, sessionId, target);
|
||||
if (typeof userAuth.setSessionOrigin === 'function' && (origin === 'h5' || origin === 'wechat')) {
|
||||
await userAuth.setSessionOrigin(sessionId, origin);
|
||||
@@ -49,7 +63,15 @@ export function createSessionBroker({ userAuth } = {}) {
|
||||
|
||||
async function resolveSessionTarget(sessionId) {
|
||||
if (!sessionId) return { target: null, node: 0 };
|
||||
return userAuth.getSessionTarget(sessionId);
|
||||
const resolved = await userAuth.getSessionTarget(sessionId);
|
||||
if (!resolved?.target && Number(resolved?.node ?? 0) > 0) {
|
||||
metrics.resolveTargetMiss({
|
||||
sessionId,
|
||||
node: Number(resolved.node),
|
||||
reason: 'legacy_node_fallback',
|
||||
});
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
async function resolveSession(sessionId) {
|
||||
@@ -76,12 +98,20 @@ export function createSessionBroker({ userAuth } = {}) {
|
||||
* Unified session ownership/target access with optional broker routing.
|
||||
* When disabled, delegates to userAuth with identical behavior (Patch 2 fallback).
|
||||
*/
|
||||
export function createSessionAccess({ userAuth, enabled = isSessionBrokerEnabled(), broker = null } = {}) {
|
||||
export function createSessionAccess({
|
||||
userAuth,
|
||||
enabled = isSessionBrokerEnabled(),
|
||||
broker = null,
|
||||
logger = console,
|
||||
metricsEnabled = isSessionBrokerMetricsEnabled(),
|
||||
} = {}) {
|
||||
if (!userAuth) {
|
||||
throw new Error('createSessionAccess requires userAuth');
|
||||
}
|
||||
const useBroker = Boolean(enabled);
|
||||
const sessionBroker = useBroker ? (broker ?? createSessionBroker({ userAuth })) : null;
|
||||
const sessionBroker = useBroker
|
||||
? (broker ?? createSessionBroker({ userAuth, logger, metricsEnabled }))
|
||||
: null;
|
||||
|
||||
async function validateOwnership(userId, sessionId) {
|
||||
if (useBroker) return sessionBroker.validateOwnership(userId, sessionId);
|
||||
|
||||
Reference in New Issue
Block a user