SLOPSHOPPER

nixie-hold-http

Spike: a tool.call hook that holds a call on a long-poll through the mods API

newguardnetwork
A shopper browsing a rack in a slop shop
README

<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>

Status

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.

Checks

bun install
bun run check        # format, lint, and the prose check

Layout

  • docs/ holds the overview, the principles, the scope, the decision records, and the designs for what is not built yet. Start with the overview.
  • spikes/ holds throwaway experiments that answer research questions.
Source 1 files
hooks/register.js 33 lines
1// Holds MARK_HOLD calls on one long-poll to the owner's decision service.
2// The wait sits inside $.http.fetch, a mods API call, so it does not count against the hook's limit.
3async function readDecision($, url, started) {
4  try {
5    const response = await $.http.fetch(url);
6    return JSON.parse(response.text).decision;
7  } catch (error) {
8    await $.ui.log(`nixie fetch failed after ${Date.now() - started} ms: ${error.message}`, {
9      to: 'debug',
10    });
11    throw error;
12  }
13}
14
15async function handleToolCall($, e, next) {
16  if (!e.command.includes('MARK_HOLD')) {
17    return next(e);
18  }
19  const base = await $.env.get('NIXIE_DECIDER_URL'),
20    started = Date.now();
21  await $.ui.log(`nixie hold start budget=${JSON.stringify(next.budget)}`, { to: 'debug' });
22  if ((await readDecision($, `${base}/decide?tool_use_id=${e.tool_use_id}`, started)) === 'allow') {
23    return next(e);
24  }
25  return {
26    deny: `nixie-hold-http: the owner refused this command after ${Date.now() - started} ms.`,
27  };
28}
29
30export function register(on) {
31  on('tool.call', { tool: 'Bash' }, handleToolCall);
32}
33