mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 05:51:15 +08:00
clawdbot-55f: route sdk session reads through seam
This commit is contained in:
@@ -5,6 +5,12 @@
|
||||
*/
|
||||
|
||||
import { loadSessionStore as loadSessionStoreImpl } from "../config/sessions/store-load.js";
|
||||
export {
|
||||
getSessionEntry,
|
||||
listSessionEntries,
|
||||
updateSessionStoreEntry,
|
||||
upsertSessionEntry,
|
||||
} from "./session-store-runtime.js";
|
||||
|
||||
/**
|
||||
* @deprecated Use getSessionEntry/listSessionEntries for reads and
|
||||
@@ -141,16 +147,12 @@ export type {
|
||||
} from "../config/types.js";
|
||||
export {
|
||||
clearSessionStoreCacheForTest,
|
||||
getSessionEntry,
|
||||
listSessionEntries,
|
||||
patchSessionEntry,
|
||||
readSessionUpdatedAt,
|
||||
recordSessionMetaFromInbound,
|
||||
saveSessionStore,
|
||||
updateLastRoute,
|
||||
updateSessionStore,
|
||||
updateSessionStoreEntry,
|
||||
upsertSessionEntry,
|
||||
resolveSessionStoreEntry,
|
||||
} from "../config/sessions/store.js";
|
||||
export { resolveSessionKey } from "../config/sessions/session-key.js";
|
||||
|
||||
94
src/plugin-sdk/session-store-runtime.test.ts
Normal file
94
src/plugin-sdk/session-store-runtime.test.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
getSessionEntry,
|
||||
listSessionEntries,
|
||||
updateSessionStoreEntry,
|
||||
upsertSessionEntry,
|
||||
} from "./session-store-runtime.js";
|
||||
|
||||
describe("session-store-runtime compatibility surface", () => {
|
||||
let tempDir: string;
|
||||
let storePath: string;
|
||||
|
||||
beforeEach(() => {
|
||||
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-sdk-session-store-"));
|
||||
storePath = path.join(tempDir, "sessions.json");
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it("keeps the public session read shape while using the accessor-backed exports", async () => {
|
||||
const sessionKey = "agent:main:main";
|
||||
await upsertSessionEntry({
|
||||
sessionKey,
|
||||
storePath,
|
||||
entry: {
|
||||
model: "gpt-5.5",
|
||||
sessionId: "session-1",
|
||||
updatedAt: 10,
|
||||
},
|
||||
});
|
||||
|
||||
expect(getSessionEntry({ sessionKey, storePath })).toMatchObject({
|
||||
model: "gpt-5.5",
|
||||
sessionId: "session-1",
|
||||
updatedAt: 10,
|
||||
});
|
||||
expect(listSessionEntries({ storePath })).toEqual([
|
||||
{
|
||||
sessionKey,
|
||||
entry: expect.objectContaining({
|
||||
model: "gpt-5.5",
|
||||
sessionId: "session-1",
|
||||
updatedAt: 10,
|
||||
}),
|
||||
},
|
||||
]);
|
||||
|
||||
await upsertSessionEntry({
|
||||
sessionKey,
|
||||
storePath,
|
||||
entry: {
|
||||
sessionId: "session-1",
|
||||
updatedAt: 20,
|
||||
},
|
||||
});
|
||||
expect(getSessionEntry({ sessionKey, storePath })?.model).toBeUndefined();
|
||||
});
|
||||
|
||||
it("keeps the public entry-update signature while delegating to the seam", async () => {
|
||||
const sessionKey = "agent:main:main";
|
||||
|
||||
await expect(
|
||||
updateSessionStoreEntry({
|
||||
sessionKey,
|
||||
storePath,
|
||||
update: () => ({ model: "gpt-5.5" }),
|
||||
}),
|
||||
).resolves.toBeNull();
|
||||
|
||||
await upsertSessionEntry({
|
||||
sessionKey,
|
||||
storePath,
|
||||
entry: {
|
||||
sessionId: "session-1",
|
||||
updatedAt: 10,
|
||||
},
|
||||
});
|
||||
await expect(
|
||||
updateSessionStoreEntry({
|
||||
sessionKey,
|
||||
storePath,
|
||||
update: () => ({ model: "gpt-5.5" }),
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
model: "gpt-5.5",
|
||||
sessionId: "session-1",
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,44 @@
|
||||
// Narrow session-store helpers for channel hot paths.
|
||||
|
||||
import {
|
||||
listSessionEntries as listAccessorSessionEntries,
|
||||
loadSessionEntry,
|
||||
replaceSessionEntry,
|
||||
updateSessionEntry,
|
||||
} from "../config/sessions/session-accessor.js";
|
||||
import { loadSessionStore as loadSessionStoreImpl } from "../config/sessions/store-load.js";
|
||||
import type { SessionEntry } from "../config/sessions/types.js";
|
||||
|
||||
type SessionStoreReadParams = {
|
||||
agentId?: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
hydrateSkillPromptRefs?: boolean;
|
||||
sessionKey: string;
|
||||
storePath?: string;
|
||||
};
|
||||
|
||||
type SessionStoreListParams = Partial<Omit<SessionStoreReadParams, "sessionKey">>;
|
||||
|
||||
type SessionStoreEntrySummary = {
|
||||
sessionKey: string;
|
||||
entry: SessionEntry;
|
||||
};
|
||||
|
||||
type SessionStoreEntryUpdate = (
|
||||
entry: SessionEntry,
|
||||
) => Promise<Partial<SessionEntry> | null> | Partial<SessionEntry> | null;
|
||||
|
||||
type UpdateSessionStoreEntryParams = {
|
||||
storePath: string;
|
||||
sessionKey: string;
|
||||
update: SessionStoreEntryUpdate;
|
||||
skipMaintenance?: boolean;
|
||||
takeCacheOwnership?: boolean;
|
||||
};
|
||||
|
||||
type UpsertSessionEntryParams = SessionStoreReadParams & {
|
||||
entry: SessionEntry;
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated Use getSessionEntry/listSessionEntries for reads and
|
||||
@@ -9,6 +47,60 @@ import { loadSessionStore as loadSessionStoreImpl } from "../config/sessions/sto
|
||||
*/
|
||||
export const loadSessionStore = loadSessionStoreImpl;
|
||||
|
||||
/** Loads one session entry through the accessor seam. */
|
||||
export function getSessionEntry(params: SessionStoreReadParams): SessionEntry | undefined {
|
||||
return loadSessionEntry({
|
||||
agentId: params.agentId,
|
||||
env: params.env,
|
||||
hydrateSkillPromptRefs: params.hydrateSkillPromptRefs,
|
||||
sessionKey: params.sessionKey,
|
||||
storePath: params.storePath,
|
||||
});
|
||||
}
|
||||
|
||||
/** Lists session entries through the accessor seam. */
|
||||
export function listSessionEntries(
|
||||
params: SessionStoreListParams = {},
|
||||
): SessionStoreEntrySummary[] {
|
||||
return listAccessorSessionEntries({
|
||||
agentId: params.agentId,
|
||||
env: params.env,
|
||||
hydrateSkillPromptRefs: params.hydrateSkillPromptRefs,
|
||||
storePath: params.storePath,
|
||||
});
|
||||
}
|
||||
|
||||
/** Updates an existing session entry through the accessor seam. */
|
||||
export async function updateSessionStoreEntry(
|
||||
params: UpdateSessionStoreEntryParams,
|
||||
): Promise<SessionEntry | null> {
|
||||
return await updateSessionEntry(
|
||||
{
|
||||
sessionKey: params.sessionKey,
|
||||
storePath: params.storePath,
|
||||
},
|
||||
params.update,
|
||||
{
|
||||
skipMaintenance: params.skipMaintenance,
|
||||
takeCacheOwnership: params.takeCacheOwnership,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/** Replaces or creates one session entry through the accessor seam. */
|
||||
export async function upsertSessionEntry(params: UpsertSessionEntryParams): Promise<void> {
|
||||
await replaceSessionEntry(
|
||||
{
|
||||
agentId: params.agentId,
|
||||
env: params.env,
|
||||
hydrateSkillPromptRefs: params.hydrateSkillPromptRefs,
|
||||
sessionKey: params.sessionKey,
|
||||
storePath: params.storePath,
|
||||
},
|
||||
params.entry,
|
||||
);
|
||||
}
|
||||
|
||||
export { resolveSessionStoreEntry } from "../config/sessions/store-entry.js";
|
||||
export {
|
||||
resolveSessionFilePath,
|
||||
@@ -22,16 +114,12 @@ export { resolveGroupSessionKey } from "../config/sessions/group.js";
|
||||
export { canonicalizeMainSessionAlias } from "../config/sessions/main-session.js";
|
||||
export {
|
||||
clearSessionStoreCacheForTest,
|
||||
getSessionEntry,
|
||||
listSessionEntries,
|
||||
patchSessionEntry,
|
||||
readSessionUpdatedAt,
|
||||
recordSessionMetaFromInbound,
|
||||
saveSessionStore,
|
||||
updateLastRoute,
|
||||
updateSessionStore,
|
||||
updateSessionStoreEntry,
|
||||
upsertSessionEntry,
|
||||
} from "../config/sessions/store.js";
|
||||
export {
|
||||
evaluateSessionFreshness,
|
||||
|
||||
@@ -11,19 +11,51 @@ import { normalizeThinkLevel, resolveThinkingProfile } from "../../auto-reply/th
|
||||
import { getRuntimeConfig } from "../../config/config.js";
|
||||
import { resolveSessionFilePath, resolveStorePath } from "../../config/sessions/paths.js";
|
||||
import {
|
||||
getSessionEntry,
|
||||
listSessionEntries,
|
||||
listSessionEntries as listAccessorSessionEntries,
|
||||
loadSessionEntry,
|
||||
replaceSessionEntry,
|
||||
updateSessionEntry,
|
||||
} from "../../config/sessions/session-accessor.js";
|
||||
import {
|
||||
loadSessionStore,
|
||||
patchSessionEntry,
|
||||
saveSessionStore,
|
||||
updateSessionStore,
|
||||
updateSessionStoreEntry,
|
||||
upsertSessionEntry,
|
||||
} from "../../config/sessions/store.js";
|
||||
import type { SessionEntry } from "../../config/sessions/types.js";
|
||||
import { createLazyRuntimeMethod, createLazyRuntimeModule } from "../../shared/lazy-runtime.js";
|
||||
import { defineCachedValue } from "./runtime-cache.js";
|
||||
import type { PluginRuntime } from "./types.js";
|
||||
|
||||
type RuntimeSessionStoreReadParams = {
|
||||
agentId?: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
hydrateSkillPromptRefs?: boolean;
|
||||
sessionKey: string;
|
||||
storePath?: string;
|
||||
};
|
||||
|
||||
type RuntimeSessionStoreListParams = Partial<Omit<RuntimeSessionStoreReadParams, "sessionKey">>;
|
||||
|
||||
type RuntimeSessionStoreEntrySummary = {
|
||||
sessionKey: string;
|
||||
entry: SessionEntry;
|
||||
};
|
||||
|
||||
type RuntimeSessionStoreEntryUpdateParams = {
|
||||
storePath: string;
|
||||
sessionKey: string;
|
||||
update: (
|
||||
entry: SessionEntry,
|
||||
) => Promise<Partial<SessionEntry> | null> | Partial<SessionEntry> | null;
|
||||
skipMaintenance?: boolean;
|
||||
takeCacheOwnership?: boolean;
|
||||
};
|
||||
|
||||
type RuntimeUpsertSessionEntryParams = RuntimeSessionStoreReadParams & {
|
||||
entry: SessionEntry;
|
||||
};
|
||||
|
||||
const loadEmbeddedAgentRuntime = createLazyRuntimeModule(
|
||||
() => import("./runtime-embedded-agent.runtime.js"),
|
||||
);
|
||||
@@ -38,6 +70,60 @@ function resolveRuntimeThinkingCatalog(
|
||||
return configuredCatalog.length > 0 ? configuredCatalog : undefined;
|
||||
}
|
||||
|
||||
function getSessionEntry(params: RuntimeSessionStoreReadParams): SessionEntry | undefined {
|
||||
return loadSessionEntry({
|
||||
agentId: params.agentId,
|
||||
env: params.env,
|
||||
hydrateSkillPromptRefs: params.hydrateSkillPromptRefs,
|
||||
sessionKey: params.sessionKey,
|
||||
storePath: params.storePath,
|
||||
});
|
||||
}
|
||||
|
||||
function listSessionEntries(
|
||||
params: RuntimeSessionStoreListParams = {},
|
||||
): RuntimeSessionStoreEntrySummary[] {
|
||||
return listAccessorSessionEntries({
|
||||
agentId: params.agentId,
|
||||
env: params.env,
|
||||
hydrateSkillPromptRefs: params.hydrateSkillPromptRefs,
|
||||
storePath: params.storePath,
|
||||
});
|
||||
}
|
||||
|
||||
async function updateSessionStoreEntry(
|
||||
params: RuntimeSessionStoreEntryUpdateParams,
|
||||
): Promise<SessionEntry | null> {
|
||||
// Preserve the plugin runtime's object-parameter API while routing the actual
|
||||
// mutation through the storage-neutral session accessor seam.
|
||||
return await updateSessionEntry(
|
||||
{
|
||||
sessionKey: params.sessionKey,
|
||||
storePath: params.storePath,
|
||||
},
|
||||
params.update,
|
||||
{
|
||||
skipMaintenance: params.skipMaintenance,
|
||||
takeCacheOwnership: params.takeCacheOwnership,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async function upsertSessionEntry(params: RuntimeUpsertSessionEntryParams): Promise<void> {
|
||||
// The public runtime helper historically replaced the full entry. Use the
|
||||
// replace seam so removed fields do not survive as merge leftovers.
|
||||
await replaceSessionEntry(
|
||||
{
|
||||
agentId: params.agentId,
|
||||
env: params.env,
|
||||
hydrateSkillPromptRefs: params.hydrateSkillPromptRefs,
|
||||
sessionKey: params.sessionKey,
|
||||
storePath: params.storePath,
|
||||
},
|
||||
params.entry,
|
||||
);
|
||||
}
|
||||
|
||||
/** Creates the plugin runtime agent facade with lazy embedded-agent/session helpers. */
|
||||
export function createRuntimeAgent(): PluginRuntime["agent"] {
|
||||
const agentRuntime = {
|
||||
|
||||
@@ -204,10 +204,45 @@ export type PluginRuntimeCore = {
|
||||
ensureAgentWorkspace: typeof import("../../agents/workspace.js").ensureAgentWorkspace;
|
||||
session: {
|
||||
resolveStorePath: typeof import("../../config/sessions/paths.js").resolveStorePath;
|
||||
getSessionEntry: typeof import("../../config/sessions/store.js").getSessionEntry;
|
||||
listSessionEntries: typeof import("../../config/sessions/store.js").listSessionEntries;
|
||||
getSessionEntry: (params: {
|
||||
agentId?: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
hydrateSkillPromptRefs?: boolean;
|
||||
sessionKey: string;
|
||||
storePath?: string;
|
||||
}) => import("../../config/sessions/types.js").SessionEntry | undefined;
|
||||
listSessionEntries: (
|
||||
params?: Partial<{
|
||||
agentId: string;
|
||||
env: NodeJS.ProcessEnv;
|
||||
hydrateSkillPromptRefs: boolean;
|
||||
storePath: string;
|
||||
}>,
|
||||
) => Array<{
|
||||
sessionKey: string;
|
||||
entry: import("../../config/sessions/types.js").SessionEntry;
|
||||
}>;
|
||||
patchSessionEntry: typeof import("../../config/sessions/store.js").patchSessionEntry;
|
||||
upsertSessionEntry: typeof import("../../config/sessions/store.js").upsertSessionEntry;
|
||||
upsertSessionEntry: (params: {
|
||||
agentId?: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
hydrateSkillPromptRefs?: boolean;
|
||||
sessionKey: string;
|
||||
storePath?: string;
|
||||
entry: import("../../config/sessions/types.js").SessionEntry;
|
||||
}) => Promise<void>;
|
||||
updateSessionStoreEntry: (params: {
|
||||
storePath: string;
|
||||
sessionKey: string;
|
||||
update: (
|
||||
entry: import("../../config/sessions/types.js").SessionEntry,
|
||||
) =>
|
||||
| Promise<Partial<import("../../config/sessions/types.js").SessionEntry> | null>
|
||||
| Partial<import("../../config/sessions/types.js").SessionEntry>
|
||||
| null;
|
||||
skipMaintenance?: boolean;
|
||||
takeCacheOwnership?: boolean;
|
||||
}) => Promise<import("../../config/sessions/types.js").SessionEntry | null>;
|
||||
/**
|
||||
* @deprecated Use getSessionEntry/listSessionEntries for reads and
|
||||
* patchSessionEntry/upsertSessionEntry for writes. This keeps the legacy
|
||||
@@ -216,7 +251,6 @@ export type PluginRuntimeCore = {
|
||||
loadSessionStore: typeof import("../../config/sessions/store-load.js").loadSessionStore;
|
||||
saveSessionStore: import("../../config/sessions/runtime-types.js").SaveSessionStore;
|
||||
updateSessionStore: typeof import("../../config/sessions/store.js").updateSessionStore;
|
||||
updateSessionStoreEntry: typeof import("../../config/sessions/store.js").updateSessionStoreEntry;
|
||||
resolveSessionFilePath: typeof import("../../config/sessions/paths.js").resolveSessionFilePath;
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user