@tjamescouch/niki 0.5.6 → 0.5.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/bin/niki +39 -1
  2. package/package.json +1 -1
package/bin/niki CHANGED
@@ -55,12 +55,16 @@ Options:
55
55
  --max-restarts <n> Max restart attempts, 0=unlimited (default: 0)
56
56
  --restart-delay <secs> Delay between restarts with ±30% jitter (default: 5)
57
57
  --kill-orphaned-mcp Kill stale agentchat-mcp processes on startup (default: off)
58
+ --on-kill <command> Run shell command when agent is killed (receives context via env vars)
59
+ Env vars: NIKI_KILL_REASON, NIKI_TOKENS, NIKI_BUDGET, NIKI_DURATION,
60
+ NIKI_SENDS, NIKI_TOOL_CALLS, NIKI_PID, NIKI_CMD
58
61
 
59
62
  Examples:
60
63
  niki --profile longrun -- gro --persistent --model sonnet
61
64
  niki --budget 500000 -- claude -p "your prompt" --verbose
62
65
  niki --timeout 1800 --max-sends 5 -- claude -p "..." --model sonnet --verbose
63
- niki --restart --max-restarts 10 -- gro --model gpt-5.2 "your prompt"`);
66
+ niki --restart --max-restarts 10 -- gro --model gpt-5.2 "your prompt"
67
+ niki --on-kill 'curl -d "$NIKI_KILL_REASON: $NIKI_TOKENS tokens" ntfy.sh/my-agents' -- claude -p "..."`);
64
68
  process.exit(1);
65
69
  }
66
70
 
@@ -128,6 +132,8 @@ const { values: opts } = parseArgs({
128
132
  'max-restarts': { type: 'string', default: D["max-restarts"] },
129
133
  'restart-delay': { type: 'string', default: D["restart-delay"] },
130
134
  'kill-orphaned-mcp': { type: 'boolean', default: false },
135
+ 'on-kill': { type: 'string' },
136
+ nick: { type: 'string' },
131
137
  },
132
138
  });
133
139
 
@@ -149,6 +155,7 @@ const RESTART = opts.restart;
149
155
  const MAX_RESTARTS = parseInt(opts['max-restarts'], 10);
150
156
  const RESTART_DELAY_S = parseFloat(opts['restart-delay']);
151
157
  const KILL_ORPHANED_MCP = opts['kill-orphaned-mcp'];
158
+ const ON_KILL = opts['on-kill'];
152
159
  const LOG_JSON = opts['log-json'];
153
160
 
154
161
  // --- Invocation rate limiting (prevent tight crash loops) ---
@@ -417,6 +424,35 @@ function checkRateLimits() {
417
424
  }
418
425
  return null;
419
426
  }
427
+ // --- Kill notification hook ---
428
+
429
+ function fireOnKill(reason) {
430
+ if (!ON_KILL) return;
431
+ const duration = Math.round((Date.now() - new Date(state.startedAt).getTime()) / 1000);
432
+ const env = {
433
+ ...process.env,
434
+ NIKI_KILL_REASON: reason,
435
+ NIKI_TOKENS: String(state.tokensTotal),
436
+ NIKI_BUDGET: String(BUDGET),
437
+ NIKI_DURATION: String(duration),
438
+ NIKI_SENDS: String(state.sendCalls),
439
+ NIKI_TOOL_CALLS: String(state.toolCalls),
440
+ NIKI_PID: String(state.pid),
441
+ NIKI_CMD: childCmd,
442
+ };
443
+ try {
444
+ const hook = spawn('sh', ['-c', ON_KILL], {
445
+ env,
446
+ stdio: 'ignore',
447
+ detached: true,
448
+ });
449
+ hook.unref();
450
+ log(`on-kill hook fired: ${ON_KILL}`, 'debug');
451
+ } catch (err) {
452
+ log(`on-kill hook failed: ${err.message}`, 'warn');
453
+ }
454
+ }
455
+
420
456
  // --- Kill logic ---
421
457
 
422
458
  let child = null;
@@ -443,6 +479,8 @@ function killChild(reason) {
443
479
  }
444
480
  log(`KILL — reason: ${reason} | tokens: ${state.tokensTotal}/${BUDGET} | sends: ${state.sendCallsThisMinute}/min | tools: ${state.toolCallsThisMinute}/min`, 'error');
445
481
 
482
+ fireOnKill(reason);
483
+
446
484
  child.kill('SIGTERM');
447
485
 
448
486
  // Grace period, then SIGKILL
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tjamescouch/niki",
3
- "version": "0.5.6",
3
+ "version": "0.5.8",
4
4
  "description": "Deterministic process supervisor for AI agents — token budgets, rate limits, and abort control",
5
5
  "bin": {
6
6
  "niki": "./bin/niki"