mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 15:10:52 +00:00
fix: solve current-session resolution and valid session creation for sparse channel-plugin requesters
This commit is contained in:
@@ -590,6 +590,76 @@ describe("session_status tool", () => {
|
||||
expect(details.statusText).toContain("🧠 Model:");
|
||||
});
|
||||
|
||||
it("resolves the default session_status lookup for a channel-plugin requester via implicit fallback", async () => {
|
||||
resetSessionStore({});
|
||||
|
||||
const tool = getSessionStatusTool("agent:main:scope:scopy:direct:scopy");
|
||||
|
||||
const result = await tool.execute("call-current-channel-plugin-default", {});
|
||||
const details = result.details as { ok?: boolean; sessionKey?: string; statusText?: string };
|
||||
expect(details.ok).toBe(true);
|
||||
expect(details.sessionKey).toBe("agent:main:scope:scopy:direct:scopy");
|
||||
expect(details.statusText).toContain("OpenClaw");
|
||||
expect(details.statusText).toContain("🧠 Model:");
|
||||
});
|
||||
|
||||
it("materializes a valid persisted session entry when implicit current fallback mutates model state", async () => {
|
||||
resetSessionStore({});
|
||||
|
||||
const tool = getSessionStatusTool("agent:main:scope:scopy:direct:scopy");
|
||||
|
||||
const result = await tool.execute("call-current-channel-plugin-model", {
|
||||
sessionKey: "current",
|
||||
model: "anthropic/claude-sonnet-4-6",
|
||||
});
|
||||
const details = result.details as { ok?: boolean; sessionKey?: string };
|
||||
expect(details.ok).toBe(true);
|
||||
expect(details.sessionKey).toBe("agent:main:scope:scopy:direct:scopy");
|
||||
expect(updateSessionStoreMock).toHaveBeenCalled();
|
||||
const [, savedStore] = updateSessionStoreMock.mock.calls.at(-1) as [
|
||||
string,
|
||||
Record<string, SessionEntry>,
|
||||
];
|
||||
const saved = savedStore["agent:main:scope:scopy:direct:scopy"];
|
||||
expect(saved).toEqual(
|
||||
expect.objectContaining({
|
||||
providerOverride: "anthropic",
|
||||
modelOverride: "claude-sonnet-4-6",
|
||||
liveModelSwitchPending: true,
|
||||
}),
|
||||
);
|
||||
expect(saved.sessionId).toEqual(expect.any(String));
|
||||
expect(saved.sessionId.trim().length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("materializes a valid persisted session entry when the default implicit current fallback mutates model state", async () => {
|
||||
resetSessionStore({});
|
||||
|
||||
const tool = getSessionStatusTool("agent:main:scope:scopy:direct:scopy");
|
||||
|
||||
const result = await tool.execute("call-current-channel-plugin-default-model", {
|
||||
model: "anthropic/claude-sonnet-4-6",
|
||||
});
|
||||
const details = result.details as { ok?: boolean; sessionKey?: string };
|
||||
expect(details.ok).toBe(true);
|
||||
expect(details.sessionKey).toBe("agent:main:scope:scopy:direct:scopy");
|
||||
expect(updateSessionStoreMock).toHaveBeenCalled();
|
||||
const [, savedStore] = updateSessionStoreMock.mock.calls.at(-1) as [
|
||||
string,
|
||||
Record<string, SessionEntry>,
|
||||
];
|
||||
const saved = savedStore["agent:main:scope:scopy:direct:scopy"];
|
||||
expect(saved).toEqual(
|
||||
expect.objectContaining({
|
||||
providerOverride: "anthropic",
|
||||
modelOverride: "claude-sonnet-4-6",
|
||||
liveModelSwitchPending: true,
|
||||
}),
|
||||
);
|
||||
expect(saved.sessionId).toEqual(expect.any(String));
|
||||
expect(saved.sessionId.trim().length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("does not synthesize a current fallback for unknown non-literal session keys", async () => {
|
||||
resetSessionStore({});
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import type {
|
||||
import { getRuntimeConfig } from "../../config/config.js";
|
||||
import {
|
||||
loadSessionStore,
|
||||
mergeSessionEntry,
|
||||
resolveStorePath,
|
||||
type SessionEntry,
|
||||
updateSessionStore,
|
||||
@@ -145,11 +146,11 @@ function synthesizeImplicitCurrentSessionEntry(): SessionEntry {
|
||||
}
|
||||
|
||||
function resolveImplicitCurrentSessionFallback(params: {
|
||||
requestedKeyRaw: string;
|
||||
allowFallback: boolean;
|
||||
storeScopedRequesterKey: string;
|
||||
}): { key: string; entry: SessionEntry } | null {
|
||||
const requesterKey = params.storeScopedRequesterKey.trim();
|
||||
if (params.requestedKeyRaw !== "current" || !requesterKey) {
|
||||
if (!params.allowFallback || !requesterKey) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
@@ -484,7 +485,7 @@ export function createSessionStatusTool(opts?: {
|
||||
|
||||
if (!resolved) {
|
||||
const fallback = resolveImplicitCurrentSessionFallback({
|
||||
requestedKeyRaw,
|
||||
allowFallback: requestedKeyRaw === "current" || requestedKeyParam === undefined,
|
||||
storeScopedRequesterKey,
|
||||
});
|
||||
if (fallback) {
|
||||
@@ -539,11 +540,22 @@ export function createSessionStatusTool(opts?: {
|
||||
markLiveSwitchPending: true,
|
||||
});
|
||||
if (applied.updated) {
|
||||
store[resolved.key] = nextEntry;
|
||||
const persistedEntry = nextEntry.sessionId.trim()
|
||||
? nextEntry
|
||||
: (() => {
|
||||
const persistedEntryPatch: Partial<SessionEntry> = { ...nextEntry };
|
||||
delete persistedEntryPatch.sessionId;
|
||||
const existingEntry = store[resolved.key];
|
||||
const existingWithValidSessionId = existingEntry?.sessionId?.trim()
|
||||
? existingEntry
|
||||
: undefined;
|
||||
return mergeSessionEntry(existingWithValidSessionId, persistedEntryPatch);
|
||||
})();
|
||||
store[resolved.key] = persistedEntry;
|
||||
await updateSessionStore(storePath, (nextStore) => {
|
||||
nextStore[resolved.key] = nextEntry;
|
||||
nextStore[resolved.key] = persistedEntry;
|
||||
});
|
||||
resolved.entry = nextEntry;
|
||||
resolved.entry = persistedEntry;
|
||||
changedModel = true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user