Files
openclaw/extensions/acpx/src/pi-session-paths.test.ts
Peter Steinberger b54034d5c2 fix: isolated gateways list and open the operator's HOME Claude/Codex/OpenCode/Pi sessions (#122983)
* fix(sessions): isolated gateways no longer inherit HOME external session catalogs

A gateway on isolated state (custom OPENCLAW_STATE_DIR/CONFIG_PATH/OPENCLAW_HOME,
relocated home, or any named profile) listed, read, continued, archived, and
reopened the operator's real Claude Code/Codex/OpenCode/Pi sessions from the
process HOME. External catalogs now require the default install identity for
process-HOME scans: every catalog verb receives the isolation policy and rejects
HOME-fallback local targets, unknown providers fail closed unless they declare
supportsProcessHomeIsolation, and one structured warning records the skip.
Paired-node hosts and explicitly rooted stores (CLAUDE_CONFIG_DIR, CODEX_HOME,
OPENCODE_DB, Pi session dirs) keep working; default-identity gateways are
unchanged.

* fix(sessions): inject catalog HOME-isolation fact at registry construction

* chore(sdk): regenerate plugin API baselines after rebase

* chore(sdk): regenerate plugin API baselines after rebase
2026-08-13 01:12:03 -07:00

85 lines
3.1 KiB
TypeScript

import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { piSessionStore } from "./pi-session-paths.js";
const temporaryDirectories: string[] = [];
afterEach(async () => {
await Promise.all(
temporaryDirectories.splice(0).map(async (directory) => {
await fs.rm(directory, { recursive: true, force: true });
}),
);
});
describe("Pi session paths", () => {
it("rejects Windows drive-less rooted session paths", () => {
const originalPlatform = process.platform;
try {
Object.defineProperty(process, "platform", { configurable: true, value: "win32" });
expect(() => piSessionStore({ PI_CODING_AGENT_SESSION_DIR: "\\sessions" })).toThrow(
"absolute or home-relative",
);
expect(() => piSessionStore({ PI_CODING_AGENT_SESSION_DIR: "C:\\sessions" })).not.toThrow();
expect(() =>
piSessionStore({ PI_CODING_AGENT_SESSION_DIR: "\\\\server\\share\\sessions" }),
).not.toThrow();
} finally {
Object.defineProperty(process, "platform", { configurable: true, value: originalPlatform });
}
});
it("trims the configured Pi agent directory", () => {
const agentDir = path.join(os.tmpdir(), "pi-agent");
expect(piSessionStore({ PI_CODING_AGENT_DIR: ` ${agentDir} ` })).toEqual({
root: path.join(agentDir, "sessions"),
flat: false,
usesProcessHomeFallback: false,
});
});
it("resolves relative project and global session directories like Pi", async () => {
const projectDirectory = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-pi-project-"));
const agentDirectory = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-pi-agent-"));
temporaryDirectories.push(projectDirectory, agentDirectory);
await fs.mkdir(path.join(projectDirectory, ".pi"), { recursive: true });
await fs.writeFile(
path.join(projectDirectory, ".pi", "settings.json"),
`${JSON.stringify({ sessionDir: "sessions" })}\n`,
);
const env = {
HOME: projectDirectory,
USERPROFILE: projectDirectory,
PI_CODING_AGENT_DIR: agentDirectory,
};
expect(piSessionStore(env, projectDirectory)).toEqual({
root: path.join(projectDirectory, ".pi", "sessions"),
flat: true,
usesProcessHomeFallback: false,
});
await fs.rm(path.join(projectDirectory, ".pi", "settings.json"));
await fs.writeFile(
path.join(agentDirectory, "settings.json"),
`${JSON.stringify({ sessionDir: "custom-sessions" })}\n`,
);
expect(piSessionStore(env, projectDirectory)).toEqual({
root: path.join(agentDirectory, "custom-sessions"),
flat: true,
usesProcessHomeFallback: false,
});
});
it("marks only the default home-derived store as a process-HOME fallback", () => {
const home = path.join(os.tmpdir(), "pi-home");
expect(piSessionStore({ HOME: home }).usesProcessHomeFallback).toBe(true);
expect(
piSessionStore({ HOME: home, PI_CODING_AGENT_SESSION_DIR: path.join(home, "sessions") })
.usesProcessHomeFallback,
).toBe(false);
});
});