feat(hook): close mailbox on SessionEnd
New SessionEnd plugin hook runs `claude-mailbox session-end`, which derives the session's auto-name from stdin and asks the daemon to delete the mailbox if it has no pending messages either direction. Renamed mailboxes are preserved (the auto-name no longer exists, so DELETE is a no-op). The daemon-side `SKIP_UPSERT_PATHS` prevents the request from auto-recreating the mailbox. Sweeper remains the safety net for sessions that exit ungracefully. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -310,3 +310,54 @@ describe("pruneStale", () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("deleteIfEmpty", () => {
|
||||
it("deletes a fresh mailbox with no pending messages and wipes its delivered history", () => {
|
||||
const store = new MailboxStore(dbPath);
|
||||
try {
|
||||
store.send("alice", "bob", "old");
|
||||
store.checkInbox("bob");
|
||||
const r = store.deleteIfEmpty("bob");
|
||||
expect(r).toEqual({ deleted: true, reason: "deleted" });
|
||||
expect(store.listMailboxes().map((m) => m.name)).not.toContain("bob");
|
||||
} finally {
|
||||
store.close();
|
||||
}
|
||||
});
|
||||
|
||||
it("refuses to delete when the mailbox has undelivered incoming mail", () => {
|
||||
const store = new MailboxStore(dbPath);
|
||||
try {
|
||||
store.send("alice", "bob", "still pending");
|
||||
const r = store.deleteIfEmpty("bob");
|
||||
expect(r).toEqual({ deleted: false, reason: "has-pending" });
|
||||
expect(store.peek("bob").pending).toBe(1);
|
||||
} finally {
|
||||
store.close();
|
||||
}
|
||||
});
|
||||
|
||||
it("refuses to delete when the mailbox has undelivered outgoing mail", () => {
|
||||
const store = new MailboxStore(dbPath);
|
||||
try {
|
||||
store.send("alice", "bob", "from alice");
|
||||
const r = store.deleteIfEmpty("alice");
|
||||
expect(r).toEqual({ deleted: false, reason: "has-pending" });
|
||||
expect(store.listMailboxes().map((m) => m.name)).toContain("alice");
|
||||
} finally {
|
||||
store.close();
|
||||
}
|
||||
});
|
||||
|
||||
it("is a no-op for an unknown name (e.g. renamed mailbox)", () => {
|
||||
const store = new MailboxStore(dbPath);
|
||||
try {
|
||||
store.upsertMailbox("alice");
|
||||
const r = store.deleteIfEmpty("nope");
|
||||
expect(r).toEqual({ deleted: false, reason: "not-found" });
|
||||
expect(store.listMailboxes().map((m) => m.name)).toEqual(["alice"]);
|
||||
} finally {
|
||||
store.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user