Spike: a tool.call hook that holds a call on a promise of its own, with a .catch handler

<h1>nixie</h1>
A personal assistant platform that its owner runs and controls: every rule readable and editable, every decision recorded, and personal data kept on the owner's own hosts.
<a href="./docs/README.md">Documentation</a> • <a href="./docs/README.md#decisions">Decisions</a> • <a href="./AGENTS.md">Agent Guidelines</a>
nixie is in Phase 2, Research, and has no product code. The work runs in 4 phases: brainstorm, research, design, and decide. Decisions are recorded as research settles each question.
bun install
bun run check # format, lint, and the prose check
hooks/register.js 29 lines1// A hooks module imports only its own files, so the timer comes from setTimeout.
2function waitMs(ms) {
3 // oxlint-disable-next-line promise/avoid-new
4 return new Promise((resolve) => {
5 setTimeout(resolve, ms);
6 });
7}
8
9// Holds MARK_HOLD calls for 15 s on a timer of the hook's own. The .catch handler denies on failure.
10async function handleToolCall($, e, next) {
11 if (!e.command.includes('MARK_HOLD')) {
12 return next(e);
13 }
14 await waitMs(15_000);
15 return { deny: 'nixie-hold-promise-catch: the owner refused this command.' };
16}
17
18function handleFailure($, e, next) {
19 return {
20 deny: `nixie policy failed (${next.error.kind}: ${next.error.message}), so the command did not run.`,
21 };
22}
23
24export function register(on) {
25 // `.catch` here is the mods API's error handler on a registration, not a promise method.
26 // oxlint-disable-next-line promise/prefer-await-to-then
27 on('tool.call', { tool: 'Bash' }, handleToolCall).catch(handleFailure);
28}
29