Sessions: fix sessions_list transcriptPath path resolution
This commit is contained in:
@@ -82,6 +82,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Dashboard/Sessions: allow authenticated Control UI clients to delete and patch sessions while still blocking regular webchat clients from session mutation RPCs, fixing Dashboard session delete failures. (#21264) Thanks @jskoiz.
|
||||
- TUI/Session model status: clear stale runtime model identity when model overrides change so `/model` updates are reflected immediately in `sessions.patch` responses and `sessions.list` status surfaces. (#28619) Thanks @lejean2000.
|
||||
- Memory/Hybrid recall: when strict hybrid scoring yields no hits, preserve keyword-backed matches using a text-weight floor so freshly indexed lexical canaries no longer disappear behind `minScore` filtering. (#29112) Thanks @ceo-nada.
|
||||
- Agents/Sessions list transcript paths: resolve `sessions_list` `transcriptPath` via agent-aware session path options and ignore combined-store sentinel paths (`(multiple)`) so listed transcript paths always point to the state directory. (#28379) Thanks @fafuzuoluo.
|
||||
- Podman/Quadlet setup: fix `sed` escaping and UID mismatch in Podman Quadlet setup. (#26414) Thanks @KnHack and @vincentkoc.
|
||||
- Browser/Navigate: resolve the correct `targetId` in navigate responses after renderer swaps. (#25326) Thanks @stone-jin and @vincentkoc.
|
||||
- Agents/Ollama discovery: skip Ollama discovery when explicit models are configured. (#28827) Thanks @Kansodata and @vincentkoc.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import path from "node:path";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
addSubagentRunForTests,
|
||||
@@ -171,6 +172,46 @@ describe("sessions tools", () => {
|
||||
expect(cronDetails.sessions?.[0]?.kind).toBe("cron");
|
||||
});
|
||||
|
||||
it("sessions_list resolves transcriptPath from agent state dir for multi-store listings", async () => {
|
||||
callGatewayMock.mockImplementation(async (opts: unknown) => {
|
||||
const request = opts as { method?: string };
|
||||
if (request.method === "sessions.list") {
|
||||
return {
|
||||
path: "(multiple)",
|
||||
sessions: [
|
||||
{
|
||||
key: "main",
|
||||
kind: "direct",
|
||||
sessionId: "sess-main",
|
||||
updatedAt: 12,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
return {};
|
||||
});
|
||||
|
||||
const tool = createOpenClawTools().find((candidate) => candidate.name === "sessions_list");
|
||||
expect(tool).toBeDefined();
|
||||
if (!tool) {
|
||||
throw new Error("missing sessions_list tool");
|
||||
}
|
||||
|
||||
const result = await tool.execute("call2b", {});
|
||||
const details = result.details as {
|
||||
sessions?: Array<{
|
||||
key?: string;
|
||||
transcriptPath?: string;
|
||||
}>;
|
||||
};
|
||||
const main = details.sessions?.find((session) => session.key === "main");
|
||||
expect(typeof main?.transcriptPath).toBe("string");
|
||||
expect(main?.transcriptPath).not.toContain("(multiple)");
|
||||
expect(main?.transcriptPath).toContain(
|
||||
path.join("agents", "main", "sessions", "sess-main.jsonl"),
|
||||
);
|
||||
});
|
||||
|
||||
it("sessions_history filters tool messages by default", async () => {
|
||||
callGatewayMock.mockImplementation(async (opts: unknown) => {
|
||||
const request = opts as { method?: string };
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import path from "node:path";
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import { loadConfig } from "../../config/config.js";
|
||||
import { resolveSessionFilePath } from "../../config/sessions.js";
|
||||
import { resolveSessionFilePath, resolveSessionFilePathOptions } from "../../config/sessions.js";
|
||||
import { callGateway } from "../../gateway/call.js";
|
||||
import { resolveAgentIdFromSessionKey } from "../../routing/session-key.js";
|
||||
import type { AnyAgentTool } from "./common.js";
|
||||
@@ -156,13 +155,14 @@ export function createSessionsListTool(opts?: {
|
||||
let transcriptPath: string | undefined;
|
||||
if (sessionId && storePath) {
|
||||
try {
|
||||
const sessionPathOpts = resolveSessionFilePathOptions({
|
||||
agentId: resolveAgentIdFromSessionKey(key),
|
||||
storePath,
|
||||
});
|
||||
transcriptPath = resolveSessionFilePath(
|
||||
sessionId,
|
||||
sessionFile ? { sessionFile } : undefined,
|
||||
{
|
||||
agentId: resolveAgentIdFromSessionKey(key),
|
||||
sessionsDir: path.dirname(storePath),
|
||||
},
|
||||
sessionPathOpts,
|
||||
);
|
||||
} catch {
|
||||
transcriptPath = undefined;
|
||||
|
||||
@@ -39,13 +39,15 @@ export type SessionFilePathOptions = {
|
||||
sessionsDir?: string;
|
||||
};
|
||||
|
||||
const MULTI_STORE_PATH_SENTINEL = "(multiple)";
|
||||
|
||||
export function resolveSessionFilePathOptions(params: {
|
||||
agentId?: string;
|
||||
storePath?: string;
|
||||
}): SessionFilePathOptions | undefined {
|
||||
const agentId = params.agentId?.trim();
|
||||
const storePath = params.storePath?.trim();
|
||||
if (storePath) {
|
||||
if (storePath && storePath !== MULTI_STORE_PATH_SENTINEL) {
|
||||
const sessionsDir = path.dirname(path.resolve(storePath));
|
||||
return agentId ? { sessionsDir, agentId } : { sessionsDir };
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
import type { SessionConfig } from "../types.base.js";
|
||||
import {
|
||||
resolveSessionFilePath,
|
||||
resolveSessionFilePathOptions,
|
||||
resolveSessionTranscriptPathInDir,
|
||||
validateSessionId,
|
||||
} from "./paths.js";
|
||||
@@ -68,6 +69,13 @@ describe("session path safety", () => {
|
||||
expect(resolved).toBe(path.resolve(sessionsDir, "sess-1.jsonl"));
|
||||
});
|
||||
|
||||
it("ignores multi-store sentinel paths when deriving session file options", () => {
|
||||
expect(resolveSessionFilePathOptions({ agentId: "worker", storePath: "(multiple)" })).toEqual({
|
||||
agentId: "worker",
|
||||
});
|
||||
expect(resolveSessionFilePathOptions({ storePath: "(multiple)" })).toBeUndefined();
|
||||
});
|
||||
|
||||
it("accepts symlink-alias session paths that resolve under the sessions dir", () => {
|
||||
if (process.platform === "win32") {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user