mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-17 04:01:05 +00:00
fix(agents): backfill missing sessionKey in embedded PI runner — prevents undefined key in model selection and live-switch (#60555)
Merged via squash.
Prepared head SHA: 8081345f1c
Co-authored-by: 100yenadmin <239388517+100yenadmin@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman
This commit is contained in:
@@ -25,7 +25,8 @@ vi.mock("../agent-scope.js", () => ({
|
||||
listAgentIds: () => hoisted.listAgentIdsMock(),
|
||||
}));
|
||||
|
||||
const { resolveSessionKeyForRequest } = await import("./session.js");
|
||||
const { resolveSessionKeyForRequest, resolveStoredSessionKeyForSessionId } =
|
||||
await import("./session.js");
|
||||
|
||||
describe("resolveSessionKeyForRequest", () => {
|
||||
beforeEach(() => {
|
||||
@@ -95,4 +96,32 @@ describe("resolveSessionKeyForRequest", () => {
|
||||
expect(result.sessionStore).toBe(otherStore);
|
||||
expect(result.storePath).toBe("/stores/other.json");
|
||||
});
|
||||
|
||||
it("scopes stored session-key lookup to the requested agent store", () => {
|
||||
const embeddedAgentStore = {
|
||||
"agent:embedded-agent:main": { sessionId: "other-session", updatedAt: 2 },
|
||||
"agent:embedded-agent:work": { sessionId: "resume-agent-1", updatedAt: 1 },
|
||||
} satisfies Record<string, SessionEntry>;
|
||||
hoisted.loadSessionStoreMock.mockImplementation((storePath) => {
|
||||
if (storePath === "/stores/embedded-agent.json") {
|
||||
return embeddedAgentStore;
|
||||
}
|
||||
return {};
|
||||
});
|
||||
|
||||
const result = resolveStoredSessionKeyForSessionId({
|
||||
cfg: {
|
||||
session: {
|
||||
store: "/stores/{agentId}.json",
|
||||
},
|
||||
} satisfies OpenClawConfig,
|
||||
sessionId: "resume-agent-1",
|
||||
agentId: "embedded-agent",
|
||||
});
|
||||
|
||||
expect(result.sessionKey).toBe("agent:embedded-agent:work");
|
||||
expect(result.sessionStore).toBe(embeddedAgentStore);
|
||||
expect(result.storePath).toBe("/stores/embedded-agent.json");
|
||||
expect(hoisted.loadSessionStoreMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -95,6 +95,37 @@ function collectSessionIdMatchesForRequest(opts: {
|
||||
return { matches, primaryStoreMatches, storeByKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve an existing stored session key for a session id from a specific agent store.
|
||||
* This scopes the lookup to the target store without implicitly converting `agentId`
|
||||
* into that agent's main session key.
|
||||
*/
|
||||
export function resolveStoredSessionKeyForSessionId(opts: {
|
||||
cfg: OpenClawConfig;
|
||||
sessionId: string;
|
||||
agentId?: string;
|
||||
}): SessionKeyResolution {
|
||||
const sessionId = opts.sessionId.trim();
|
||||
const storeAgentId = opts.agentId?.trim() ? normalizeAgentId(opts.agentId) : undefined;
|
||||
const storePath = resolveStorePath(opts.cfg.session?.store, {
|
||||
agentId: storeAgentId,
|
||||
});
|
||||
const sessionStore = loadSessionStore(storePath);
|
||||
if (!sessionId) {
|
||||
return { sessionKey: undefined, sessionStore, storePath };
|
||||
}
|
||||
|
||||
const selection = resolveSessionIdMatchSelection(
|
||||
Object.entries(sessionStore).filter(([, entry]) => entry?.sessionId === sessionId),
|
||||
sessionId,
|
||||
);
|
||||
return {
|
||||
sessionKey: selection.kind === "selected" ? selection.sessionKey : undefined,
|
||||
sessionStore,
|
||||
storePath,
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveSessionKeyForRequest(opts: {
|
||||
cfg: OpenClawConfig;
|
||||
to?: string;
|
||||
@@ -121,9 +152,10 @@ export function resolveSessionKeyForRequest(opts: {
|
||||
let sessionKey: string | undefined =
|
||||
explicitSessionKey ?? (ctx ? resolveSessionKey(scope, ctx, mainKey) : undefined);
|
||||
|
||||
// If a session id was provided, prefer to re-use its entry (by id) even when no key was derived.
|
||||
// When duplicates exist across agent stores, pick the same deterministic best match used by the
|
||||
// shared gateway/session resolver helpers instead of whichever store happens to be scanned first.
|
||||
// If a session id was provided, prefer to re-use its existing entry (by id) even when no key was
|
||||
// derived. When duplicates exist across agent stores, pick the same deterministic best match used
|
||||
// by the shared gateway/session resolver helpers instead of whichever store happens to be scanned
|
||||
// first.
|
||||
if (
|
||||
opts.sessionId &&
|
||||
!explicitSessionKey &&
|
||||
|
||||
Reference in New Issue
Block a user