mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-08-28 08:35:57 +00:00
feat(pi-notify): notify on permission prompts
This commit is contained in:
@@ -6,15 +6,20 @@ import type { NotifyConfig } from "../src/types.ts";
|
||||
|
||||
type Handler = (...args: any[]) => any;
|
||||
|
||||
const savedMinSeconds = process.env.PI_NOTIFY_MIN_SECONDS;
|
||||
const savedEnv = new Map(
|
||||
["PI_NOTIFY_MIN_SECONDS", "PI_NOTIFY_MESSAGE_SOURCE"].map((key) => [key, process.env[key]]),
|
||||
);
|
||||
|
||||
afterEach(() => {
|
||||
if (savedMinSeconds === undefined) delete process.env.PI_NOTIFY_MIN_SECONDS;
|
||||
else process.env.PI_NOTIFY_MIN_SECONDS = savedMinSeconds;
|
||||
for (const [key, value] of savedEnv) {
|
||||
if (value === undefined) delete process.env[key];
|
||||
else process.env[key] = value;
|
||||
}
|
||||
});
|
||||
|
||||
function harness(mode: ExtensionContext["mode"] = "tui") {
|
||||
const handlers = new Map<string, Handler>();
|
||||
const eventHandlers = new Map<string, Handler>();
|
||||
const commands = new Map<string, { handler: Handler }>();
|
||||
const notifications: NotifyConfig[] = [];
|
||||
const uiMessages: string[] = [];
|
||||
@@ -24,6 +29,12 @@ function harness(mode: ExtensionContext["mode"] = "tui") {
|
||||
on(name: string, handler: Handler) {
|
||||
handlers.set(name, handler);
|
||||
},
|
||||
events: {
|
||||
on(name: string, handler: Handler) {
|
||||
eventHandlers.set(name, handler);
|
||||
return () => eventHandlers.delete(name);
|
||||
},
|
||||
},
|
||||
registerCommand(name: string, command: { handler: Handler }) {
|
||||
commands.set(name, command);
|
||||
},
|
||||
@@ -50,6 +61,7 @@ function harness(mode: ExtensionContext["mode"] = "tui") {
|
||||
|
||||
return {
|
||||
handlers,
|
||||
eventHandlers,
|
||||
commands,
|
||||
notifications,
|
||||
uiMessages,
|
||||
@@ -84,8 +96,14 @@ describe("pi-notify extension", () => {
|
||||
process.env.PI_NOTIFY_MIN_SECONDS = "0";
|
||||
for (const mode of ["rpc", "json", "print"] as const) {
|
||||
const h = harness(mode);
|
||||
await h.handlers.get("session_start")?.({}, h.ctx);
|
||||
await h.handlers.get("agent_start")?.({}, h.ctx);
|
||||
await h.handlers.get("agent_settled")?.({}, h.ctx);
|
||||
h.eventHandlers.get("permissions:ui_prompt")?.({
|
||||
requestId: "perm-non-tui",
|
||||
surface: "bash",
|
||||
value: "git push",
|
||||
});
|
||||
assert.equal(h.notifications.length, 0, mode);
|
||||
}
|
||||
});
|
||||
@@ -103,4 +121,49 @@ describe("pi-notify extension", () => {
|
||||
await command.handler("status", h.ctx);
|
||||
assert.match(h.uiMessages.at(-1) ?? "", /kitty/);
|
||||
});
|
||||
|
||||
it("notifies immediately when permission confirmation needs human input", async () => {
|
||||
process.env.PI_NOTIFY_MIN_SECONDS = "3600";
|
||||
const h = harness();
|
||||
await h.handlers.get("session_start")?.({}, h.ctx);
|
||||
|
||||
h.eventHandlers.get("permissions:ui_prompt")?.({
|
||||
requestId: "perm-1",
|
||||
source: "tool_call",
|
||||
surface: "bash",
|
||||
value: "git push\norigin main",
|
||||
agentName: null,
|
||||
forwarding: { requesterAgentName: "reviewer", requesterSessionId: "child-1" },
|
||||
});
|
||||
|
||||
assert.equal(h.notifications.length, 1);
|
||||
assert.equal(h.notifications[0]?.body, "Permission required · reviewer · bash\ngit push origin main");
|
||||
assert.equal(h.notifications[0]?.durationMs, 0);
|
||||
assert.equal(h.notifications[0]?.minDurationMs, 0, "permission prompts bypass the completion threshold");
|
||||
assert.equal(h.notifications[0]?.notificationId, "pi-session-test");
|
||||
});
|
||||
|
||||
it("keeps permission details private and honors the session off override", async () => {
|
||||
process.env.PI_NOTIFY_MESSAGE_SOURCE = "none";
|
||||
const h = harness();
|
||||
await h.handlers.get("session_start")?.({}, h.ctx);
|
||||
const prompt = { requestId: "perm-secret", surface: "bash", value: "cat private-file" };
|
||||
|
||||
h.eventHandlers.get("permissions:ui_prompt")?.(prompt);
|
||||
assert.equal(h.notifications[0]?.body, "Permission confirmation required");
|
||||
|
||||
await h.commands.get("notify")?.handler("off", h.ctx);
|
||||
h.eventHandlers.get("permissions:ui_prompt")?.(prompt);
|
||||
assert.equal(h.notifications.length, 1);
|
||||
});
|
||||
|
||||
it("ignores malformed permission broadcasts and unsubscribes on shutdown", async () => {
|
||||
const h = harness();
|
||||
await h.handlers.get("session_start")?.({}, h.ctx);
|
||||
h.eventHandlers.get("permissions:ui_prompt")?.({ surface: "bash", value: "git push" });
|
||||
assert.equal(h.notifications.length, 0);
|
||||
|
||||
await h.handlers.get("session_shutdown")?.({}, h.ctx);
|
||||
assert.equal(h.eventHandlers.has("permissions:ui_prompt"), false);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user