fix(agents): resolve bound route agent for inbound sessions (#95118)

This commit is contained in:
Yzx
2026-06-23 03:14:17 +08:00
committed by GitHub
parent bfbf25e234
commit a0ed4273ee
7 changed files with 54 additions and 4 deletions

View File

@@ -301,6 +301,7 @@ export function resolveSessionAgentIds(params: {
sessionKey?: string;
config?: OpenClawConfig;
agentId?: string;
fallbackAgentId?: string;
}): {
defaultAgentId: string;
sessionAgentId: string;
@@ -308,11 +309,14 @@ export function resolveSessionAgentIds(params: {
const defaultAgentId = resolveDefaultAgentId(params.config ?? {});
const explicitAgentIdRaw = normalizeLowercaseStringOrEmpty(params.agentId);
const explicitAgentId = explicitAgentIdRaw ? normalizeAgentId(explicitAgentIdRaw) : null;
const fallbackAgentIdRaw = normalizeLowercaseStringOrEmpty(params.fallbackAgentId);
const fallbackAgentId = fallbackAgentIdRaw ? normalizeAgentId(fallbackAgentIdRaw) : null;
const sessionKey = params.sessionKey?.trim();
const normalizedSessionKey = sessionKey ? normalizeLowercaseStringOrEmpty(sessionKey) : undefined;
const parsed = normalizedSessionKey ? parseAgentSessionKey(normalizedSessionKey) : null;
const sessionAgentId =
explicitAgentId ?? (parsed?.agentId ? normalizeAgentId(parsed.agentId) : defaultAgentId);
explicitAgentId ??
(parsed?.agentId ? normalizeAgentId(parsed.agentId) : (fallbackAgentId ?? defaultAgentId));
return { defaultAgentId, sessionAgentId };
}
@@ -320,6 +324,7 @@ export function resolveSessionAgentId(params: {
sessionKey?: string;
config?: OpenClawConfig;
agentId?: string;
fallbackAgentId?: string;
}): string {
return resolveSessionAgentIds(params).sessionAgentId;
}

View File

@@ -68,4 +68,32 @@ describe("resolveSessionAgentIds", () => {
});
expect(sessionAgentId).toBe("main");
});
it("uses fallbackAgentId for unscoped channel session keys", () => {
const { sessionAgentId } = resolveSessionAgentIds({
sessionKey: "feishu:direct:ou_user1",
fallbackAgentId: "main",
config: cfg,
});
expect(sessionAgentId).toBe("main");
});
it("prefers session-key agent over fallbackAgentId", () => {
const { sessionAgentId } = resolveSessionAgentIds({
sessionKey: "agent:beta:feishu:direct:ou_user1",
fallbackAgentId: "main",
config: cfg,
});
expect(sessionAgentId).toBe("beta");
});
it("prefers explicit agentId over fallbackAgentId", () => {
const { sessionAgentId } = resolveSessionAgentIds({
sessionKey: "feishu:direct:ou_user1",
agentId: "beta",
fallbackAgentId: "main",
config: cfg,
});
expect(sessionAgentId).toBe("beta");
});
});

View File

@@ -394,7 +394,7 @@ const resolveSessionStoreLookup = (
if (!sessionKey) {
return {};
}
const agentId = resolveSessionAgentId({ sessionKey, config: cfg, agentId: ctx.AgentId });
const agentId = resolveSessionAgentId({ sessionKey, config: cfg, fallbackAgentId: ctx.AgentId });
const storePath = resolveStorePath(cfg.session?.store, { agentId });
try {
const store = loadSessionStore(storePath);
@@ -1275,7 +1275,7 @@ export async function dispatchReplyFromConfig(
const sessionAgentId = resolveSessionAgentId({
sessionKey: acpDispatchSessionKey,
config: cfg,
agentId: ctx.AgentId,
fallbackAgentId: ctx.AgentId,
});
const sessionAgentCfg = resolveAgentConfig(cfg, sessionAgentId);
const verboseProgress = createShouldEmitVerboseProgress({

View File

@@ -284,7 +284,7 @@ export async function getReplyFromConfig(
agentId: resolveSessionAgentId({
sessionKey: resolvedAgentSessionKey,
config: cfg,
agentId: finalized.AgentId,
fallbackAgentId: finalized.AgentId,
}),
};
},

View File

@@ -265,6 +265,7 @@ export async function initSessionState(params: {
const agentId = resolveSessionAgentId({
sessionKey: sessionCtxForState.SessionKey,
config: cfg,
fallbackAgentId: sessionCtxForState.AgentId,
});
const groupResolution = resolveGroupSessionKey(sessionCtxForState) ?? undefined;
const resetTriggers = sessionCfg?.resetTriggers?.length

View File

@@ -137,6 +137,7 @@ describe("buildChannelInboundEventContext", () => {
From: "test:user:u1",
To: "test:room:room-1",
SessionKey: "agent:main:test:group:room-1",
AgentId: "main",
AccountId: "acct",
ParentSessionKey: "agent:main:test:group",
ModelParentSessionKey: "agent:main:test:model",
@@ -211,6 +212,20 @@ describe("buildChannelInboundEventContext", () => {
expect(ctx.CommandAuthorized).toBe(false);
});
it("carries the routed agent for unscoped session keys", async () => {
const ctx = buildChannelInboundEventContext(
createBaseContextParams({
route: {
agentId: "bound-agent",
routeSessionKey: "feishu:direct:ou_user1",
},
}),
);
expect(ctx.AgentId).toBe("bound-agent");
expect(ctx.SessionKey).toBe("feishu:direct:ou_user1");
});
it("carries room event semantics into the finalized context", async () => {
const ctx = buildChannelInboundEventContext(
createBaseContextParams({

View File

@@ -484,6 +484,7 @@ export function buildChannelInboundEventContext(
From: params.from,
To: params.reply.to,
SessionKey: params.route.dispatchSessionKey ?? params.route.routeSessionKey,
AgentId: params.route.agentId,
AccountId: params.route.accountId ?? params.accountId,
ParentSessionKey: params.route.parentSessionKey,
ModelParentSessionKey: params.route.modelParentSessionKey,