modelstat 0.1.2 → 0.2.0

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/dist/cli.mjs CHANGED
@@ -149,7 +149,7 @@ var init_billing = __esm({
149
149
  });
150
150
 
151
151
  // ../../packages/core/src/enums.ts
152
- var AGENTS, PROVIDERS, IDENTITY_OWNER_SCOPES, INSTALL_METHODS, OS_FAMILIES, EVENT_KINDS, TOOL_CALL_STATUSES, COMPANION_PHASES, CLASSIFICATION_CONFIDENCE;
152
+ var AGENTS, PROVIDERS, IDENTITY_OWNER_SCOPES, INSTALL_METHODS, OS_FAMILIES, EVENT_KINDS, TOOL_CALL_STATUSES, DAEMON_PHASES, CLASSIFICATION_CONFIDENCE;
153
153
  var init_enums = __esm({
154
154
  "../../packages/core/src/enums.ts"() {
155
155
  "use strict";
@@ -181,7 +181,7 @@ var init_enums = __esm({
181
181
  "raw_sdk_anthropic",
182
182
  "raw_sdk_openai",
183
183
  "raw_sdk_google",
184
- // Web chat UIs (Chrome-extension companion). Categorically distinct
184
+ // Web chat UIs (Chrome-extension daemon). Categorically distinct
185
185
  // from *_cli / *_desktop agents — same provider, different surface.
186
186
  "chatgpt_web",
187
187
  "claude_web",
@@ -241,7 +241,7 @@ var init_enums = __esm({
241
241
  "timeout",
242
242
  "unknown"
243
243
  ];
244
- COMPANION_PHASES = [
244
+ DAEMON_PHASES = [
245
245
  "starting",
246
246
  "discovering",
247
247
  "idle",
@@ -4540,7 +4540,7 @@ var init_redact_floor = __esm({
4540
4540
  pattern: /(?:sk|pk|rk)_test_[A-Za-z0-9]{24,}/g,
4541
4541
  replacement: "<REDACTED:stripe_test_key>"
4542
4542
  },
4543
- // Discord bot token (was agent-sdk-only — the canonical drift example).
4543
+ // Discord bot token (was daemon-sdk-only — the canonical drift example).
4544
4544
  {
4545
4545
  name: "discord_token",
4546
4546
  pattern: /[MN][A-Za-z\d]{23}\.[\w-]{6}\.[\w-]{27}/g,
@@ -4624,16 +4624,22 @@ function redact(text, repoRootAbs) {
4624
4624
  });
4625
4625
  }
4626
4626
  out = out.replace(TOKEN_CANDIDATE, (match) => {
4627
- if (/^[a-f0-9]+$/i.test(match)) return match;
4628
- if (/^[A-Z]+$/.test(match)) return match;
4629
- const hasLetter = /[A-Za-z]/.test(match);
4627
+ if (/^[a-fA-F0-9]{32,}$/.test(match)) {
4628
+ counts.secrets_found += 1;
4629
+ return "[REDACTED:hash]";
4630
+ }
4631
+ if (/^[A-Z0-9_]+$/.test(match)) return match;
4632
+ if (/=$|\+/.test(match) && entropy(match) >= 3.5) {
4633
+ counts.secrets_found += 1;
4634
+ return "[REDACTED:base64]";
4635
+ }
4630
4636
  const hasDigit = /\d/.test(match);
4631
4637
  const hasUpper = /[A-Z]/.test(match);
4632
4638
  const hasLower = /[a-z]/.test(match);
4633
- if (!(hasLetter && hasDigit && hasUpper && hasLower)) return match;
4639
+ if (!(hasDigit && hasUpper && hasLower)) return match;
4634
4640
  if (entropy(match) < 3.6) return match;
4635
4641
  counts.secrets_found += 1;
4636
- return `[REDACTED:hi-entropy]`;
4642
+ return "[REDACTED:hi-entropy]";
4637
4643
  });
4638
4644
  out = out.replace(EMAIL_PATTERN, () => {
4639
4645
  counts.emails_redacted += 1;
@@ -5039,7 +5045,7 @@ var init_schemas = __esm({
5039
5045
  // available, the on-device Privacy Filter adapter.
5040
5046
  // 3. Stripping code blocks and file-path noise.
5041
5047
  // Optional — events without it fall back to metadata-only abstracts
5042
- // (the historical behaviour). The companion-core pipeline runs
5048
+ // (the historical behaviour). The daemon-core pipeline runs
5043
5049
  // redact() over it again as defence-in-depth before building the
5044
5050
  // summarize prompt; it never gets stored long-term server-side, only
5045
5051
  // used to construct the summarize input.
@@ -5070,7 +5076,7 @@ var init_schemas = __esm({
5070
5076
  root_key: external_exports.string().max(60),
5071
5077
  name: external_exports.string().max(120),
5072
5078
  confidence: external_exports.number().min(0).max(1).default(0.7),
5073
- /** Optional free-text reason the companion attached this tag — surfaces
5079
+ /** Optional free-text reason the daemon attached this tag — surfaces
5074
5080
  * in the audit log so the user can see "why was this tagged X?" */
5075
5081
  reason: external_exports.string().max(200).optional()
5076
5082
  });
@@ -5093,7 +5099,7 @@ var init_schemas = __esm({
5093
5099
  /** `source_event_id`s covered by this segment. Used for dedupe + replay. */
5094
5100
  source_event_ids: external_exports.array(external_exports.string()).max(2e3),
5095
5101
  /** Optional embedding of the abstract (BGE-small-en-v1.5, 384 dims).
5096
- * Present when the companion has an Embedder adapter configured. */
5102
+ * Present when the daemon has an Embedder adapter configured. */
5097
5103
  abstract_embedding: external_exports.array(external_exports.number()).length(384).optional()
5098
5104
  });
5099
5105
  ToolAction = external_exports.object({
@@ -5107,8 +5113,10 @@ var init_schemas = __esm({
5107
5113
  object: external_exports.string().max(60).nullable().default(null),
5108
5114
  /** Governed safe flags (`destructive`, `remote`, …). (tier 0) */
5109
5115
  qualifiers: external_exports.array(external_exports.string().max(40)).max(8).default([]),
5110
- /** Value-masked argument skeleton (every value → `§`). (tier 1) */
5111
- param_shape: external_exports.string().max(200).nullable().default(null),
5116
+ /** Value-masked argument skeleton (every value → `§`). Carried in full up
5117
+ * to a malicious-size guard (mirrors backend `MAX_TOOL_ACTION_PARAM_SHAPE_CHARS`);
5118
+ * the daemon clamps rather than truncating semantically. (tier 1) */
5119
+ param_shape: external_exports.string().max(16384).nullable().default(null),
5112
5120
  /** Relevant non-sensitive keywords (e.g. ["rollout","restart","prod"]),
5113
5121
  * OpenAI-redacted on-device. (tier 0) */
5114
5122
  keywords: external_exports.array(external_exports.string().max(40)).max(12).default([]),
@@ -5118,7 +5126,7 @@ var init_schemas = __esm({
5118
5126
  /** The compliance-redacted command text — PII/secrets stripped on-device
5119
5127
  * (SOC2/GDPR), org-internal infra intact; the server derives semantics from
5120
5128
  * it. Un-redacted raw never ships. (tier 0, post-redaction) */
5121
- command_redacted: external_exports.string().max(1e3).nullable().default(null),
5129
+ command_redacted: external_exports.string().max(16384).nullable().default(null),
5122
5130
  /** Per-script content abstracts for any script/bash FILES the command runs
5123
5131
  * — summarized on-device by the local model, then redacted. Ordered by
5124
5132
  * appearance; `token` is the script's token exactly as it appears in
@@ -5139,7 +5147,7 @@ var init_schemas = __esm({
5139
5147
  session_id: external_exports.string().max(120),
5140
5148
  /** The RawEvent that contained the tool_use (dedupe/replay anchor). */
5141
5149
  source_event_id: external_exports.string(),
5142
- /** Segment containing source_event_id — filled by the companion at
5150
+ /** Segment containing source_event_id — filled by the daemon at
5143
5151
  * batch-build time when known, else null. */
5144
5152
  segment_id: external_exports.string().max(64).nullable().default(null),
5145
5153
  /** The agent that made the call (AGENTS enum). */
@@ -5177,7 +5185,7 @@ var init_schemas = __esm({
5177
5185
  batch_id: external_exports.string(),
5178
5186
  // ULID
5179
5187
  device_id: external_exports.string(),
5180
- companion_version: external_exports.string().max(40),
5188
+ daemon_version: external_exports.string().max(40),
5181
5189
  events: external_exports.array(RawEvent).max(1e4),
5182
5190
  segments: external_exports.array(Segment).max(2e3).default([]),
5183
5191
  /** Per-call tool invocations (additive — old agents omit it, old
@@ -5193,8 +5201,8 @@ var init_schemas = __esm({
5193
5201
  })
5194
5202
  ).optional(),
5195
5203
  /** Optional per-session titles — session_id → short redacted title
5196
- * (≤120 chars) produced by the companion's local titler from the
5197
- * session's segment abstracts. Companions recompute it from the full
5204
+ * (≤120 chars) produced by the daemon's local titler from the
5205
+ * session's segment abstracts. Daemons recompute it from the full
5198
5206
  * session view on every upload, so the latest batch always carries the
5199
5207
  * freshest title. Absent for runtimes without a titler (older agents,
5200
5208
  * no-op browser summariser). */
@@ -5203,20 +5211,20 @@ var init_schemas = __esm({
5203
5211
  * {@link SessionMetadata}: the repos, pull requests, commits, and issues the
5204
5212
  * session touched, detected on-device across git context, tool calls,
5205
5213
  * redacted content, and the local model (so it works for any provider).
5206
- * Additive — old companions omit it, old servers ignore it (the wire has no
5214
+ * Additive — old daemons omit it, old servers ignore it (the wire has no
5207
5215
  * `deny_unknown_fields`). The join layer between AI spend and shipped work. */
5208
5216
  session_metadata: external_exports.record(external_exports.string(), SessionMetadata).optional()
5209
5217
  });
5210
5218
  HeartbeatPayload = external_exports.object({
5211
5219
  device_id: external_exports.string(),
5212
- status: external_exports.enum(COMPANION_PHASES),
5220
+ status: external_exports.enum(DAEMON_PHASES),
5213
5221
  message: external_exports.string().max(240).nullable(),
5214
5222
  progress_done: external_exports.number().int().nonnegative().default(0),
5215
5223
  progress_total: external_exports.number().int().nonnegative().default(0),
5216
5224
  queue_size: external_exports.number().int().nonnegative().default(0),
5217
5225
  stats: external_exports.record(external_exports.string(), external_exports.unknown()).default({}),
5218
5226
  last_event_at: external_exports.string().datetime({ offset: true }).nullable(),
5219
- companion_version: external_exports.string().max(40)
5227
+ daemon_version: external_exports.string().max(40)
5220
5228
  });
5221
5229
  DeviceEnrollment = external_exports.object({
5222
5230
  machine_id: external_exports.string(),
@@ -5225,10 +5233,10 @@ var init_schemas = __esm({
5225
5233
  os_family: external_exports.enum(OS_FAMILIES),
5226
5234
  os_version: external_exports.string().max(60),
5227
5235
  arch: external_exports.enum(["x86_64", "arm64", "other"]),
5228
- companion_version: external_exports.string().max(40)
5236
+ daemon_version: external_exports.string().max(40)
5229
5237
  });
5230
5238
  DeviceSelfRegister = external_exports.object({
5231
- /** Agent-generated UUIDv7 — must pass shape + recent-timestamp checks. */
5239
+ /** Daemon-generated UUIDv7 — must pass shape + recent-timestamp checks. */
5232
5240
  device_uuid: external_exports.string(),
5233
5241
  /** Base64-encoded ed25519 public key, exactly 32 raw bytes. Optional
5234
5242
  * but recommended (used for sender-constrained tokens / DPoP later). */
@@ -5241,8 +5249,8 @@ var init_schemas = __esm({
5241
5249
  os_family: external_exports.enum(OS_FAMILIES).optional(),
5242
5250
  os_version: external_exports.string().max(60).optional(),
5243
5251
  arch: external_exports.enum(["x86_64", "arm64", "other"]).optional(),
5244
- companion: external_exports.string().max(80).optional(),
5245
- companion_version: external_exports.string().max(40).optional()
5252
+ daemon: external_exports.string().max(80).optional(),
5253
+ daemon_version: external_exports.string().max(40).optional()
5246
5254
  // Allow extra fields for forward-compat without breaking old agents.
5247
5255
  }).catchall(external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean()])).default({})
5248
5256
  });
@@ -5377,6 +5385,10 @@ var init_scripts = __esm({
5377
5385
  });
5378
5386
 
5379
5387
  // ../../packages/parsers/src/tool-action/index.ts
5388
+ function clampChars(s, max) {
5389
+ const cps = [...s];
5390
+ return cps.length > max ? cps.slice(0, max).join("") : s;
5391
+ }
5380
5392
  function extractToolAction(call) {
5381
5393
  const isMcp = call.server.startsWith("mcp:");
5382
5394
  const command = isMcp ? null : shellCommandOf(call.input);
@@ -5387,8 +5399,8 @@ function extractToolAction(call) {
5387
5399
  if (command != null) {
5388
5400
  const [head = "", ...rest] = command.trim().split(/\s+/);
5389
5401
  executable = basename(head) || null;
5390
- param_shape = paramShape(rest.join(" ")) || null;
5391
- command_redacted = redact(command, call.cwd ?? void 0).text.slice(0, MAX_COMMAND_REDACTED) || null;
5402
+ param_shape = clampChars(paramShape(rest.join(" ")), MAX_FIELD_CHARS) || null;
5403
+ command_redacted = clampChars(redact(command, call.cwd ?? void 0).text, MAX_FIELD_CHARS) || null;
5392
5404
  }
5393
5405
  return {
5394
5406
  surface,
@@ -5426,13 +5438,13 @@ function shellCommandOf(input) {
5426
5438
  function basename(token) {
5427
5439
  return token.split("/").pop() ?? token;
5428
5440
  }
5429
- var MAX_COMMAND_REDACTED;
5441
+ var MAX_FIELD_CHARS;
5430
5442
  var init_tool_action = __esm({
5431
5443
  "../../packages/parsers/src/tool-action/index.ts"() {
5432
5444
  "use strict";
5433
5445
  init_src();
5434
5446
  init_scripts();
5435
- MAX_COMMAND_REDACTED = 1e3;
5447
+ MAX_FIELD_CHARS = 16384;
5436
5448
  }
5437
5449
  });
5438
5450
 
@@ -8538,6 +8550,33 @@ async function probeIdentities(os2) {
8538
8550
  } catch {
8539
8551
  }
8540
8552
  }
8553
+ const claudeConfigs = [`${homedir()}/.claude.json`];
8554
+ if (process.env.CLAUDE_CONFIG_DIR) {
8555
+ claudeConfigs.unshift(`${process.env.CLAUDE_CONFIG_DIR}/.claude.json`);
8556
+ }
8557
+ for (const candidate of claudeConfigs) {
8558
+ if (!existsSync3(candidate)) continue;
8559
+ try {
8560
+ const data = await fs4.promises.readFile(candidate, "utf8");
8561
+ const obj = JSON.parse(data);
8562
+ const acct = obj.oauthAccount;
8563
+ const stableId = acct?.accountUuid ?? acct?.organizationUuid;
8564
+ if (acct && stableId) {
8565
+ ids.push({
8566
+ provider: "anthropic",
8567
+ provider_account_id: stableId,
8568
+ provider_account_label: acct.emailAddress ?? acct.organizationName ?? acct.displayName ?? "Claude account",
8569
+ account_email: acct.emailAddress ?? null,
8570
+ account_org: acct.organizationName ?? acct.billingType ?? null,
8571
+ display_name: acct.displayName ?? null,
8572
+ owner_scope: "unassigned",
8573
+ detection_source: "claude_json_oauth"
8574
+ });
8575
+ break;
8576
+ }
8577
+ } catch {
8578
+ }
8579
+ }
8541
8580
  for (const candidate of [
8542
8581
  `${homedir()}/.codex/auth.json`,
8543
8582
  `${homedir()}/.config/codex/auth.json`
@@ -33569,14 +33608,14 @@ ${captureLines}` : capture.stack;
33569
33608
  }
33570
33609
  });
33571
33610
 
33572
- // ../../packages/companion-core/src/config/index.ts
33611
+ // ../../packages/daemon-core/src/config/index.ts
33573
33612
  function expBackoff(attempt) {
33574
33613
  const i = Math.min(Math.max(attempt, 0), BACKOFF_MS.length - 1);
33575
33614
  return BACKOFF_MS[i];
33576
33615
  }
33577
33616
  var INGEST_BATCH_MAX_EVENTS, BACKOFF_MS, BACKSTOP_SCAN_MS;
33578
33617
  var init_config = __esm({
33579
- "../../packages/companion-core/src/config/index.ts"() {
33618
+ "../../packages/daemon-core/src/config/index.ts"() {
33580
33619
  "use strict";
33581
33620
  INGEST_BATCH_MAX_EVENTS = 1e3;
33582
33621
  BACKOFF_MS = [1e3, 2500, 5e3, 1e4, 2e4, 6e4];
@@ -33584,7 +33623,7 @@ var init_config = __esm({
33584
33623
  }
33585
33624
  });
33586
33625
 
33587
- // ../../packages/companion-core/src/logger.ts
33626
+ // ../../packages/daemon-core/src/logger.ts
33588
33627
  function defaultLevel() {
33589
33628
  const env2 = globalThis.process?.env ?? {};
33590
33629
  const raw = (env2.LOG_LEVEL ?? "").toLowerCase();
@@ -33637,7 +33676,7 @@ function describeErrorWithCause(err, depth = 4) {
33637
33676
  }
33638
33677
  var LEVEL_RANK;
33639
33678
  var init_logger = __esm({
33640
- "../../packages/companion-core/src/logger.ts"() {
33679
+ "../../packages/daemon-core/src/logger.ts"() {
33641
33680
  "use strict";
33642
33681
  LEVEL_RANK = {
33643
33682
  debug: 10,
@@ -33648,7 +33687,7 @@ var init_logger = __esm({
33648
33687
  }
33649
33688
  });
33650
33689
 
33651
- // ../../packages/companion-core/src/http/index.ts
33690
+ // ../../packages/daemon-core/src/http/index.ts
33652
33691
  function classifyStatus(status2, attempt) {
33653
33692
  if (status2 >= 200 && status2 < 300) return { type: "commit" };
33654
33693
  if (status2 === 400 || status2 === 422) return { type: "drop", reason: `http_${status2}` };
@@ -33668,7 +33707,7 @@ function sleep(ms) {
33668
33707
  }
33669
33708
  var IngestClient;
33670
33709
  var init_http = __esm({
33671
- "../../packages/companion-core/src/http/index.ts"() {
33710
+ "../../packages/daemon-core/src/http/index.ts"() {
33672
33711
  "use strict";
33673
33712
  init_config();
33674
33713
  init_logger();
@@ -45210,7 +45249,7 @@ var init_config2 = __esm({
45210
45249
  DEFAULT_API_URL = "https://modelstat.ai";
45211
45250
  LEGACY_LOCALHOST_API = "http://localhost:3010";
45212
45251
  store = new Conf({
45213
- projectName: "modelstat-agent-dev",
45252
+ projectName: "modelstat-daemon",
45214
45253
  defaults: {
45215
45254
  // Intentionally empty — the apiUrl getter below computes this
45216
45255
  // from env + stored value + DEFAULT_API_URL. Keeping the stored
@@ -45242,7 +45281,7 @@ var init_config2 = __esm({
45242
45281
  * or paired pre-0.0.8) → production default. The legacy localhost
45243
45282
  * value is ignored so upgrades from 0.0.7 self-heal. */
45244
45283
  get apiUrl() {
45245
- if (process.env.AGENT_API_URL) return process.env.AGENT_API_URL;
45284
+ if (process.env.DAEMON_API_URL) return process.env.DAEMON_API_URL;
45246
45285
  const stored = store.get("apiUrl");
45247
45286
  if (stored && stored !== LEGACY_LOCALHOST_API) return stored;
45248
45287
  return DEFAULT_API_URL;
@@ -45250,6 +45289,16 @@ var init_config2 = __esm({
45250
45289
  setApiUrl(v) {
45251
45290
  store.set("apiUrl", v);
45252
45291
  },
45292
+ /** True when `apiUrl` resolves to the baked-in production default with no
45293
+ * explicit override (no `DAEMON_API_URL`, no operator-set stored URL). Used
45294
+ * to refuse silent prod self-register from CI/non-interactive environments
45295
+ * so ephemeral runners don't pile up unclaimed device rows. */
45296
+ get isProdDefaultApi() {
45297
+ if (process.env.DAEMON_API_URL) return false;
45298
+ const stored = store.get("apiUrl");
45299
+ if (stored && stored !== LEGACY_LOCALHOST_API) return false;
45300
+ return true;
45301
+ },
45253
45302
  // ── Identity: backed by ~/.modelstat/identity.json ─────────────
45254
45303
  /** Seed a fresh identity after a successful self-register. Writes
45255
45304
  * the file atomically; use `state.backupAndReset()` first if
@@ -45388,7 +45437,7 @@ async function rotateDeviceSecret(currentSecret) {
45388
45437
  }
45389
45438
  async function reportDiscovery(report) {
45390
45439
  const bearer = state.bearer;
45391
- if (!bearer) throw new Error("agent not enrolled \u2014 run `register` first");
45440
+ if (!bearer) throw new Error("daemon not enrolled \u2014 run `register` first");
45392
45441
  const res = await (0, import_undici.request)(`${state.apiUrl}/v1/devices/discovery`, {
45393
45442
  method: "POST",
45394
45443
  headers: { "content-type": "application/json", authorization: `Bearer ${bearer}` },
@@ -45433,7 +45482,7 @@ async function fetchDeviceViewLedgerByClaim(claimCode) {
45433
45482
  }
45434
45483
  function ingestClient() {
45435
45484
  if (_ingest) return _ingest;
45436
- const logger = createLogger("agent.ingest");
45485
+ const logger = createLogger("daemon.ingest");
45437
45486
  _ingest = new IngestClient({
45438
45487
  apiUrl: state.apiUrl,
45439
45488
  auth: {
@@ -45593,24 +45642,24 @@ var init_machine_key = __esm({
45593
45642
  }
45594
45643
  });
45595
45644
 
45596
- // ../../packages/companion-core/src/contracts/index.ts
45645
+ // ../../packages/daemon-core/src/contracts/index.ts
45597
45646
  var init_contracts = __esm({
45598
- "../../packages/companion-core/src/contracts/index.ts"() {
45647
+ "../../packages/daemon-core/src/contracts/index.ts"() {
45599
45648
  "use strict";
45600
45649
  init_schemas();
45601
45650
  init_enums();
45602
45651
  }
45603
45652
  });
45604
45653
 
45605
- // ../../packages/companion-core/src/ids.ts
45654
+ // ../../packages/daemon-core/src/ids.ts
45606
45655
  var init_ids2 = __esm({
45607
- "../../packages/companion-core/src/ids.ts"() {
45656
+ "../../packages/daemon-core/src/ids.ts"() {
45608
45657
  "use strict";
45609
45658
  init_ids();
45610
45659
  }
45611
45660
  });
45612
45661
 
45613
- // ../../packages/companion-core/src/queue/index.ts
45662
+ // ../../packages/daemon-core/src/queue/index.ts
45614
45663
  function attachSegmentIdsByMap(calls, segmentByEvent) {
45615
45664
  return calls.map((c) => ({
45616
45665
  ...c,
@@ -45618,14 +45667,14 @@ function attachSegmentIdsByMap(calls, segmentByEvent) {
45618
45667
  }));
45619
45668
  }
45620
45669
  var init_queue = __esm({
45621
- "../../packages/companion-core/src/queue/index.ts"() {
45670
+ "../../packages/daemon-core/src/queue/index.ts"() {
45622
45671
  "use strict";
45623
45672
  init_config();
45624
45673
  init_ids2();
45625
45674
  }
45626
45675
  });
45627
45676
 
45628
- // ../../packages/companion-core/src/pipeline/cognition.ts
45677
+ // ../../packages/daemon-core/src/pipeline/cognition.ts
45629
45678
  function buildCognitionUserPrompt(abstract) {
45630
45679
  return `Summary: "${abstract.replace(/\s+/g, " ").trim().slice(0, 480)}"
45631
45680
 
@@ -45697,7 +45746,7 @@ function extractFirstJsonObject(s) {
45697
45746
  }
45698
45747
  var COGNITION_SYSTEM_PROMPT, MAX_COGNITION_TAGS_PER_FIELD, MAX_COGNITION_TAG_CHARS, COGNITION_MAX_TOKENS, COGNITION_TEMPERATURE;
45699
45748
  var init_cognition = __esm({
45700
- "../../packages/companion-core/src/pipeline/cognition.ts"() {
45749
+ "../../packages/daemon-core/src/pipeline/cognition.ts"() {
45701
45750
  "use strict";
45702
45751
  COGNITION_SYSTEM_PROMPT = 'You read a one-sentence summary of an AI-coding work session and identify the user\'s emotional state and meta-cognitive state. Output JSON only \u2014 first character of reply is `{`. Schema: {"emotions":[],"meta":[]}. emotions: \u2264 3 short lowercase MOOD tags \u2014 how the user FEELS \u2014 such as frustrated, curious, excited, calm, confused, anxious, satisfied, proud, bored, energised, overwhelmed, confident. meta: \u2264 3 short lowercase MENTAL-MODE tags \u2014 HOW the user is THINKING, never what they are doing. Valid examples: focused, scattered, in-flow, deliberate, hurried, stuck, open, exploratory, methodical, distracted. DO NOT emit ACTIVITY verbs (debugging, refactoring, designing, reviewing, deploying, planning, documenting, implementing) under meta \u2014 those describe the WORK, not the MIND. If the only candidate tag would be an activity verb, return [] for meta instead. Each tag \u2264 24 chars, single word or hyphenated, no punctuation. Only emit a tag if the summary gives clear evidence \u2014 return [] for either field when unsure. Do not invent emotions or mental modes the user did not display. No prose, no markdown.';
45703
45752
  MAX_COGNITION_TAGS_PER_FIELD = 3;
@@ -45707,10 +45756,10 @@ var init_cognition = __esm({
45707
45756
  }
45708
45757
  });
45709
45758
 
45710
- // ../../packages/companion-core/src/pipeline/prompts.ts
45759
+ // ../../packages/daemon-core/src/pipeline/prompts.ts
45711
45760
  var OLLAMA_CHAT_MODEL, OLLAMA_EMBED_MODEL, SUMMARISER_SYSTEM_PROMPT, SUMMARISER_MAX_TOKENS, SUMMARISER_TEMPERATURE, QWEN_CHARS_PER_TOKEN, ABSTRACT_OUTPUT_MAX_CHARS;
45712
45761
  var init_prompts = __esm({
45713
- "../../packages/companion-core/src/pipeline/prompts.ts"() {
45762
+ "../../packages/daemon-core/src/pipeline/prompts.ts"() {
45714
45763
  "use strict";
45715
45764
  OLLAMA_CHAT_MODEL = "qwen3:4b";
45716
45765
  OLLAMA_EMBED_MODEL = "bge-small-en-v1.5";
@@ -45722,7 +45771,7 @@ var init_prompts = __esm({
45722
45771
  }
45723
45772
  });
45724
45773
 
45725
- // ../../packages/companion-core/src/pipeline/script-summary.ts
45774
+ // ../../packages/daemon-core/src/pipeline/script-summary.ts
45726
45775
  function buildScriptSummaryUserPrompt(input) {
45727
45776
  const body = input.content.slice(0, SCRIPT_SUMMARY_INPUT_MAX_CHARS);
45728
45777
  return [
@@ -45737,7 +45786,7 @@ function buildScriptSummaryUserPrompt(input) {
45737
45786
  }
45738
45787
  var SCRIPT_SUMMARY_OUTPUT_MAX_CHARS, SCRIPT_SUMMARY_TEMPERATURE, SCRIPT_SUMMARY_MAX_TOKENS, SCRIPT_SUMMARY_INPUT_MAX_CHARS, SCRIPT_SUMMARY_SYSTEM_PROMPT;
45739
45788
  var init_script_summary = __esm({
45740
- "../../packages/companion-core/src/pipeline/script-summary.ts"() {
45789
+ "../../packages/daemon-core/src/pipeline/script-summary.ts"() {
45741
45790
  "use strict";
45742
45791
  SCRIPT_SUMMARY_OUTPUT_MAX_CHARS = 200;
45743
45792
  SCRIPT_SUMMARY_TEMPERATURE = 0.2;
@@ -45754,7 +45803,7 @@ Rules:
45754
45803
  }
45755
45804
  });
45756
45805
 
45757
- // ../../packages/companion-core/src/pipeline/title.ts
45806
+ // ../../packages/daemon-core/src/pipeline/title.ts
45758
45807
  function buildTitleUserPrompt(input) {
45759
45808
  const lines = input.abstracts.map(
45760
45809
  (a, i) => ` [part ${i + 1}] ${a.replace(/\s+/g, " ").trim().slice(0, TITLER_ABSTRACT_SLICE_CHARS)}`
@@ -45825,7 +45874,7 @@ async function buildSessionTitles(segments, entitle) {
45825
45874
  }
45826
45875
  var TITLER_SYSTEM_PROMPT, TITLE_MAX_CHARS, TITLER_MAX_TOKENS, TITLER_TEMPERATURE, TITLER_MAX_ABSTRACTS, TITLER_ABSTRACT_SLICE_CHARS;
45827
45876
  var init_title = __esm({
45828
- "../../packages/companion-core/src/pipeline/title.ts"() {
45877
+ "../../packages/daemon-core/src/pipeline/title.ts"() {
45829
45878
  "use strict";
45830
45879
  init_redact();
45831
45880
  TITLER_SYSTEM_PROMPT = "You write a short TITLE for an AI coding session, given one-sentence summaries of its parts in chronological order. The title names what the session was about at a high level, in 3-8 words. If the session clearly spans more than one theme, name the top themes (at most 3) separated by ' \xB7 '. Use concrete domain keywords (features, components, subsystems). No narration, no filler words like 'session' or 'work on', no quotes, no trailing period, no PII, no code literals, no file paths. Reply with only the title.";
@@ -45837,7 +45886,7 @@ var init_title = __esm({
45837
45886
  }
45838
45887
  });
45839
45888
 
45840
- // ../../packages/companion-core/src/pipeline/session-metadata.ts
45889
+ // ../../packages/daemon-core/src/pipeline/session-metadata.ts
45841
45890
  function buildLinkExtractUserPrompt(abstracts) {
45842
45891
  const lines = abstracts.map((a, i) => ` [part ${i + 1}] ${a.replace(/\s+/g, " ").trim().slice(0, 240)}`).join("\n");
45843
45892
  return `Summaries of the session's parts:
@@ -45936,7 +45985,7 @@ async function buildSessionMetadata(segments, events, opts = {}) {
45936
45985
  }
45937
45986
  var LINK_EXTRACT_SYSTEM_PROMPT, LINK_EXTRACT_MAX_TOKENS, LINK_EXTRACT_TEMPERATURE, LINK_EXTRACT_MAX_ABSTRACTS;
45938
45987
  var init_session_metadata2 = __esm({
45939
- "../../packages/companion-core/src/pipeline/session-metadata.ts"() {
45988
+ "../../packages/daemon-core/src/pipeline/session-metadata.ts"() {
45940
45989
  "use strict";
45941
45990
  init_session_metadata();
45942
45991
  init_title();
@@ -45947,7 +45996,7 @@ var init_session_metadata2 = __esm({
45947
45996
  }
45948
45997
  });
45949
45998
 
45950
- // ../../packages/companion-core/src/pipeline/index.ts
45999
+ // ../../packages/daemon-core/src/pipeline/index.ts
45951
46000
  async function buildSegmentsForSession(events, adapters2, onProgress) {
45952
46001
  if (events.length === 0) return [];
45953
46002
  const bySession = /* @__PURE__ */ new Map();
@@ -46249,7 +46298,7 @@ function inferEnvironment(branch) {
46249
46298
  }
46250
46299
  var SEGMENT_TIME_GAP_MS, SEGMENT_TOPIC_THRESHOLD, SEGMENT_MAX_TURNS, SEGMENT_MAX_DURATION_MS, SEGMENT_MAX_CONTENT_CHARS, ABSTRACT_MAX_CHARS;
46251
46300
  var init_pipeline = __esm({
46252
- "../../packages/companion-core/src/pipeline/index.ts"() {
46301
+ "../../packages/daemon-core/src/pipeline/index.ts"() {
46253
46302
  "use strict";
46254
46303
  init_ids();
46255
46304
  init_redact();
@@ -46270,9 +46319,9 @@ var init_pipeline = __esm({
46270
46319
  }
46271
46320
  });
46272
46321
 
46273
- // ../../packages/companion-core/src/index.ts
46322
+ // ../../packages/daemon-core/src/index.ts
46274
46323
  var init_src3 = __esm({
46275
- "../../packages/companion-core/src/index.ts"() {
46324
+ "../../packages/daemon-core/src/index.ts"() {
46276
46325
  "use strict";
46277
46326
  init_contracts();
46278
46327
  init_ids2();
@@ -46284,12 +46333,12 @@ var init_src3 = __esm({
46284
46333
  }
46285
46334
  });
46286
46335
 
46287
- // ../../packages/companion-core/src/node/file-queue-store.ts
46336
+ // ../../packages/daemon-core/src/node/file-queue-store.ts
46288
46337
  import { promises as fs3 } from "fs";
46289
46338
  import { dirname as dirname4 } from "path";
46290
46339
  var SENT_TTL_MS, FileQueueStore;
46291
46340
  var init_file_queue_store = __esm({
46292
- "../../packages/companion-core/src/node/file-queue-store.ts"() {
46341
+ "../../packages/daemon-core/src/node/file-queue-store.ts"() {
46293
46342
  "use strict";
46294
46343
  SENT_TTL_MS = 24 * 60 * 60 * 1e3;
46295
46344
  FileQueueStore = class {
@@ -46407,7 +46456,7 @@ var init_file_queue_store = __esm({
46407
46456
  }
46408
46457
  });
46409
46458
 
46410
- // ../../packages/companion-core/src/node/llama.ts
46459
+ // ../../packages/daemon-core/src/node/llama.ts
46411
46460
  import { existsSync as existsSync7 } from "fs";
46412
46461
  import { mkdir } from "fs/promises";
46413
46462
  import { homedir as homedir5 } from "os";
@@ -46703,7 +46752,7 @@ function llamaExtractLinks(cfg = defaultLlamaConfig()) {
46703
46752
  }
46704
46753
  var DEFAULT_LLAMA_MODEL_URL, LLAMA_MAX_TOKENS, loaded, loadPromise, inflight, llamaInstance;
46705
46754
  var init_llama = __esm({
46706
- "../../packages/companion-core/src/node/llama.ts"() {
46755
+ "../../packages/daemon-core/src/node/llama.ts"() {
46707
46756
  "use strict";
46708
46757
  init_cognition();
46709
46758
  init_prompts();
@@ -46719,7 +46768,7 @@ var init_llama = __esm({
46719
46768
  }
46720
46769
  });
46721
46770
 
46722
- // ../../packages/companion-core/src/node/ollama.ts
46771
+ // ../../packages/daemon-core/src/node/ollama.ts
46723
46772
  function defaultOllamaConfig() {
46724
46773
  const base = globalThis.process?.env ?? {};
46725
46774
  return {
@@ -46822,14 +46871,14 @@ function ollamaCognize(cfg = defaultOllamaConfig()) {
46822
46871
  };
46823
46872
  }
46824
46873
  var init_ollama = __esm({
46825
- "../../packages/companion-core/src/node/ollama.ts"() {
46874
+ "../../packages/daemon-core/src/node/ollama.ts"() {
46826
46875
  "use strict";
46827
46876
  init_prompts();
46828
46877
  init_cognition();
46829
46878
  }
46830
46879
  });
46831
46880
 
46832
- // ../../packages/companion-core/src/optional-module.ts
46881
+ // ../../packages/daemon-core/src/optional-module.ts
46833
46882
  function isMissingOptionalModuleError(err) {
46834
46883
  const code = err?.code;
46835
46884
  if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") return true;
@@ -46838,13 +46887,13 @@ function isMissingOptionalModuleError(err) {
46838
46887
  }
46839
46888
  var OPTIONAL_MODULE_MAX_LOAD_ATTEMPTS;
46840
46889
  var init_optional_module = __esm({
46841
- "../../packages/companion-core/src/optional-module.ts"() {
46890
+ "../../packages/daemon-core/src/optional-module.ts"() {
46842
46891
  "use strict";
46843
46892
  OPTIONAL_MODULE_MAX_LOAD_ATTEMPTS = 3;
46844
46893
  }
46845
46894
  });
46846
46895
 
46847
- // ../../packages/companion-core/src/node/transformersjs-embed.ts
46896
+ // ../../packages/daemon-core/src/node/transformersjs-embed.ts
46848
46897
  async function loadPipeline(model) {
46849
46898
  if (cached) return cached;
46850
46899
  if (loadFailedPermanently) return null;
@@ -46898,7 +46947,7 @@ function createTransformersJsEmbedder(model = DEFAULT_MODEL) {
46898
46947
  }
46899
46948
  var cached, loadPromise2, loadFailedPermanently, loadAttempts, warnedUnavailable, warnedInferenceError, DEFAULT_MODEL, importModule;
46900
46949
  var init_transformersjs_embed = __esm({
46901
- "../../packages/companion-core/src/node/transformersjs-embed.ts"() {
46950
+ "../../packages/daemon-core/src/node/transformersjs-embed.ts"() {
46902
46951
  "use strict";
46903
46952
  init_optional_module();
46904
46953
  cached = null;
@@ -46915,7 +46964,7 @@ var init_transformersjs_embed = __esm({
46915
46964
  }
46916
46965
  });
46917
46966
 
46918
- // ../../packages/companion-core/src/node/index.ts
46967
+ // ../../packages/daemon-core/src/node/index.ts
46919
46968
  var node_exports = {};
46920
46969
  __export(node_exports, {
46921
46970
  DEFAULT_LLAMA_MODEL_URL: () => DEFAULT_LLAMA_MODEL_URL,
@@ -46937,7 +46986,7 @@ __export(node_exports, {
46937
46986
  ollamaTokenize: () => ollamaTokenize
46938
46987
  });
46939
46988
  var init_node2 = __esm({
46940
- "../../packages/companion-core/src/node/index.ts"() {
46989
+ "../../packages/daemon-core/src/node/index.ts"() {
46941
46990
  "use strict";
46942
46991
  init_file_queue_store();
46943
46992
  init_llama();
@@ -46946,7 +46995,7 @@ var init_node2 = __esm({
46946
46995
  }
46947
46996
  });
46948
46997
 
46949
- // ../../packages/companion-core/src/redact/privacy-filter.ts
46998
+ // ../../packages/daemon-core/src/redact/privacy-filter.ts
46950
46999
  async function createPrivacyFilterRedactor(opts = {}) {
46951
47000
  const isBrowser = typeof globalThis !== "undefined" && typeof globalThis.window !== "undefined";
46952
47001
  const device = opts.device ?? (isBrowser ? "webgpu" : "cpu");
@@ -47052,7 +47101,7 @@ async function createPrivacyFilterRedactor(opts = {}) {
47052
47101
  };
47053
47102
  }
47054
47103
  var init_privacy_filter = __esm({
47055
- "../../packages/companion-core/src/redact/privacy-filter.ts"() {
47104
+ "../../packages/daemon-core/src/redact/privacy-filter.ts"() {
47056
47105
  "use strict";
47057
47106
  init_optional_module();
47058
47107
  }
@@ -47178,7 +47227,7 @@ async function getAdapters() {
47178
47227
  await import("node-llama-cpp");
47179
47228
  } catch (err) {
47180
47229
  throw new Error(
47181
- `modelstat agent can't start: the bundled summariser (node-llama-cpp) failed to load. Re-run \`modelstat connect\` (or \`npm i -g modelstat\`) so the native runtime is re-staged beside the bundle. Underlying error: ${err.message}`
47230
+ `modelstat daemon can't start: the bundled summariser (node-llama-cpp) failed to load. Re-run \`modelstat connect\` (or \`npm i -g modelstat\`) so the native runtime is re-staged beside the bundle. Underlying error: ${err.message}`
47182
47231
  );
47183
47232
  }
47184
47233
  console.log("[modelstat] using bundled local summariser (Qwen3.5-4B, runs on this machine)");
@@ -47249,7 +47298,7 @@ function withNonNullTokens(e) {
47249
47298
  }
47250
47299
  async function scanAll(cb = {}) {
47251
47300
  const deviceId = state.deviceId;
47252
- if (!deviceId) throw new Error("agent not enrolled \u2014 run `register` first");
47301
+ if (!deviceId) throw new Error("daemon not enrolled \u2014 run `register` first");
47253
47302
  const jobs = [];
47254
47303
  try {
47255
47304
  const base = join6(homedir6(), ".claude/projects");
@@ -47354,7 +47403,7 @@ async function scanAll(cb = {}) {
47354
47403
  const batch = {
47355
47404
  batch_id: batchId(),
47356
47405
  device_id: deviceId,
47357
- companion_version: AGENT_VERSION,
47406
+ daemon_version: DAEMON_VERSION,
47358
47407
  events,
47359
47408
  segments,
47360
47409
  tool_calls: attachSegmentIdsByMap(toolCallBuffer, callSegmentByEvent),
@@ -47429,7 +47478,7 @@ async function scanAll(cb = {}) {
47429
47478
  morePending
47430
47479
  };
47431
47480
  }
47432
- var AGENT_VERSION, BATCH_MAX_EVENTS, BATCH_MAX_TOOL_CALLS, BATCH_BUFFER_HARD_CAP, ZERO_TOKENS;
47481
+ var DAEMON_VERSION, BATCH_MAX_EVENTS, BATCH_MAX_TOOL_CALLS, BATCH_BUFFER_HARD_CAP, ZERO_TOKENS;
47433
47482
  var init_scan = __esm({
47434
47483
  "src/scan.ts"() {
47435
47484
  "use strict";
@@ -47440,7 +47489,7 @@ var init_scan = __esm({
47440
47489
  init_api();
47441
47490
  init_config2();
47442
47491
  init_pipeline2();
47443
- AGENT_VERSION = true ? "agent-0.1.2" : "agent-dev";
47492
+ DAEMON_VERSION = true ? "daemon-0.2.0" : "daemon-dev";
47444
47493
  BATCH_MAX_EVENTS = INGEST_BATCH_MAX_EVENTS;
47445
47494
  BATCH_MAX_TOOL_CALLS = 2e4;
47446
47495
  BATCH_BUFFER_HARD_CAP = BATCH_MAX_EVENTS * 2;
@@ -47487,7 +47536,7 @@ function readDaemonLock(lockFile = LOCK_FILE) {
47487
47536
  return {
47488
47537
  pid: obj.pid,
47489
47538
  startedAt: obj.startedAt ?? "unknown",
47490
- companionVersion: obj.companionVersion ?? "unknown",
47539
+ daemonVersion: obj.daemonVersion ?? "unknown",
47491
47540
  apiUrl: obj.apiUrl ?? "unknown"
47492
47541
  };
47493
47542
  } catch {
@@ -47529,7 +47578,7 @@ function acquireDaemonLock(opts) {
47529
47578
  const meta = {
47530
47579
  pid: process.pid,
47531
47580
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
47532
- companionVersion: opts.companionVersion,
47581
+ daemonVersion: opts.daemonVersion,
47533
47582
  apiUrl: opts.apiUrl
47534
47583
  };
47535
47584
  writeLockAtomic(meta);
@@ -47847,7 +47896,7 @@ var init_node3 = __esm({
47847
47896
  }
47848
47897
  });
47849
47898
 
47850
- // ../../packages/companion-core/src/policies/index.ts
47899
+ // ../../packages/daemon-core/src/policies/index.ts
47851
47900
  var policies_exports = {};
47852
47901
  __export(policies_exports, {
47853
47902
  createPolicyRefresher: () => createPolicyRefresher
@@ -47891,7 +47940,7 @@ function createPolicyRefresher(opts) {
47891
47940
  }
47892
47941
  var DEFAULT_REFRESH_MS, policiesKind;
47893
47942
  var init_policies2 = __esm({
47894
- "../../packages/companion-core/src/policies/index.ts"() {
47943
+ "../../packages/daemon-core/src/policies/index.ts"() {
47895
47944
  "use strict";
47896
47945
  init_src();
47897
47946
  init_src4();
@@ -49671,7 +49720,7 @@ function snapshotBody() {
49671
49720
  queue_size: status.queueSize,
49672
49721
  stats: status.stats,
49673
49722
  last_event_at: status.lastEventAt,
49674
- companion_version: AGENT_VERSION2,
49723
+ daemon_version: DAEMON_VERSION2,
49675
49724
  machine_id: machineKey()
49676
49725
  };
49677
49726
  }
@@ -49696,7 +49745,7 @@ async function sendHeartbeat() {
49696
49745
  if (!bearer || !deviceId) return;
49697
49746
  const body = { ...snapshotBody(), device_id: deviceId };
49698
49747
  try {
49699
- const res = await (0, import_undici2.request)(`${state.apiUrl}/v1/agent/heartbeat`, {
49748
+ const res = await (0, import_undici2.request)(`${state.apiUrl}/v1/daemon/heartbeat`, {
49700
49749
  method: "POST",
49701
49750
  headers: { "content-type": "application/json", authorization: `Bearer ${bearer}` },
49702
49751
  body: JSON.stringify(body)
@@ -49827,7 +49876,7 @@ async function runDaemon(opts = {}) {
49827
49876
  throw new Error("not enrolled \u2014 run `npx modelstat@latest` first");
49828
49877
  }
49829
49878
  const lock = acquireDaemonLock({
49830
- companionVersion: AGENT_VERSION2,
49879
+ daemonVersion: DAEMON_VERSION2,
49831
49880
  apiUrl: state.apiUrl,
49832
49881
  force: opts.force === true,
49833
49882
  // If a racing daemon out-renamed us for the lock (see lock.ts
@@ -49845,7 +49894,7 @@ async function runDaemon(opts = {}) {
49845
49894
  console.log(
49846
49895
  `modelstat daemon is already running \u2014 PID ${lock.owner.pid}, started ${formatAge(
49847
49896
  lock.ageSec
49848
- )} ago, agent ${lock.owner.companionVersion}.`
49897
+ )} ago, daemon ${lock.owner.daemonVersion}.`
49849
49898
  );
49850
49899
  console.log(" \u2192 to stop it: kill " + lock.owner.pid);
49851
49900
  console.log(" \u2192 to force-replace it: modelstat start --force");
@@ -49937,7 +49986,7 @@ async function runDaemon(opts = {}) {
49937
49986
  await new Promise(() => {
49938
49987
  });
49939
49988
  }
49940
- var import_undici2, AGENT_VERSION2, HEARTBEAT_INTERVAL_MS, SCAN_INTERVAL_MS, DISCOVERY_INTERVAL_MS, status, LOCAL_FLUSH_THROTTLE_MS, localFlushTimer, localFlushPending, LOG_MAX_BYTES, LOG_TAIL_KEEP_BYTES, lastStatusPath, scanRunner;
49989
+ var import_undici2, DAEMON_VERSION2, HEARTBEAT_INTERVAL_MS, SCAN_INTERVAL_MS, DISCOVERY_INTERVAL_MS, status, LOCAL_FLUSH_THROTTLE_MS, localFlushTimer, localFlushPending, LOG_MAX_BYTES, LOG_TAIL_KEEP_BYTES, lastStatusPath, scanRunner;
49941
49990
  var init_daemon = __esm({
49942
49991
  "src/daemon.ts"() {
49943
49992
  "use strict";
@@ -49950,7 +49999,7 @@ var init_daemon = __esm({
49950
49999
  init_machine_key();
49951
50000
  init_scan();
49952
50001
  init_single_flight();
49953
- AGENT_VERSION2 = true ? "agent-0.1.2" : "agent-dev";
50002
+ DAEMON_VERSION2 = true ? "daemon-0.2.0" : "daemon-dev";
49954
50003
  HEARTBEAT_INTERVAL_MS = 1e4;
49955
50004
  SCAN_INTERVAL_MS = 5 * 60 * 1e3;
49956
50005
  DISCOVERY_INTERVAL_MS = 6e4;
@@ -50082,6 +50131,7 @@ import { createInterface as createInterface3 } from "readline";
50082
50131
  // src/service.ts
50083
50132
  import { spawn, spawnSync as spawnSync2 } from "child_process";
50084
50133
  import {
50134
+ chmodSync as chmodSync3,
50085
50135
  copyFileSync,
50086
50136
  existsSync as existsSync9,
50087
50137
  mkdirSync as mkdirSync3,
@@ -50094,7 +50144,7 @@ import { createRequire } from "module";
50094
50144
  import { homedir as homedir7, platform as platform3, userInfo } from "os";
50095
50145
  import { dirname as dirname6, join as join7 } from "path";
50096
50146
  import { fileURLToPath as fileURLToPath2 } from "url";
50097
- var SERVICE_LABEL = "ai.modelstat.agent";
50147
+ var SERVICE_LABEL = "ai.modelstat.daemon";
50098
50148
  var SYSTEMD_UNIT = "modelstat";
50099
50149
  function home() {
50100
50150
  return homedir7();
@@ -50301,7 +50351,7 @@ function writeSystemdUnit(cliPath) {
50301
50351
  const unitPath = systemdUnitPath();
50302
50352
  mkdirSync3(dirname6(unitPath), { recursive: true });
50303
50353
  const unit = `[Unit]
50304
- Description=modelstat agent
50354
+ Description=modelstat daemon
50305
50355
  Documentation=https://modelstat.ai
50306
50356
  After=network-online.target
50307
50357
  Wants=network-online.target
@@ -50368,7 +50418,7 @@ function installService() {
50368
50418
  return { path: systemdUnitPath(), logs: logDir() };
50369
50419
  }
50370
50420
  throw new Error(
50371
- `Service installation isn't supported on ${p}. Run 'modelstat start' manually to keep the agent running.`
50421
+ `Service installation isn't supported on ${p}. Run 'modelstat start' manually to keep the daemon running.`
50372
50422
  );
50373
50423
  }
50374
50424
  function uninstallService() {
@@ -50396,6 +50446,7 @@ function installTrayApp(sourceAppPath) {
50396
50446
  if (r.status !== 0) {
50397
50447
  throw new Error(`cp ModelstatTray.app failed: ${r.stderr?.trim() || `exit ${r.status}`}`);
50398
50448
  }
50449
+ chmodSync3(join7(dest, "Contents", "MacOS", "modelstat-tray"), 493);
50399
50450
  return { installedAt: dest };
50400
50451
  }
50401
50452
  async function bundledTrayAppPath(progress) {
@@ -50404,7 +50455,7 @@ async function bundledTrayAppPath(progress) {
50404
50455
  const candidates = [
50405
50456
  // Pre-built .app — CI with codesigning drops one here.
50406
50457
  join7(here2, "..", "vendor", "ModelstatTray.app"),
50407
- // Local dev layout: apps/agent-dev/src/service.ts → ../../tray-mac/build/ModelstatTray.app
50458
+ // Local dev layout: apps/daemon/src/service.ts → ../../tray-mac/build/ModelstatTray.app
50408
50459
  join7(here2, "..", "..", "tray-mac", "build", "ModelstatTray.app")
50409
50460
  ];
50410
50461
  for (const c of candidates) {
@@ -50478,7 +50529,7 @@ function decideSupervision(input) {
50478
50529
  const fresh = input.statusFreshMs ?? STATUS_FRESH_MS;
50479
50530
  const grace = input.bootGraceMs ?? BOOT_GRACE_MS;
50480
50531
  if (!input.lock || !input.ownerAlive) return "spawn";
50481
- if (input.myCompanionVersion && input.lock.companionVersion !== "unknown" && input.lock.companionVersion !== input.myCompanionVersion) {
50532
+ if (input.myDaemonVersion && input.lock.daemonVersion !== "unknown" && input.lock.daemonVersion !== input.myDaemonVersion) {
50482
50533
  return "replace";
50483
50534
  }
50484
50535
  if (input.statusAgeMs !== null && input.statusAgeMs <= fresh) return "adopt";
@@ -50506,7 +50557,7 @@ function daemonHealth(opts = {}) {
50506
50557
  ownerAlive,
50507
50558
  lockAgeMs,
50508
50559
  statusAgeMs,
50509
- myCompanionVersion: opts.myCompanionVersion
50560
+ myDaemonVersion: opts.myDaemonVersion
50510
50561
  }),
50511
50562
  lock,
50512
50563
  ownerAlive,
@@ -50550,7 +50601,7 @@ function tryOpenBrowser(url) {
50550
50601
  return false;
50551
50602
  }
50552
50603
  }
50553
- var AGENT_VERSION3 = true ? "agent-0.1.2" : "agent-dev";
50604
+ var DAEMON_VERSION3 = true ? "daemon-0.2.0" : "daemon-dev";
50554
50605
  function osFamily() {
50555
50606
  const p = platform5();
50556
50607
  if (p === "darwin") return "macos";
@@ -50568,17 +50619,30 @@ function intendedDeviceUuid() {
50568
50619
  const key = salt ? `${machineKey()}:${salt}` : machineKey();
50569
50620
  return deviceUuidFromMachineKey(key);
50570
50621
  }
50622
+ function isNonInteractive() {
50623
+ return Boolean(process.env.CI) || process.stdin.isTTY !== true;
50624
+ }
50625
+ function prodRegisterOptIn() {
50626
+ const v = process.env.MODELSTAT_ALLOW_PROD_REGISTER?.trim().toLowerCase();
50627
+ return v === "1" || v === "true" || v === "yes";
50628
+ }
50571
50629
  async function cmdSelfRegister() {
50572
50630
  const deviceUuid = state.deviceUuid ?? intendedDeviceUuid();
50573
50631
  const derived = !state.deviceUuid;
50632
+ if (derived && state.isProdDefaultApi && isNonInteractive() && !prodRegisterOptIn()) {
50633
+ process.stderr.write(
50634
+ "modelstat: refusing to self-register a new device against production from a\nnon-interactive/CI environment (no claim is possible here anyway). Either:\n \u2022 point at your own backend: DAEMON_API_URL=https://your-host (CI/e2e)\n \u2022 explicitly opt in: MODELSTAT_ALLOW_PROD_REGISTER=1\n \u2022 or run it interactively: npx modelstat@latest\n"
50635
+ );
50636
+ process.exit(2);
50637
+ }
50574
50638
  const mid = machineKey();
50575
50639
  const fingerprint = {
50576
50640
  hostname: hostname2(),
50577
50641
  os_family: osFamily(),
50578
50642
  os_version: release(),
50579
50643
  arch: osArch(),
50580
- companion: "modelstat-agent-dev",
50581
- companion_version: AGENT_VERSION3,
50644
+ daemon: "modelstat-daemon",
50645
+ daemon_version: DAEMON_VERSION3,
50582
50646
  // Stable, install-method-independent machine key. The server
50583
50647
  // dedupes self-register on this so the same physical machine can
50584
50648
  // never become two device rows, even if the UUID somehow differs
@@ -50804,7 +50868,7 @@ async function cmdConnect(opts) {
50804
50868
  warn(`couldn't prepare summariser model: ${e.message}`);
50805
50869
  warn("the background service will retry the download on its first scan");
50806
50870
  }
50807
- step("Installing/refreshing background service so the agent survives reboots");
50871
+ step("Installing/refreshing background service so the daemon survives reboots");
50808
50872
  let serviceOk = false;
50809
50873
  try {
50810
50874
  const svc = installService();
@@ -50818,7 +50882,7 @@ async function cmdConnect(opts) {
50818
50882
  } catch (e) {
50819
50883
  emitEvent(opts, "service_install_failed", { error: e.message });
50820
50884
  warn(`couldn't install service: ${e.message}`);
50821
- warn("the agent will not run in the background \u2014 re-run after fixing the issue");
50885
+ warn("the daemon will not run in the background \u2014 re-run after fixing the issue");
50822
50886
  }
50823
50887
  step("Detecting installed AI tools and signed-in accounts");
50824
50888
  let discovered = null;
@@ -51040,7 +51104,7 @@ async function cmdStats(args) {
51040
51104
  console.log(` ${dashboard}`);
51041
51105
  if (local) {
51042
51106
  console.log(
51043
- `local agent: ${local.status ?? "?"}${local.message ? ` \xB7 ${local.message}` : ""}`
51107
+ `local daemon: ${local.status ?? "?"}${local.message ? ` \xB7 ${local.message}` : ""}`
51044
51108
  );
51045
51109
  const stats = local.stats ?? {};
51046
51110
  for (const [k, v] of Object.entries(stats)) console.log(` ${k}: ${v}`);
@@ -51055,9 +51119,9 @@ async function cmdStats(args) {
51055
51119
  }
51056
51120
  console.log(`device: ${view.device.id}`);
51057
51121
  console.log(`host: ${view.device.hostname ?? "(unknown)"} (${view.device.os_family ?? "?"})`);
51058
- console.log(`companion: ${view.device.companion_version ?? "(unknown)"}`);
51122
+ console.log(`daemon: ${view.device.daemon_version ?? "(unknown)"}`);
51059
51123
  console.log(
51060
- `status: ${view.device.companion_status ?? "(unknown)"}${view.device.last_seen_at ? ` \xB7 last seen ${view.device.last_seen_at}` : ""}`
51124
+ `status: ${view.device.daemon_status ?? "(unknown)"}${view.device.last_seen_at ? ` \xB7 last seen ${view.device.last_seen_at}` : ""}`
51061
51125
  );
51062
51126
  console.log(
51063
51127
  `claim: ${view.status}${view.status === "unclaimed" ? ` (at ${view.claim_url})` : ""}`
@@ -51185,7 +51249,7 @@ async function main() {
51185
51249
  }
51186
51250
  case "_daemon-health": {
51187
51251
  try {
51188
- console.log(JSON.stringify(daemonHealth({ myCompanionVersion: AGENT_VERSION3 })));
51252
+ console.log(JSON.stringify(daemonHealth({ myDaemonVersion: DAEMON_VERSION3 })));
51189
51253
  } catch (e) {
51190
51254
  console.log(JSON.stringify({ decision: "spawn", error: e.message }));
51191
51255
  }