@tjamescouch/niki 0.5.5 → 0.5.6

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 +63 -12
  2. package/package.json +1 -1
package/bin/niki CHANGED
@@ -34,6 +34,7 @@ if (SEPARATOR === -1 || SEPARATOR === process.argv.length - 1) {
34
34
  Usage: niki [options] -- <command> [args...]
35
35
 
36
36
  Options:
37
+ --profile <name> Apply a preset profile (default: none; profiles: longrun)
37
38
  --budget <tokens> Max total tokens (input+output) before SIGTERM (default: 1000000)
38
39
  --timeout <seconds> Max wall-clock runtime before SIGTERM (default: 3600)
39
40
  --max-sends <n> Max agentchat_send calls per minute (default: 10)
@@ -53,8 +54,10 @@ Options:
53
54
  --restart Restart the child process when it exits (default: off)
54
55
  --max-restarts <n> Max restart attempts, 0=unlimited (default: 0)
55
56
  --restart-delay <secs> Delay between restarts with ±30% jitter (default: 5)
57
+ --kill-orphaned-mcp Kill stale agentchat-mcp processes on startup (default: off)
56
58
 
57
59
  Examples:
60
+ niki --profile longrun -- gro --persistent --model sonnet
58
61
  niki --budget 500000 -- claude -p "your prompt" --verbose
59
62
  niki --timeout 1800 --max-sends 5 -- claude -p "..." --model sonnet --verbose
60
63
  niki --restart --max-restarts 10 -- gro --model gpt-5.2 "your prompt"`);
@@ -65,28 +68,66 @@ const nikiArgs = process.argv.slice(2, SEPARATOR);
65
68
  const childCmd = process.argv[SEPARATOR + 1];
66
69
  const childArgs = process.argv.slice(SEPARATOR + 2);
67
70
 
71
+ function getProfileArg(args) {
72
+ const i = args.indexOf("--profile");
73
+ if (i === -1) return null;
74
+ const v = args[i + 1];
75
+ return v && !v.startsWith("-") ? v : null;
76
+ }
77
+
78
+ const DEFAULTS = {
79
+ budget: "1000000",
80
+ timeout: "3600",
81
+ "max-sends": "10",
82
+ "max-tool-calls": "30",
83
+ "stall-timeout": "60",
84
+ "startup-timeout": "180",
85
+ "dead-air-timeout": "5",
86
+ "max-nudges": "3",
87
+ cooldown: "5",
88
+ "poll-interval": "1000",
89
+ "max-restarts": "0",
90
+ "restart-delay": "5"
91
+ };
92
+
93
+ const PROFILES = {
94
+ longrun: {
95
+ timeout: "86400",
96
+ "stall-timeout": "0",
97
+ "startup-timeout": "0",
98
+ "dead-air-timeout": "0",
99
+ cooldown: "15"
100
+ }
101
+ };
102
+
103
+ const profileArg = getProfileArg(nikiArgs);
104
+ const D = profileArg && PROFILES[profileArg] ? { ...DEFAULTS, ...PROFILES[profileArg] } : DEFAULTS;
105
+
106
+
68
107
  const { values: opts } = parseArgs({
69
108
  args: nikiArgs,
70
109
  options: {
71
- budget: { type: 'string', default: '1000000' },
72
- timeout: { type: 'string', default: '3600' },
73
- 'max-sends': { type: 'string', default: '10' },
74
- 'max-tool-calls': { type: 'string', default: '30' },
75
- 'stall-timeout': { type: 'string', default: '60' },
76
- 'startup-timeout': { type: 'string', default: '180' },
77
- 'dead-air-timeout': { type: 'string', default: '1440' },
78
- 'max-nudges': { type: 'string', default: '3' },
110
+ profile: { type: 'string' },
111
+ budget: { type: 'string', default: D.budget },
112
+ timeout: { type: 'string', default: D.timeout },
113
+ 'max-sends': { type: 'string', default: D["max-sends"] },
114
+ 'max-tool-calls': { type: 'string', default: D["max-tool-calls"] },
115
+ 'stall-timeout': { type: 'string', default: D["stall-timeout"] },
116
+ 'startup-timeout': { type: 'string', default: D["startup-timeout"] },
117
+ 'dead-air-timeout': { type: 'string', default: D["dead-air-timeout"] },
118
+ 'max-nudges': { type: 'string', default: D["max-nudges"] },
79
119
  log: { type: 'string' },
80
120
  'log-level': { type: 'string', default: 'info' },
81
121
  'log-json': { type: 'boolean', default: false },
82
122
  state: { type: 'string' },
83
123
  metrics: { type: 'string' },
84
- cooldown: { type: 'string', default: '5' },
124
+ cooldown: { type: 'string', default: D.cooldown },
85
125
  'abort-file': { type: 'string' },
86
- 'poll-interval': { type: 'string', default: '1000' },
126
+ 'poll-interval': { type: 'string', default: D["poll-interval"] },
87
127
  restart: { type: 'boolean', default: false },
88
- 'max-restarts': { type: 'string', default: '0' },
89
- 'restart-delay': { type: 'string', default: '5' },
128
+ 'max-restarts': { type: 'string', default: D["max-restarts"] },
129
+ 'restart-delay': { type: 'string', default: D["restart-delay"] },
130
+ 'kill-orphaned-mcp': { type: 'boolean', default: false },
90
131
  },
91
132
  });
92
133
 
@@ -107,6 +148,7 @@ const METRICS_FILE = opts.metrics;
107
148
  const RESTART = opts.restart;
108
149
  const MAX_RESTARTS = parseInt(opts['max-restarts'], 10);
109
150
  const RESTART_DELAY_S = parseFloat(opts['restart-delay']);
151
+ const KILL_ORPHANED_MCP = opts['kill-orphaned-mcp'];
110
152
  const LOG_JSON = opts['log-json'];
111
153
 
112
154
  // --- Invocation rate limiting (prevent tight crash loops) ---
@@ -701,6 +743,15 @@ function startChild() {
701
743
  log(`Restart: enabled | max: ${MAX_RESTARTS || 'unlimited'} | delay: ${RESTART_DELAY_S}s ±30% | restarts so far: ${state.restarts}`, 'debug');
702
744
  }
703
745
 
746
+ // If --kill-orphaned-mcp: kill stale agentchat-mcp procs before spawning.
747
+ // Prevents session_displaced churn from orphaned prior-session MCP processes.
748
+ if (KILL_ORPHANED_MCP) {
749
+ try {
750
+ execSync('pkill -f agentchat-mcp', { stdio: 'ignore' });
751
+ log('Killed orphaned agentchat-mcp processes', 'debug');
752
+ } catch { /* pkill exits non-zero when nothing matched */ }
753
+ }
754
+
704
755
  child = spawn(childCmd, childArgs, {
705
756
  stdio: ['pipe', 'pipe', 'pipe'],
706
757
  env: process.env,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tjamescouch/niki",
3
- "version": "0.5.5",
3
+ "version": "0.5.6",
4
4
  "description": "Deterministic process supervisor for AI agents — token budgets, rate limits, and abort control",
5
5
  "bin": {
6
6
  "niki": "./bin/niki"