Skip to content

Commit 845c433

Browse files
committed
fix(sessions): allow cross-agent session file paths in multi-agent setups
When OPENCLAW_STATE_DIR changes between session creation and resolution (e.g., after reinstall or config change), absolute session file paths pointing to other agents' sessions directories were rejected even though they structurally match the valid .../agents/<agentId>/sessions/... pattern. The existing fallback logic in resolvePathWithinSessionsDir extracts the agent ID from the path and tries to resolve it via the current env's state directory. When those directories differ, the containment check fails. Now, if the path structurally matches the agent sessions pattern (validated by extractAgentIdFromAbsoluteSessionPath), we accept it directly as a final fallback. Fixes #15410, Fixes #15565, Fixes #15468
1 parent b2aa6e0 commit 845c433

File tree

2 files changed

+61
-0
lines changed

2 files changed

+61
-0
lines changed

src/config/sessions.test.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,62 @@ describe("sessions", () => {
469469
}
470470
});
471471

472+
it("resolves cross-agent absolute sessionFile paths", () => {
473+
const prev = process.env.OPENCLAW_STATE_DIR;
474+
process.env.OPENCLAW_STATE_DIR = "/home/user/.openclaw";
475+
try {
476+
// Agent bot1 resolves a sessionFile that belongs to agent bot2
477+
const sessionFile = resolveSessionFilePath(
478+
"sess-1",
479+
{ sessionFile: "/home/user/.openclaw/agents/bot2/sessions/sess-1.jsonl" },
480+
{ agentId: "bot1" },
481+
);
482+
expect(sessionFile).toBe("/home/user/.openclaw/agents/bot2/sessions/sess-1.jsonl");
483+
} finally {
484+
if (prev === undefined) {
485+
delete process.env.OPENCLAW_STATE_DIR;
486+
} else {
487+
process.env.OPENCLAW_STATE_DIR = prev;
488+
}
489+
}
490+
});
491+
492+
it("resolves cross-agent paths when OPENCLAW_STATE_DIR differs from stored paths", () => {
493+
const prev = process.env.OPENCLAW_STATE_DIR;
494+
process.env.OPENCLAW_STATE_DIR = "/different/state";
495+
try {
496+
// sessionFile was created under a different state dir than current env
497+
const sessionFile = resolveSessionFilePath(
498+
"sess-1",
499+
{ sessionFile: "/original/state/agents/bot2/sessions/sess-1.jsonl" },
500+
{ agentId: "bot1" },
501+
);
502+
expect(sessionFile).toBe("/original/state/agents/bot2/sessions/sess-1.jsonl");
503+
} finally {
504+
if (prev === undefined) {
505+
delete process.env.OPENCLAW_STATE_DIR;
506+
} else {
507+
process.env.OPENCLAW_STATE_DIR = prev;
508+
}
509+
}
510+
});
511+
512+
it("rejects absolute sessionFile paths outside agent sessions directories", () => {
513+
const prev = process.env.OPENCLAW_STATE_DIR;
514+
process.env.OPENCLAW_STATE_DIR = "/home/user/.openclaw";
515+
try {
516+
expect(() =>
517+
resolveSessionFilePath("sess-1", { sessionFile: "/etc/passwd" }, { agentId: "bot1" }),
518+
).toThrow(/within sessions directory/);
519+
} finally {
520+
if (prev === undefined) {
521+
delete process.env.OPENCLAW_STATE_DIR;
522+
} else {
523+
process.env.OPENCLAW_STATE_DIR = prev;
524+
}
525+
}
526+
});
527+
472528
it("updateSessionStoreEntry merges concurrent patches", async () => {
473529
const mainSessionKey = "agent:main:main";
474530
const dir = await createCaseDir("updateSessionStoreEntry");

src/config/sessions/paths.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,11 @@ function resolvePathWithinSessionsDir(
152152
if (resolvedFromPath) {
153153
return resolvedFromPath;
154154
}
155+
// The path structurally matches .../agents/<agentId>/sessions/...
156+
// Accept it even if the root directory differs from the current env
157+
// (e.g., OPENCLAW_STATE_DIR changed between session creation and resolution).
158+
// The structural pattern provides sufficient containment guarantees.
159+
return path.resolve(trimmed);
155160
}
156161
}
157162
if (!normalized || normalized.startsWith("..") || path.isAbsolute(normalized)) {

0 commit comments

Comments
 (0)