A bot hook (bot_hooks) is a secret URL, POST /api/hooks/<token>, whose calls queue the hook's brief plus the raw body as a task, per user and bot like a routine. Only the token's hash is kept (shown once, rotatable); an optional HMAC-SHA256 signing secret is stored encrypted. Calls are rate limited per address and per hook, recorded in bot_hook_events (pruned daily), deduplicated by delivery id, and a call while the last run still waits joins its brief instead of queueing another. The payload is fenced data and HookRunNote tells the bot never to take instructions from it; failed runs retry like routines. A hook can instead watch a computer's browser notifications: the app installs a small watcher that owns org.freedesktop.Notifications on the sandbox's session bus, so Chrome hands it every notification (it only looks for that service at start-up, so Chrome is restarted once, never mid-task), and it posts each one to its hooks, optionally filtered. Computers reach the app at COMPUTER_APP_URL or the app container's name. Users make hooks in the conversation's Hooks modal and manage them all on /admin/routines; bots never create or change hooks. Co-Authored-By: Claude Opus 5.5 <[email protected]>
36 lines
1.4 KiB
TypeScript
36 lines
1.4 KiB
TypeScript
import { z } from "zod";
|
|
import type { HarnessTool } from "../../types";
|
|
|
|
type Params = {
|
|
/**
|
|
* Gets why the run failed. The task runner throws it after the run, so
|
|
* the queue retries the job.
|
|
*/
|
|
onFail: (params: { reason: string }) => void;
|
|
};
|
|
|
|
type Args = {
|
|
reason: string;
|
|
};
|
|
|
|
/**
|
|
* Lets a run that acts on its own (a routine's, a hook call's) say it
|
|
* couldn't do what its brief asks (a send that failed, a service that's
|
|
* down), so it's retried later instead of posting a report. Only given to
|
|
* those runs.
|
|
*/
|
|
export default function reportRunFailedTool({ onFail }: Params): HarnessTool<Args> {
|
|
return {
|
|
name: "report_run_failed",
|
|
description: `Call this when this run (a routine's or a hook call's) couldn't do what its brief asks (a tool or service failed, something it needs is down). The run is tried again later, and the user is told only if every try fails. Say why in one or two sentences. Don't call it for something only the user can do; ask them to take over instead.`,
|
|
schema: z.object({
|
|
reason: z.string().trim().min(1).max(1000).describe("Why the run failed, for the user"),
|
|
}),
|
|
run: async ({ args }) => {
|
|
onFail({ reason: args.reason.trim() });
|
|
|
|
return `Recorded. Stop here: the run will be tried again later. Don't write a report.`;
|
|
},
|
|
};
|
|
}
|