Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions tests/web/pi-adapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,80 @@ test("snapshot pins current and selected sessions while bounding the projection"
}
});

test("discovers default Pi sessions as bounded read-only projections", async () => {
const root = await mkdtemp(join(tmpdir(), "openpi-web-terminal-history-"));
const sessionDirectory = join(root, "web-sessions");
const agentDirectory = join(root, "pi-agent");
const previousAgentDirectory = process.env.PI_CODING_AGENT_DIR;
process.env.PI_CODING_AGENT_DIR = agentDirectory;
try {
await mkdir(sessionDirectory, { recursive: true });
const current = SessionManager.inMemory(root);
const terminal = SessionManager.create(root);
persistSession(terminal, "terminal history", 2);
const terminalPath = terminal.getSessionFile();
assert.ok(terminalPath);
const unrelatedWorkspace = join(root, "unrelated-workspace");
await mkdir(unrelatedWorkspace);
const unrelated = SessionManager.create(unrelatedWorkspace);
persistSession(unrelated, "unrelated-only-token", 3);
const unrelatedPath = unrelated.getSessionFile();
assert.ok(unrelatedPath);
const adapter = new PiWebAdapter(
runtimeFor(root, sessionDirectory, current),
);
const listAll = SessionManager.listAll;
SessionManager.listAll = async () => {
throw new Error("unrelated Session discovery must not be used");
};
try {
const listed = await adapter.listReadOnlyTerminalSessions({ limit: 1 });
assert.equal(listed.total, 1);
assert.equal("allMessagesText" in listed.sessions[0]!, false);
assert.deepEqual(listed.sessions[0], {
id: terminal.getSessionId(),
path: terminalPath,
cwd: root,
modified: listed.sessions[0]?.modified,
created: listed.sessions[0]?.created,
messageCount: 2,
firstMessage: "terminal history",
source: "pi-default",
origin: "terminal",
readOnly: true,
});
const inspected = await adapter.getReadOnlyTerminalSession(terminalPath);
assert.equal(inspected.readOnly, true);
assert.equal(inspected.source, "pi-default");
assert.equal(inspected.preview.messages.length, 2);
assert.equal(
(
await adapter.listReadOnlyTerminalSessions({
query: "unrelated-only-token",
})
).total,
0,
);
await assert.rejects(
adapter.getReadOnlyTerminalSession(unrelatedPath),
(error: unknown) =>
error instanceof Error &&
(error as { code?: string }).code === "SESSION_NOT_FOUND",
);
} finally {
SessionManager.listAll = listAll;
}
assert.equal((await SessionManager.listAll(sessionDirectory)).length, 0);
} finally {
if (previousAgentDirectory === undefined) {
delete process.env.PI_CODING_AGENT_DIR;
} else {
process.env.PI_CODING_AGENT_DIR = previousAgentDirectory;
}
await rm(root, { recursive: true, force: true });
}
});

test("an unbound Web runtime never projects its bootstrap cwd as a workspace or Session", async () => {
const root = await mkdtemp(join(tmpdir(), "openpi-web-unbound-"));
const bootstrap = join(root, ".bootstrap-workspace");
Expand Down
137 changes: 137 additions & 0 deletions tests/web/web-host.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,143 @@ test("serves workspaces through a runtime isolated from terminal sessions", asyn
}
});

test("serves terminal Sessions through a read-only bounded endpoint", async () => {
const root = await mkdtemp(join(tmpdir(), "openpi-web-terminal-host-"));
const previousAgentDirectory = process.env.PI_CODING_AGENT_DIR;
process.env.PI_CODING_AGENT_DIR = join(root, "pi-agent");
const sessionManager = SessionManager.inMemory(root);
const terminal = SessionManager.create(root);
terminal.appendMessage({
role: "user",
content: "terminal endpoint",
timestamp: 1,
});
terminal.appendMessage({
role: "assistant",
content: [],
api: "openai-responses",
provider: "fixture",
model: "fixture",
usage: {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
totalTokens: 0,
cost: {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
total: 0,
},
},
stopReason: "stop",
timestamp: 1,
});
const runtime: WebRuntimeController = {
cwd: root,
workspaceSelected: true,
sessionDirectory: join(root, "web-sessions"),
sessionManager,
isIdle: () => true,
getActiveTurn: () => undefined,
cancelTurn: async (options) => ({ ...options, state: "stale-turn" }),
sendPrompt: async () => ({ pendingFollowUps: 0 }),
newSession: async () => ({ cancelled: false }),
switchSession: async () => ({ cancelled: false }),
listModels: () => [],
setModel: async () => {
throw new Error("not available");
},
subscribe: () => () => {},
dispose: async () => {},
};
const host = new WebHost({ runtime });
try {
await host.start();
const launched = new URL(host.url);
const token = new URLSearchParams(launched.hash.slice(1)).get("token");
assert.ok(token);
const headers = { Authorization: `Bearer ${token}` };
const listed = await fetch(
`${launched.origin}/api/terminal-sessions?limit=1`,
{
headers,
},
);
assert.equal(listed.status, 200);
const page = (await listed.json()) as {
sessions: Array<{
path: string;
source: string;
origin: string;
readOnly: boolean;
}>;
total: number;
};
assert.equal(page.total, 1);
assert.equal(page.sessions[0]?.path, terminal.getSessionFile());
assert.equal(page.sessions[0]?.source, "pi-default");
assert.equal(page.sessions[0]?.origin, "terminal");
assert.equal(page.sessions[0]?.readOnly, true);
assert.equal(
(
await fetch(
`${launched.origin}/api/terminal-sessions?query=${"x".repeat(201)}`,
{ headers },
)
).status,
400,
);
assert.equal(
(
await fetch(`${launched.origin}/api/terminal-sessions?cursor=nope`, {
headers,
})
).status,
400,
);
assert.equal(
(
await fetch(`${launched.origin}/api/terminal-sessions?limit=101`, {
headers,
})
).status,
400,
);
const missing = await fetch(
`${launched.origin}/api/terminal-sessions?path=${encodeURIComponent(join(root, "missing.jsonl"))}`,
{ headers },
);
assert.equal(missing.status, 404);
assert.deepEqual(await missing.json(), {
code: "SESSION_NOT_FOUND",
error: "Terminal Session is not available",
});
const inspected = await fetch(
`${launched.origin}/api/terminal-sessions?path=${encodeURIComponent(terminal.getSessionFile()!)}`,
{ headers },
);
assert.equal(inspected.status, 200);
const details = (await inspected.json()) as {
readOnly: boolean;
preview: { messages: unknown[]; retainedBytes: number };
};
assert.equal(details.readOnly, true);
assert.equal(details.preview.messages.length, 2);
assert.ok(details.preview.retainedBytes > 0);
} finally {
await host.stop();
if (previousAgentDirectory === undefined) {
delete process.env.PI_CODING_AGENT_DIR;
} else {
process.env.PI_CODING_AGENT_DIR = previousAgentDirectory;
}
await rm(root, { recursive: true, force: true });
}
});

test("an unbound Host exposes no bootstrap Session and rejects prompt bypasses", async () => {
const root = await mkdtemp(join(tmpdir(), "openpi-web-unbound-host-"));
const bootstrap = join(root, ".bootstrap-workspace");
Expand Down
Loading
Loading