niahere 0.2.84 → 0.2.85

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.
package/README.md CHANGED
@@ -55,6 +55,8 @@ nia init — interactive setup (db, channels, persona, age
55
55
  nia start / stop — daemon + OS service (launchd/systemd)
56
56
  nia restart — restart daemon (service-aware)
57
57
  nia status — show daemon, jobs, channels, chat rooms
58
+ nia active [--full] — show active engine count or details
59
+ nia model [name] — show or set global Claude model
58
60
  nia health — check daemon, db, channels, config
59
61
  nia chat [-c|-r] [--channel ch] — terminal chat (new by default, -c continue, -r pick)
60
62
  nia run <prompt> — one-shot prompt execution
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "niahere",
3
- "version": "0.2.84",
3
+ "version": "0.2.85",
4
4
  "description": "A personal AI assistant daemon — chat, scheduled jobs, persona system, extensible via skills.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -22,6 +22,7 @@ import type {
22
22
  import { truncate, formatToolUse } from "../utils/format-activity";
23
23
  import { finalizeSession, cancelPending } from "../core/finalizer";
24
24
  import { log } from "../utils/log";
25
+ import { getConfig } from "../utils/config";
25
26
  import { isRetryableApiError, sleep } from "../utils/retry";
26
27
  import { registerActiveHandle, unregisterActiveHandle } from "../core/active-handles";
27
28
  import { resolveJobPrompt } from "../core/job-prompt";
@@ -108,6 +109,11 @@ export function formatChatError(rawError: string | null | undefined): string {
108
109
  return `[error] ${error}`;
109
110
  }
110
111
 
112
+ export function resolveSdkModel(contextModel?: string | null): string | undefined {
113
+ const model = contextModel || getConfig().model;
114
+ return model && model !== "default" ? model : undefined;
115
+ }
116
+
111
117
  /**
112
118
  * Push-based async iterable for streaming user messages to the SDK.
113
119
  * Keeps the query subprocess alive between messages.
@@ -177,30 +183,40 @@ export async function createChatEngine(opts: EngineOptions): Promise<ChatEngine>
177
183
 
178
184
  // Context overrides: employee > agent > job > default
179
185
  let cwd = homedir();
186
+ let contextModel: string | null | undefined;
180
187
  if (opts.employee) {
181
188
  const empPrompt = buildEmployeePrompt(opts.employee);
182
189
  if (empPrompt) systemPrompt = empPrompt;
183
190
  const emp = getEmployee(opts.employee);
191
+ contextModel = emp?.model;
184
192
  if (emp?.repo && existsSync(emp.repo)) cwd = emp.repo;
185
193
  } else if (opts.agent) {
186
194
  const agents = scanAgents();
187
195
  const agentDef = agents.find((a) => a.name === opts.agent);
188
- if (agentDef) systemPrompt = agentDef.body + "\n\n" + buildContextSuffix("chat");
196
+ if (agentDef) {
197
+ systemPrompt = agentDef.body + "\n\n" + buildContextSuffix("chat");
198
+ contextModel = agentDef.model;
199
+ }
189
200
  } else if (opts.job) {
190
201
  // Job chat: load job and use its context
191
202
  const jobData = await Job.get(opts.job);
192
203
  if (jobData) {
204
+ contextModel = jobData.model;
193
205
  // If job has an employee, use employee prompt
194
206
  if (jobData.employee) {
195
207
  const empPrompt = buildEmployeePrompt(jobData.employee);
196
208
  if (empPrompt) systemPrompt = empPrompt;
197
209
  const emp = getEmployee(jobData.employee);
210
+ if (!contextModel) contextModel = emp?.model;
198
211
  if (emp?.repo && existsSync(emp.repo)) cwd = emp.repo;
199
212
  } else if (jobData.agent) {
200
213
  // If job has an agent, use agent prompt + context
201
214
  const agents = scanAgents();
202
215
  const agentDef = agents.find((a) => a.name === jobData.agent);
203
- if (agentDef) systemPrompt = agentDef.body + "\n\n" + buildContextSuffix("chat");
216
+ if (agentDef) {
217
+ systemPrompt = agentDef.body + "\n\n" + buildContextSuffix("chat");
218
+ if (!contextModel) contextModel = agentDef.model;
219
+ }
204
220
  }
205
221
  const resolvedPrompt = resolveJobPrompt(jobData);
206
222
  const source = resolvedPrompt.source === "file" ? ` from ${resolvedPrompt.filePath}` : "";
@@ -316,6 +332,10 @@ export async function createChatEngine(opts: EngineOptions): Promise<ChatEngine>
316
332
  settingSources: ["project", "user"],
317
333
  skills: [],
318
334
  };
335
+ const model = resolveSdkModel(contextModel);
336
+ if (model) {
337
+ options.model = model;
338
+ }
319
339
 
320
340
  if (sessionId) {
321
341
  options.resume = sessionId;
@@ -0,0 +1,36 @@
1
+ import { ActiveEngine } from "../db/models";
2
+ import { withDb } from "../db/with-db";
3
+ import { errMsg } from "../utils/errors";
4
+ import { dateSortValue, formatTimeLine } from "../utils/format";
5
+
6
+ function hasFullFlag(argv: string[]): boolean {
7
+ return argv.includes("--full");
8
+ }
9
+
10
+ export async function activeCommand(argv: string[] = []): Promise<void> {
11
+ const full = hasFullFlag(argv);
12
+ const now = new Date();
13
+ let engines: Awaited<ReturnType<typeof ActiveEngine.list>> = [];
14
+
15
+ try {
16
+ await withDb(async () => {
17
+ engines = await ActiveEngine.list();
18
+ });
19
+ } catch (err) {
20
+ console.error(`active engines unavailable: ${errMsg(err)}`);
21
+ process.exitCode = 1;
22
+ return;
23
+ }
24
+
25
+ if (!full) {
26
+ console.log(String(engines.length));
27
+ return;
28
+ }
29
+
30
+ console.log(`Active engines: ${engines.length === 0 ? "none" : engines.length}`);
31
+ for (const engine of engines.sort((a, b) => dateSortValue(a.startedAt) - dateSortValue(b.startedAt))) {
32
+ const started = formatTimeLine(engine.startedAt, now);
33
+ const ping = formatTimeLine(engine.lastPing, now);
34
+ console.log(` ${engine.room} (${engine.channel}) • started ${started} • last ping ${ping}`);
35
+ }
36
+ }
package/src/cli/index.ts CHANGED
@@ -11,6 +11,8 @@ import { errMsg } from "../utils/errors";
11
11
  import { fail, ICON_PASS, ICON_WARN } from "../utils/cli";
12
12
  import { jobCommand } from "./job";
13
13
  import { statusCommand } from "./status";
14
+ import { activeCommand } from "./active";
15
+ import { modelCommand } from "./model";
14
16
  import { sendCommand, telegramCommand, slackCommand } from "./channels";
15
17
  import { rulesCommand, memoryCommand } from "./self";
16
18
  import { watchCommand } from "./watch";
@@ -127,6 +129,16 @@ switch (command) {
127
129
  break;
128
130
  }
129
131
 
132
+ case "active": {
133
+ await activeCommand(process.argv.slice(3));
134
+ break;
135
+ }
136
+
137
+ case "model": {
138
+ await modelCommand(process.argv.slice(3));
139
+ break;
140
+ }
141
+
130
142
  case "health": {
131
143
  const { healthCommand } = await import("../commands/health");
132
144
  await healthCommand();
@@ -565,6 +577,8 @@ Daemon:
565
577
  restart [--wait N] [--force] Restart daemon
566
578
  update [--wait N] [--force] Update to latest version
567
579
  status [--json --rooms N --all] Show daemon, jobs, channels
580
+ active [--full] Show active engine count or details
581
+ model [name] Show or set global Claude model
568
582
  health Check daemon, db, channels, config
569
583
  logs [-f] [--channel ch] Daemon logs (filter by channel)
570
584
 
@@ -0,0 +1,29 @@
1
+ import { isRunning, readPid } from "../core/daemon";
2
+ import { getConfig, resetConfig, updateRawConfig } from "../utils/config";
3
+
4
+ function printUsage(): void {
5
+ console.log("Usage: nia model [default|sonnet|opus|opusplan|haiku|<model-id>]");
6
+ }
7
+
8
+ export async function modelCommand(argv: string[] = []): Promise<void> {
9
+ const model = argv[0];
10
+
11
+ if (!model) {
12
+ console.log(`model = ${getConfig().model}`);
13
+ return;
14
+ }
15
+
16
+ if (model === "--help" || model === "-h") {
17
+ printUsage();
18
+ return;
19
+ }
20
+
21
+ updateRawConfig({ model });
22
+ resetConfig();
23
+ console.log(`model = ${model}`);
24
+
25
+ const pid = readPid();
26
+ if (pid && isRunning()) {
27
+ process.kill(pid, "SIGHUP");
28
+ }
29
+ }