Tie each hook to a session, picked in the hook form
A hook's calls used to run in whatever session was active at the time. Now `bot_hooks.session_id` pins them to one session, open or closed (null still follows the active one). The hook form picks where it runs (the bot's own chat or one of its workspaces) and a session there, or a new one of its own started beside the active one so the chat on screen stays. A bot's proposed hook gets the session it was proposed in, and a missing session is replaced on the next call. Changing place or session is refused while a run is open; the computer stays only if reachable. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import AppError from "./app-error";
|
||||
import hookMoveComputers from "./hook-move-computers";
|
||||
|
||||
describe("hookMoveComputers", () => {
|
||||
test("keeps the run computer the bot reaches there", () => {
|
||||
expect(hookMoveComputers({ title: "H", watching: false, computer_id: 3, reachable_ids: [1, 3] })).toEqual({
|
||||
computer_id: 3,
|
||||
computer_dropped: false,
|
||||
});
|
||||
});
|
||||
|
||||
test("drops one it doesn't reach", () => {
|
||||
expect(hookMoveComputers({ title: "H", watching: false, computer_id: 3, reachable_ids: [1] })).toEqual({
|
||||
computer_id: null,
|
||||
computer_dropped: true,
|
||||
});
|
||||
});
|
||||
|
||||
test("nothing to drop without a run computer", () => {
|
||||
expect(hookMoveComputers({ title: "H", watching: false, computer_id: null, reachable_ids: [] })).toEqual({
|
||||
computer_id: null,
|
||||
computer_dropped: false,
|
||||
});
|
||||
});
|
||||
|
||||
test("a notification hook needs its watched computer there", () => {
|
||||
expect(() =>
|
||||
hookMoveComputers({ title: "H", watching: true, computer_id: 2, watch_computer_id: 2, reachable_ids: [1] }),
|
||||
).toThrow(AppError);
|
||||
|
||||
expect(
|
||||
hookMoveComputers({ title: "H", watching: true, computer_id: 2, watch_computer_id: 2, reachable_ids: ["2"] }),
|
||||
).toEqual({ computer_id: 2, computer_dropped: false });
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,35 @@
|
||||
import AppError from "./app-error";
|
||||
|
||||
type Params = {
|
||||
/**
|
||||
* The hook's title, for the refusal.
|
||||
*/
|
||||
title: string;
|
||||
watching: boolean;
|
||||
computer_id?: number | string | null;
|
||||
watch_computer_id?: number | string | null;
|
||||
/**
|
||||
* The ids of the computers its bot reaches where the hook goes.
|
||||
*/
|
||||
reachable_ids: (number | string)[];
|
||||
};
|
||||
|
||||
/**
|
||||
* The computer a moved hook's runs use: kept only when its bot reaches it
|
||||
* in the new place, else null (the bot's current one). A
|
||||
* browser-notification hook can't move where its bot doesn't reach the
|
||||
* computer it watches, since that computer's watcher is what calls it.
|
||||
*/
|
||||
export default function hookMoveComputers({ title, watching, computer_id, watch_computer_id, reachable_ids }: Params) {
|
||||
const reaches = (id?: number | string | null) => Boolean(id) && reachable_ids.some((it) => String(it) == String(id));
|
||||
|
||||
if (watching && !reaches(watch_computer_id)) {
|
||||
throw new AppError(
|
||||
`"${title}" takes the browser notifications of computer #${watch_computer_id}, which the bot doesn't reach there.`,
|
||||
);
|
||||
}
|
||||
|
||||
const kept = reaches(computer_id) ? Number(computer_id) : null;
|
||||
|
||||
return { computer_id: kept, computer_dropped: Boolean(computer_id && !kept) };
|
||||
}
|
||||
@@ -39,4 +39,10 @@ describe("hookSubtitle", () => {
|
||||
"1 run · called 5m · signed · This hook is paused.",
|
||||
);
|
||||
});
|
||||
|
||||
test("a hook with a session of its own names it", () => {
|
||||
const hook = { bot_name: "Scout", session_id: 9, session_title: "Hook: Issues", run_count: 1 } as BotHookWithNames;
|
||||
|
||||
expect(hookSubtitle({ hook, now })).toBe('Scout · in "Hook: Issues" · 1 run · never called');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,7 +13,8 @@ type Params = {
|
||||
};
|
||||
|
||||
/**
|
||||
* The line under a hook's name: whose it is, where it runs, which
|
||||
* The line under a hook's name: whose it is, where it runs (and in which
|
||||
* session, when it has one of its own), which
|
||||
* computer's notifications it gets (and its filter), how many runs
|
||||
* it started and when it was last called, whether calls must be signed,
|
||||
* then why its last call or run failed.
|
||||
@@ -24,6 +25,7 @@ export default function hookSubtitle({ hook, now = Date.now(), show_bot = true,
|
||||
return [
|
||||
show_bot ? hook.bot_name || "A bot" : "",
|
||||
show_workspace ? hook.workspace_name || "" : "",
|
||||
hook.session_id ? `in "${hook.session_title || `session ${hook.session_id}`}"` : "",
|
||||
hook.source == "browser_notifications"
|
||||
? `notifications on ${hook.watch_computer_name || "a computer"}${hook.watch_filter ? ` with "${hook.watch_filter}"` : ""}`
|
||||
: "",
|
||||
|
||||
Reference in New Issue
Block a user