modelstat 0.1.3 → 0.3.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
@@ -137,6 +137,62 @@ var init_git = __esm({
137
137
  }
138
138
  });
139
139
 
140
+ // ../../packages/parsers/src/git-outcome.ts
141
+ import { execFile as execFile2 } from "child_process";
142
+ import { promisify as promisify2 } from "util";
143
+ function parseGitLog(stdout) {
144
+ const out = [];
145
+ for (const record of stdout.split(RS)) {
146
+ const rec = record.trim();
147
+ if (!rec) continue;
148
+ const [sha = "", committedAt = "", subject = "", ...rest] = rec.split(FS);
149
+ out.push({ sha, committedAt, subject, body: rest.join(FS) });
150
+ }
151
+ return out;
152
+ }
153
+ function findMergeCommitForPr(commits, prNumber) {
154
+ const re = new RegExp(`(^|\\D)#${prNumber}(\\D|$)`);
155
+ return commits.find((c) => re.test(c.subject)) ?? null;
156
+ }
157
+ function isReverted(commits, mergeSha) {
158
+ if (!mergeSha) return false;
159
+ const short = mergeSha.slice(0, 7);
160
+ return commits.some(
161
+ (c) => c.body.includes(`This reverts commit ${mergeSha}`) || c.body.includes(`This reverts commit ${short}`)
162
+ );
163
+ }
164
+ function outcomeFromCommits(commits, prNumber) {
165
+ const merge = findMergeCommitForPr(commits, prNumber);
166
+ if (!merge) return { merged: false, merged_at: null, reverted: false };
167
+ return {
168
+ merged: true,
169
+ merged_at: merge.committedAt || null,
170
+ reverted: isReverted(commits, merge.sha)
171
+ };
172
+ }
173
+ async function checkPullRequestOutcome(cwd, prNumber) {
174
+ try {
175
+ const { stdout } = await pexec2("git", ["log", "-n", "1000", `--format=${GIT_LOG_FORMAT}`], {
176
+ cwd,
177
+ timeout: 4e3,
178
+ maxBuffer: 16 * 1024 * 1024
179
+ });
180
+ return outcomeFromCommits(parseGitLog(stdout), prNumber);
181
+ } catch {
182
+ return null;
183
+ }
184
+ }
185
+ var pexec2, FS, RS, GIT_LOG_FORMAT;
186
+ var init_git_outcome = __esm({
187
+ "../../packages/parsers/src/git-outcome.ts"() {
188
+ "use strict";
189
+ pexec2 = promisify2(execFile2);
190
+ FS = "";
191
+ RS = "";
192
+ GIT_LOG_FORMAT = `%H${FS}%cI${FS}%s${FS}%b${RS}`;
193
+ }
194
+ });
195
+
140
196
  // ../../packages/core/src/billing.ts
141
197
  var MILLION, FREE_INCLUDED_TOKENS, TEAM_INCLUDED_PER_SEAT;
142
198
  var init_billing = __esm({
@@ -149,7 +205,7 @@ var init_billing = __esm({
149
205
  });
150
206
 
151
207
  // ../../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;
208
+ var AGENTS, PROVIDERS, IDENTITY_OWNER_SCOPES, INSTALL_METHODS, OS_FAMILIES, EVENT_KINDS, TOOL_CALL_STATUSES, DAEMON_PHASES, CLASSIFICATION_CONFIDENCE;
153
209
  var init_enums = __esm({
154
210
  "../../packages/core/src/enums.ts"() {
155
211
  "use strict";
@@ -181,7 +237,7 @@ var init_enums = __esm({
181
237
  "raw_sdk_anthropic",
182
238
  "raw_sdk_openai",
183
239
  "raw_sdk_google",
184
- // Web chat UIs (Chrome-extension companion). Categorically distinct
240
+ // Web chat UIs (Chrome-extension daemon). Categorically distinct
185
241
  // from *_cli / *_desktop agents — same provider, different surface.
186
242
  "chatgpt_web",
187
243
  "claude_web",
@@ -241,7 +297,7 @@ var init_enums = __esm({
241
297
  "timeout",
242
298
  "unknown"
243
299
  ];
244
- COMPANION_PHASES = [
300
+ DAEMON_PHASES = [
245
301
  "starting",
246
302
  "discovering",
247
303
  "idle",
@@ -4540,7 +4596,7 @@ var init_redact_floor = __esm({
4540
4596
  pattern: /(?:sk|pk|rk)_test_[A-Za-z0-9]{24,}/g,
4541
4597
  replacement: "<REDACTED:stripe_test_key>"
4542
4598
  },
4543
- // Discord bot token (was agent-sdk-only — the canonical drift example).
4599
+ // Discord bot token (was caught by only one redactor — the canonical drift example).
4544
4600
  {
4545
4601
  name: "discord_token",
4546
4602
  pattern: /[MN][A-Za-z\d]{23}\.[\w-]{6}\.[\w-]{27}/g,
@@ -4938,7 +4994,16 @@ var init_session_metadata = __esm({
4938
4994
  number: external_exports.number().int().positive(),
4939
4995
  url: external_exports.string().max(400).nullable().default(null),
4940
4996
  source: RefSource.default("content"),
4941
- confidence: external_exports.number().min(0).max(1).default(0.9)
4997
+ confidence: external_exports.number().min(0).max(1).default(0.9),
4998
+ // On-device verified-outcome signals — filled by the parsers' local git-check
4999
+ // (`checkPullRequestOutcome`) when the PR's repo is on disk. `null` = unknown;
5000
+ // the server's CPVO engine classifies verified/failed from these. Mirrors
5001
+ // core's `PullRequestRef` field-for-field.
5002
+ merged: external_exports.boolean().nullable().optional(),
5003
+ merged_at: external_exports.string().max(40).nullable().optional(),
5004
+ reverted: external_exports.boolean().nullable().optional(),
5005
+ hotfixed: external_exports.boolean().nullable().optional(),
5006
+ reopened: external_exports.boolean().nullable().optional()
4942
5007
  });
4943
5008
  CommitRef = external_exports.object({
4944
5009
  /** 7–40 char hex SHA. */
@@ -5045,7 +5110,7 @@ var init_schemas = __esm({
5045
5110
  // available, the on-device Privacy Filter adapter.
5046
5111
  // 3. Stripping code blocks and file-path noise.
5047
5112
  // Optional — events without it fall back to metadata-only abstracts
5048
- // (the historical behaviour). The companion-core pipeline runs
5113
+ // (the historical behaviour). The daemon-core pipeline runs
5049
5114
  // redact() over it again as defence-in-depth before building the
5050
5115
  // summarize prompt; it never gets stored long-term server-side, only
5051
5116
  // used to construct the summarize input.
@@ -5076,7 +5141,7 @@ var init_schemas = __esm({
5076
5141
  root_key: external_exports.string().max(60),
5077
5142
  name: external_exports.string().max(120),
5078
5143
  confidence: external_exports.number().min(0).max(1).default(0.7),
5079
- /** Optional free-text reason the companion attached this tag — surfaces
5144
+ /** Optional free-text reason the daemon attached this tag — surfaces
5080
5145
  * in the audit log so the user can see "why was this tagged X?" */
5081
5146
  reason: external_exports.string().max(200).optional()
5082
5147
  });
@@ -5099,7 +5164,7 @@ var init_schemas = __esm({
5099
5164
  /** `source_event_id`s covered by this segment. Used for dedupe + replay. */
5100
5165
  source_event_ids: external_exports.array(external_exports.string()).max(2e3),
5101
5166
  /** Optional embedding of the abstract (BGE-small-en-v1.5, 384 dims).
5102
- * Present when the companion has an Embedder adapter configured. */
5167
+ * Present when the daemon has an Embedder adapter configured. */
5103
5168
  abstract_embedding: external_exports.array(external_exports.number()).length(384).optional()
5104
5169
  });
5105
5170
  ToolAction = external_exports.object({
@@ -5115,7 +5180,7 @@ var init_schemas = __esm({
5115
5180
  qualifiers: external_exports.array(external_exports.string().max(40)).max(8).default([]),
5116
5181
  /** Value-masked argument skeleton (every value → `§`). Carried in full up
5117
5182
  * to a malicious-size guard (mirrors backend `MAX_TOOL_ACTION_PARAM_SHAPE_CHARS`);
5118
- * the companion clamps rather than truncating semantically. (tier 1) */
5183
+ * the daemon clamps rather than truncating semantically. (tier 1) */
5119
5184
  param_shape: external_exports.string().max(16384).nullable().default(null),
5120
5185
  /** Relevant non-sensitive keywords (e.g. ["rollout","restart","prod"]),
5121
5186
  * OpenAI-redacted on-device. (tier 0) */
@@ -5147,7 +5212,7 @@ var init_schemas = __esm({
5147
5212
  session_id: external_exports.string().max(120),
5148
5213
  /** The RawEvent that contained the tool_use (dedupe/replay anchor). */
5149
5214
  source_event_id: external_exports.string(),
5150
- /** Segment containing source_event_id — filled by the companion at
5215
+ /** Segment containing source_event_id — filled by the daemon at
5151
5216
  * batch-build time when known, else null. */
5152
5217
  segment_id: external_exports.string().max(64).nullable().default(null),
5153
5218
  /** The agent that made the call (AGENTS enum). */
@@ -5185,7 +5250,7 @@ var init_schemas = __esm({
5185
5250
  batch_id: external_exports.string(),
5186
5251
  // ULID
5187
5252
  device_id: external_exports.string(),
5188
- companion_version: external_exports.string().max(40),
5253
+ daemon_version: external_exports.string().max(40),
5189
5254
  events: external_exports.array(RawEvent).max(1e4),
5190
5255
  segments: external_exports.array(Segment).max(2e3).default([]),
5191
5256
  /** Per-call tool invocations (additive — old agents omit it, old
@@ -5201,8 +5266,8 @@ var init_schemas = __esm({
5201
5266
  })
5202
5267
  ).optional(),
5203
5268
  /** Optional per-session titles — session_id → short redacted title
5204
- * (≤120 chars) produced by the companion's local titler from the
5205
- * session's segment abstracts. Companions recompute it from the full
5269
+ * (≤120 chars) produced by the daemon's local titler from the
5270
+ * session's segment abstracts. Daemons recompute it from the full
5206
5271
  * session view on every upload, so the latest batch always carries the
5207
5272
  * freshest title. Absent for runtimes without a titler (older agents,
5208
5273
  * no-op browser summariser). */
@@ -5211,20 +5276,20 @@ var init_schemas = __esm({
5211
5276
  * {@link SessionMetadata}: the repos, pull requests, commits, and issues the
5212
5277
  * session touched, detected on-device across git context, tool calls,
5213
5278
  * redacted content, and the local model (so it works for any provider).
5214
- * Additive — old companions omit it, old servers ignore it (the wire has no
5279
+ * Additive — old daemons omit it, old servers ignore it (the wire has no
5215
5280
  * `deny_unknown_fields`). The join layer between AI spend and shipped work. */
5216
5281
  session_metadata: external_exports.record(external_exports.string(), SessionMetadata).optional()
5217
5282
  });
5218
5283
  HeartbeatPayload = external_exports.object({
5219
5284
  device_id: external_exports.string(),
5220
- status: external_exports.enum(COMPANION_PHASES),
5285
+ status: external_exports.enum(DAEMON_PHASES),
5221
5286
  message: external_exports.string().max(240).nullable(),
5222
5287
  progress_done: external_exports.number().int().nonnegative().default(0),
5223
5288
  progress_total: external_exports.number().int().nonnegative().default(0),
5224
5289
  queue_size: external_exports.number().int().nonnegative().default(0),
5225
5290
  stats: external_exports.record(external_exports.string(), external_exports.unknown()).default({}),
5226
5291
  last_event_at: external_exports.string().datetime({ offset: true }).nullable(),
5227
- companion_version: external_exports.string().max(40)
5292
+ daemon_version: external_exports.string().max(40)
5228
5293
  });
5229
5294
  DeviceEnrollment = external_exports.object({
5230
5295
  machine_id: external_exports.string(),
@@ -5233,10 +5298,10 @@ var init_schemas = __esm({
5233
5298
  os_family: external_exports.enum(OS_FAMILIES),
5234
5299
  os_version: external_exports.string().max(60),
5235
5300
  arch: external_exports.enum(["x86_64", "arm64", "other"]),
5236
- companion_version: external_exports.string().max(40)
5301
+ daemon_version: external_exports.string().max(40)
5237
5302
  });
5238
5303
  DeviceSelfRegister = external_exports.object({
5239
- /** Agent-generated UUIDv7 — must pass shape + recent-timestamp checks. */
5304
+ /** Daemon-generated UUIDv7 — must pass shape + recent-timestamp checks. */
5240
5305
  device_uuid: external_exports.string(),
5241
5306
  /** Base64-encoded ed25519 public key, exactly 32 raw bytes. Optional
5242
5307
  * but recommended (used for sender-constrained tokens / DPoP later). */
@@ -5249,8 +5314,8 @@ var init_schemas = __esm({
5249
5314
  os_family: external_exports.enum(OS_FAMILIES).optional(),
5250
5315
  os_version: external_exports.string().max(60).optional(),
5251
5316
  arch: external_exports.enum(["x86_64", "arm64", "other"]).optional(),
5252
- companion: external_exports.string().max(80).optional(),
5253
- companion_version: external_exports.string().max(40).optional()
5317
+ daemon: external_exports.string().max(80).optional(),
5318
+ daemon_version: external_exports.string().max(40).optional()
5254
5319
  // Allow extra fields for forward-compat without breaking old agents.
5255
5320
  }).catchall(external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean()])).default({})
5256
5321
  });
@@ -8367,11 +8432,11 @@ var init_cursor = __esm({
8367
8432
  });
8368
8433
 
8369
8434
  // ../../packages/parsers/src/discovery/index.ts
8370
- import { execFile as execFile2, execSync } from "child_process";
8435
+ import { execFile as execFile3, execSync } from "child_process";
8371
8436
  import { existsSync as existsSync3, statSync } from "fs";
8372
8437
  import { homedir, platform } from "os";
8373
8438
  import { join as join2, resolve as resolve2 } from "path";
8374
- import { promisify as promisify2 } from "util";
8439
+ import { promisify as promisify3 } from "util";
8375
8440
  async function discover(options = {}) {
8376
8441
  const os2 = platform() === "darwin" ? "macos" : "linux";
8377
8442
  const skip = new Set(options.skip ?? []);
@@ -8496,7 +8561,7 @@ function classifyInstallMethod(binPath, os2) {
8496
8561
  }
8497
8562
  async function safeVersionProbe(binPath) {
8498
8563
  try {
8499
- const { stdout } = await pexec2(binPath, ["--version"], { timeout: 1500 });
8564
+ const { stdout } = await pexec3(binPath, ["--version"], { timeout: 1500 });
8500
8565
  return stdout.trim().split(/\s+/).pop() ?? null;
8501
8566
  } catch {
8502
8567
  return null;
@@ -8701,11 +8766,11 @@ function dedupeIdentities(list) {
8701
8766
  }
8702
8767
  return Array.from(seen.values());
8703
8768
  }
8704
- var pexec2, H, SOURCES;
8769
+ var pexec3, H, SOURCES;
8705
8770
  var init_discovery = __esm({
8706
8771
  "../../packages/parsers/src/discovery/index.ts"() {
8707
8772
  "use strict";
8708
- pexec2 = promisify2(execFile2);
8773
+ pexec3 = promisify3(execFile3);
8709
8774
  H = (p) => p.startsWith("~") ? p.replace("~", homedir()) : p;
8710
8775
  SOURCES = [
8711
8776
  {
@@ -8797,6 +8862,7 @@ var init_src2 = __esm({
8797
8862
  "use strict";
8798
8863
  init_types();
8799
8864
  init_git();
8865
+ init_git_outcome();
8800
8866
  init_tool_action();
8801
8867
  init_claude_code();
8802
8868
  init_codex();
@@ -21668,7 +21734,7 @@ var require_mock_interceptor = __commonJS({
21668
21734
  var require_mock_client = __commonJS({
21669
21735
  "../../node_modules/.pnpm/undici@7.25.0/node_modules/undici/lib/mock/mock-client.js"(exports, module) {
21670
21736
  "use strict";
21671
- var { promisify: promisify4 } = __require("util");
21737
+ var { promisify: promisify5 } = __require("util");
21672
21738
  var Client = require_client();
21673
21739
  var { buildMockDispatch } = require_mock_utils();
21674
21740
  var {
@@ -21716,7 +21782,7 @@ var require_mock_client = __commonJS({
21716
21782
  this[kDispatches] = [];
21717
21783
  }
21718
21784
  async [kClose]() {
21719
- await promisify4(this[kOriginalClose])();
21785
+ await promisify5(this[kOriginalClose])();
21720
21786
  this[kConnected] = 0;
21721
21787
  this[kMockAgent][Symbols.kClients].delete(this[kOrigin]);
21722
21788
  }
@@ -21929,7 +21995,7 @@ var require_mock_call_history = __commonJS({
21929
21995
  var require_mock_pool = __commonJS({
21930
21996
  "../../node_modules/.pnpm/undici@7.25.0/node_modules/undici/lib/mock/mock-pool.js"(exports, module) {
21931
21997
  "use strict";
21932
- var { promisify: promisify4 } = __require("util");
21998
+ var { promisify: promisify5 } = __require("util");
21933
21999
  var Pool = require_pool();
21934
22000
  var { buildMockDispatch } = require_mock_utils();
21935
22001
  var {
@@ -21977,7 +22043,7 @@ var require_mock_pool = __commonJS({
21977
22043
  this[kDispatches] = [];
21978
22044
  }
21979
22045
  async [kClose]() {
21980
- await promisify4(this[kOriginalClose])();
22046
+ await promisify5(this[kOriginalClose])();
21981
22047
  this[kConnected] = 0;
21982
22048
  this[kMockAgent][Symbols.kClients].delete(this[kOrigin]);
21983
22049
  }
@@ -33608,14 +33674,14 @@ ${captureLines}` : capture.stack;
33608
33674
  }
33609
33675
  });
33610
33676
 
33611
- // ../../packages/companion-core/src/config/index.ts
33677
+ // ../../packages/daemon-core/src/config/index.ts
33612
33678
  function expBackoff(attempt) {
33613
33679
  const i = Math.min(Math.max(attempt, 0), BACKOFF_MS.length - 1);
33614
33680
  return BACKOFF_MS[i];
33615
33681
  }
33616
33682
  var INGEST_BATCH_MAX_EVENTS, BACKOFF_MS, BACKSTOP_SCAN_MS;
33617
33683
  var init_config = __esm({
33618
- "../../packages/companion-core/src/config/index.ts"() {
33684
+ "../../packages/daemon-core/src/config/index.ts"() {
33619
33685
  "use strict";
33620
33686
  INGEST_BATCH_MAX_EVENTS = 1e3;
33621
33687
  BACKOFF_MS = [1e3, 2500, 5e3, 1e4, 2e4, 6e4];
@@ -33623,7 +33689,7 @@ var init_config = __esm({
33623
33689
  }
33624
33690
  });
33625
33691
 
33626
- // ../../packages/companion-core/src/logger.ts
33692
+ // ../../packages/daemon-core/src/logger.ts
33627
33693
  function defaultLevel() {
33628
33694
  const env2 = globalThis.process?.env ?? {};
33629
33695
  const raw = (env2.LOG_LEVEL ?? "").toLowerCase();
@@ -33676,7 +33742,7 @@ function describeErrorWithCause(err, depth = 4) {
33676
33742
  }
33677
33743
  var LEVEL_RANK;
33678
33744
  var init_logger = __esm({
33679
- "../../packages/companion-core/src/logger.ts"() {
33745
+ "../../packages/daemon-core/src/logger.ts"() {
33680
33746
  "use strict";
33681
33747
  LEVEL_RANK = {
33682
33748
  debug: 10,
@@ -33687,7 +33753,7 @@ var init_logger = __esm({
33687
33753
  }
33688
33754
  });
33689
33755
 
33690
- // ../../packages/companion-core/src/http/index.ts
33756
+ // ../../packages/daemon-core/src/http/index.ts
33691
33757
  function classifyStatus(status2, attempt) {
33692
33758
  if (status2 >= 200 && status2 < 300) return { type: "commit" };
33693
33759
  if (status2 === 400 || status2 === 422) return { type: "drop", reason: `http_${status2}` };
@@ -33707,7 +33773,7 @@ function sleep(ms) {
33707
33773
  }
33708
33774
  var IngestClient;
33709
33775
  var init_http = __esm({
33710
- "../../packages/companion-core/src/http/index.ts"() {
33776
+ "../../packages/daemon-core/src/http/index.ts"() {
33711
33777
  "use strict";
33712
33778
  init_config();
33713
33779
  init_logger();
@@ -34612,25 +34678,25 @@ var init_constants2 = __esm({
34612
34678
 
34613
34679
  // ../../node_modules/.pnpm/stubborn-fs@2.0.0/node_modules/stubborn-fs/dist/index.js
34614
34680
  import fs from "fs";
34615
- import { promisify as promisify3 } from "util";
34616
- var FS, dist_default;
34681
+ import { promisify as promisify4 } from "util";
34682
+ var FS2, dist_default;
34617
34683
  var init_dist2 = __esm({
34618
34684
  "../../node_modules/.pnpm/stubborn-fs@2.0.0/node_modules/stubborn-fs/dist/index.js"() {
34619
34685
  "use strict";
34620
34686
  init_dist();
34621
34687
  init_dist();
34622
34688
  init_constants2();
34623
- FS = {
34689
+ FS2 = {
34624
34690
  attempt: {
34625
34691
  /* ASYNC */
34626
- chmod: attemptify_async_default(promisify3(fs.chmod), ATTEMPTIFY_CHANGE_ERROR_OPTIONS),
34627
- chown: attemptify_async_default(promisify3(fs.chown), ATTEMPTIFY_CHANGE_ERROR_OPTIONS),
34628
- close: attemptify_async_default(promisify3(fs.close), ATTEMPTIFY_NOOP_OPTIONS),
34629
- fsync: attemptify_async_default(promisify3(fs.fsync), ATTEMPTIFY_NOOP_OPTIONS),
34630
- mkdir: attemptify_async_default(promisify3(fs.mkdir), ATTEMPTIFY_NOOP_OPTIONS),
34631
- realpath: attemptify_async_default(promisify3(fs.realpath), ATTEMPTIFY_NOOP_OPTIONS),
34632
- stat: attemptify_async_default(promisify3(fs.stat), ATTEMPTIFY_NOOP_OPTIONS),
34633
- unlink: attemptify_async_default(promisify3(fs.unlink), ATTEMPTIFY_NOOP_OPTIONS),
34692
+ chmod: attemptify_async_default(promisify4(fs.chmod), ATTEMPTIFY_CHANGE_ERROR_OPTIONS),
34693
+ chown: attemptify_async_default(promisify4(fs.chown), ATTEMPTIFY_CHANGE_ERROR_OPTIONS),
34694
+ close: attemptify_async_default(promisify4(fs.close), ATTEMPTIFY_NOOP_OPTIONS),
34695
+ fsync: attemptify_async_default(promisify4(fs.fsync), ATTEMPTIFY_NOOP_OPTIONS),
34696
+ mkdir: attemptify_async_default(promisify4(fs.mkdir), ATTEMPTIFY_NOOP_OPTIONS),
34697
+ realpath: attemptify_async_default(promisify4(fs.realpath), ATTEMPTIFY_NOOP_OPTIONS),
34698
+ stat: attemptify_async_default(promisify4(fs.stat), ATTEMPTIFY_NOOP_OPTIONS),
34699
+ unlink: attemptify_async_default(promisify4(fs.unlink), ATTEMPTIFY_NOOP_OPTIONS),
34634
34700
  /* SYNC */
34635
34701
  chmodSync: attemptify_sync_default(fs.chmodSync, ATTEMPTIFY_CHANGE_ERROR_OPTIONS),
34636
34702
  chownSync: attemptify_sync_default(fs.chownSync, ATTEMPTIFY_CHANGE_ERROR_OPTIONS),
@@ -34644,14 +34710,14 @@ var init_dist2 = __esm({
34644
34710
  },
34645
34711
  retry: {
34646
34712
  /* ASYNC */
34647
- close: retryify_async_default(promisify3(fs.close), RETRYIFY_OPTIONS),
34648
- fsync: retryify_async_default(promisify3(fs.fsync), RETRYIFY_OPTIONS),
34649
- open: retryify_async_default(promisify3(fs.open), RETRYIFY_OPTIONS),
34650
- readFile: retryify_async_default(promisify3(fs.readFile), RETRYIFY_OPTIONS),
34651
- rename: retryify_async_default(promisify3(fs.rename), RETRYIFY_OPTIONS),
34652
- stat: retryify_async_default(promisify3(fs.stat), RETRYIFY_OPTIONS),
34653
- write: retryify_async_default(promisify3(fs.write), RETRYIFY_OPTIONS),
34654
- writeFile: retryify_async_default(promisify3(fs.writeFile), RETRYIFY_OPTIONS),
34713
+ close: retryify_async_default(promisify4(fs.close), RETRYIFY_OPTIONS),
34714
+ fsync: retryify_async_default(promisify4(fs.fsync), RETRYIFY_OPTIONS),
34715
+ open: retryify_async_default(promisify4(fs.open), RETRYIFY_OPTIONS),
34716
+ readFile: retryify_async_default(promisify4(fs.readFile), RETRYIFY_OPTIONS),
34717
+ rename: retryify_async_default(promisify4(fs.rename), RETRYIFY_OPTIONS),
34718
+ stat: retryify_async_default(promisify4(fs.stat), RETRYIFY_OPTIONS),
34719
+ write: retryify_async_default(promisify4(fs.write), RETRYIFY_OPTIONS),
34720
+ writeFile: retryify_async_default(promisify4(fs.writeFile), RETRYIFY_OPTIONS),
34655
34721
  /* SYNC */
34656
34722
  closeSync: retryify_sync_default(fs.closeSync, RETRYIFY_OPTIONS),
34657
34723
  fsyncSync: retryify_sync_default(fs.fsyncSync, RETRYIFY_OPTIONS),
@@ -34663,7 +34729,7 @@ var init_dist2 = __esm({
34663
34729
  writeFileSync: retryify_sync_default(fs.writeFileSync, RETRYIFY_OPTIONS)
34664
34730
  }
34665
34731
  };
34666
- dist_default = FS;
34732
+ dist_default = FS2;
34667
34733
  }
34668
34734
  });
34669
34735
 
@@ -45249,7 +45315,7 @@ var init_config2 = __esm({
45249
45315
  DEFAULT_API_URL = "https://modelstat.ai";
45250
45316
  LEGACY_LOCALHOST_API = "http://localhost:3010";
45251
45317
  store = new Conf({
45252
- projectName: "modelstat-agent-dev",
45318
+ projectName: "modelstat-daemon",
45253
45319
  defaults: {
45254
45320
  // Intentionally empty — the apiUrl getter below computes this
45255
45321
  // from env + stored value + DEFAULT_API_URL. Keeping the stored
@@ -45281,7 +45347,7 @@ var init_config2 = __esm({
45281
45347
  * or paired pre-0.0.8) → production default. The legacy localhost
45282
45348
  * value is ignored so upgrades from 0.0.7 self-heal. */
45283
45349
  get apiUrl() {
45284
- if (process.env.AGENT_API_URL) return process.env.AGENT_API_URL;
45350
+ if (process.env.DAEMON_API_URL) return process.env.DAEMON_API_URL;
45285
45351
  const stored = store.get("apiUrl");
45286
45352
  if (stored && stored !== LEGACY_LOCALHOST_API) return stored;
45287
45353
  return DEFAULT_API_URL;
@@ -45289,6 +45355,16 @@ var init_config2 = __esm({
45289
45355
  setApiUrl(v) {
45290
45356
  store.set("apiUrl", v);
45291
45357
  },
45358
+ /** True when `apiUrl` resolves to the baked-in production default with no
45359
+ * explicit override (no `DAEMON_API_URL`, no operator-set stored URL). Used
45360
+ * to refuse silent prod self-register from CI/non-interactive environments
45361
+ * so ephemeral runners don't pile up unclaimed device rows. */
45362
+ get isProdDefaultApi() {
45363
+ if (process.env.DAEMON_API_URL) return false;
45364
+ const stored = store.get("apiUrl");
45365
+ if (stored && stored !== LEGACY_LOCALHOST_API) return false;
45366
+ return true;
45367
+ },
45292
45368
  // ── Identity: backed by ~/.modelstat/identity.json ─────────────
45293
45369
  /** Seed a fresh identity after a successful self-register. Writes
45294
45370
  * the file atomically; use `state.backupAndReset()` first if
@@ -45427,7 +45503,7 @@ async function rotateDeviceSecret(currentSecret) {
45427
45503
  }
45428
45504
  async function reportDiscovery(report) {
45429
45505
  const bearer = state.bearer;
45430
- if (!bearer) throw new Error("agent not enrolled \u2014 run `register` first");
45506
+ if (!bearer) throw new Error("daemon not enrolled \u2014 run `register` first");
45431
45507
  const res = await (0, import_undici.request)(`${state.apiUrl}/v1/devices/discovery`, {
45432
45508
  method: "POST",
45433
45509
  headers: { "content-type": "application/json", authorization: `Bearer ${bearer}` },
@@ -45472,7 +45548,7 @@ async function fetchDeviceViewLedgerByClaim(claimCode) {
45472
45548
  }
45473
45549
  function ingestClient() {
45474
45550
  if (_ingest) return _ingest;
45475
- const logger = createLogger("agent.ingest");
45551
+ const logger = createLogger("daemon.ingest");
45476
45552
  _ingest = new IngestClient({
45477
45553
  apiUrl: state.apiUrl,
45478
45554
  auth: {
@@ -45632,24 +45708,24 @@ var init_machine_key = __esm({
45632
45708
  }
45633
45709
  });
45634
45710
 
45635
- // ../../packages/companion-core/src/contracts/index.ts
45711
+ // ../../packages/daemon-core/src/contracts/index.ts
45636
45712
  var init_contracts = __esm({
45637
- "../../packages/companion-core/src/contracts/index.ts"() {
45713
+ "../../packages/daemon-core/src/contracts/index.ts"() {
45638
45714
  "use strict";
45639
45715
  init_schemas();
45640
45716
  init_enums();
45641
45717
  }
45642
45718
  });
45643
45719
 
45644
- // ../../packages/companion-core/src/ids.ts
45720
+ // ../../packages/daemon-core/src/ids.ts
45645
45721
  var init_ids2 = __esm({
45646
- "../../packages/companion-core/src/ids.ts"() {
45722
+ "../../packages/daemon-core/src/ids.ts"() {
45647
45723
  "use strict";
45648
45724
  init_ids();
45649
45725
  }
45650
45726
  });
45651
45727
 
45652
- // ../../packages/companion-core/src/queue/index.ts
45728
+ // ../../packages/daemon-core/src/queue/index.ts
45653
45729
  function attachSegmentIdsByMap(calls, segmentByEvent) {
45654
45730
  return calls.map((c) => ({
45655
45731
  ...c,
@@ -45657,14 +45733,14 @@ function attachSegmentIdsByMap(calls, segmentByEvent) {
45657
45733
  }));
45658
45734
  }
45659
45735
  var init_queue = __esm({
45660
- "../../packages/companion-core/src/queue/index.ts"() {
45736
+ "../../packages/daemon-core/src/queue/index.ts"() {
45661
45737
  "use strict";
45662
45738
  init_config();
45663
45739
  init_ids2();
45664
45740
  }
45665
45741
  });
45666
45742
 
45667
- // ../../packages/companion-core/src/pipeline/cognition.ts
45743
+ // ../../packages/daemon-core/src/pipeline/cognition.ts
45668
45744
  function buildCognitionUserPrompt(abstract) {
45669
45745
  return `Summary: "${abstract.replace(/\s+/g, " ").trim().slice(0, 480)}"
45670
45746
 
@@ -45736,7 +45812,7 @@ function extractFirstJsonObject(s) {
45736
45812
  }
45737
45813
  var COGNITION_SYSTEM_PROMPT, MAX_COGNITION_TAGS_PER_FIELD, MAX_COGNITION_TAG_CHARS, COGNITION_MAX_TOKENS, COGNITION_TEMPERATURE;
45738
45814
  var init_cognition = __esm({
45739
- "../../packages/companion-core/src/pipeline/cognition.ts"() {
45815
+ "../../packages/daemon-core/src/pipeline/cognition.ts"() {
45740
45816
  "use strict";
45741
45817
  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.';
45742
45818
  MAX_COGNITION_TAGS_PER_FIELD = 3;
@@ -45746,10 +45822,10 @@ var init_cognition = __esm({
45746
45822
  }
45747
45823
  });
45748
45824
 
45749
- // ../../packages/companion-core/src/pipeline/prompts.ts
45825
+ // ../../packages/daemon-core/src/pipeline/prompts.ts
45750
45826
  var OLLAMA_CHAT_MODEL, OLLAMA_EMBED_MODEL, SUMMARISER_SYSTEM_PROMPT, SUMMARISER_MAX_TOKENS, SUMMARISER_TEMPERATURE, QWEN_CHARS_PER_TOKEN, ABSTRACT_OUTPUT_MAX_CHARS;
45751
45827
  var init_prompts = __esm({
45752
- "../../packages/companion-core/src/pipeline/prompts.ts"() {
45828
+ "../../packages/daemon-core/src/pipeline/prompts.ts"() {
45753
45829
  "use strict";
45754
45830
  OLLAMA_CHAT_MODEL = "qwen3:4b";
45755
45831
  OLLAMA_EMBED_MODEL = "bge-small-en-v1.5";
@@ -45761,7 +45837,7 @@ var init_prompts = __esm({
45761
45837
  }
45762
45838
  });
45763
45839
 
45764
- // ../../packages/companion-core/src/pipeline/script-summary.ts
45840
+ // ../../packages/daemon-core/src/pipeline/script-summary.ts
45765
45841
  function buildScriptSummaryUserPrompt(input) {
45766
45842
  const body = input.content.slice(0, SCRIPT_SUMMARY_INPUT_MAX_CHARS);
45767
45843
  return [
@@ -45776,7 +45852,7 @@ function buildScriptSummaryUserPrompt(input) {
45776
45852
  }
45777
45853
  var SCRIPT_SUMMARY_OUTPUT_MAX_CHARS, SCRIPT_SUMMARY_TEMPERATURE, SCRIPT_SUMMARY_MAX_TOKENS, SCRIPT_SUMMARY_INPUT_MAX_CHARS, SCRIPT_SUMMARY_SYSTEM_PROMPT;
45778
45854
  var init_script_summary = __esm({
45779
- "../../packages/companion-core/src/pipeline/script-summary.ts"() {
45855
+ "../../packages/daemon-core/src/pipeline/script-summary.ts"() {
45780
45856
  "use strict";
45781
45857
  SCRIPT_SUMMARY_OUTPUT_MAX_CHARS = 200;
45782
45858
  SCRIPT_SUMMARY_TEMPERATURE = 0.2;
@@ -45793,7 +45869,7 @@ Rules:
45793
45869
  }
45794
45870
  });
45795
45871
 
45796
- // ../../packages/companion-core/src/pipeline/title.ts
45872
+ // ../../packages/daemon-core/src/pipeline/title.ts
45797
45873
  function buildTitleUserPrompt(input) {
45798
45874
  const lines = input.abstracts.map(
45799
45875
  (a, i) => ` [part ${i + 1}] ${a.replace(/\s+/g, " ").trim().slice(0, TITLER_ABSTRACT_SLICE_CHARS)}`
@@ -45864,7 +45940,7 @@ async function buildSessionTitles(segments, entitle) {
45864
45940
  }
45865
45941
  var TITLER_SYSTEM_PROMPT, TITLE_MAX_CHARS, TITLER_MAX_TOKENS, TITLER_TEMPERATURE, TITLER_MAX_ABSTRACTS, TITLER_ABSTRACT_SLICE_CHARS;
45866
45942
  var init_title = __esm({
45867
- "../../packages/companion-core/src/pipeline/title.ts"() {
45943
+ "../../packages/daemon-core/src/pipeline/title.ts"() {
45868
45944
  "use strict";
45869
45945
  init_redact();
45870
45946
  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.";
@@ -45876,7 +45952,7 @@ var init_title = __esm({
45876
45952
  }
45877
45953
  });
45878
45954
 
45879
- // ../../packages/companion-core/src/pipeline/session-metadata.ts
45955
+ // ../../packages/daemon-core/src/pipeline/session-metadata.ts
45880
45956
  function buildLinkExtractUserPrompt(abstracts) {
45881
45957
  const lines = abstracts.map((a, i) => ` [part ${i + 1}] ${a.replace(/\s+/g, " ").trim().slice(0, 240)}`).join("\n");
45882
45958
  return `Summaries of the session's parts:
@@ -45904,6 +45980,7 @@ async function buildSessionMetadata(segments, events, opts = {}) {
45904
45980
  const evs = eventsBySession.get(sessionId) ?? [];
45905
45981
  const segs = segsBySession.get(sessionId) ?? [];
45906
45982
  const parts = [];
45983
+ const slugToCwd = /* @__PURE__ */ new Map();
45907
45984
  const cwds = /* @__PURE__ */ new Set();
45908
45985
  for (const e of evs) {
45909
45986
  if (e.cwd) cwds.add(e.cwd);
@@ -45938,6 +46015,7 @@ async function buildSessionMetadata(segments, events, opts = {}) {
45938
46015
  g = null;
45939
46016
  }
45940
46017
  if (!g?.remote_slug) continue;
46018
+ slugToCwd.set(g.remote_slug.toLowerCase(), cwd);
45941
46019
  const refs = emptyDetectedRefs();
45942
46020
  refs.repos.push({
45943
46021
  host: g.remote_host ?? null,
@@ -45967,6 +46045,21 @@ async function buildSessionMetadata(segments, events, opts = {}) {
45967
46045
  }
45968
46046
  }
45969
46047
  const meta = dedupeSessionMetadata(parts);
46048
+ if (opts.checkPrOutcome && meta.pull_requests.length > 0) {
46049
+ for (const pr of meta.pull_requests) {
46050
+ const cwd = pr.slug ? slugToCwd.get(pr.slug.toLowerCase()) : void 0;
46051
+ if (!cwd) continue;
46052
+ try {
46053
+ const o = await opts.checkPrOutcome(cwd, pr.number);
46054
+ if (o) {
46055
+ pr.merged = o.merged;
46056
+ pr.merged_at = o.merged_at;
46057
+ pr.reverted = o.reverted;
46058
+ }
46059
+ } catch {
46060
+ }
46061
+ }
46062
+ }
45970
46063
  if (!isEmptySessionMetadata(meta)) out[sessionId] = meta;
45971
46064
  } catch {
45972
46065
  }
@@ -45975,7 +46068,7 @@ async function buildSessionMetadata(segments, events, opts = {}) {
45975
46068
  }
45976
46069
  var LINK_EXTRACT_SYSTEM_PROMPT, LINK_EXTRACT_MAX_TOKENS, LINK_EXTRACT_TEMPERATURE, LINK_EXTRACT_MAX_ABSTRACTS;
45977
46070
  var init_session_metadata2 = __esm({
45978
- "../../packages/companion-core/src/pipeline/session-metadata.ts"() {
46071
+ "../../packages/daemon-core/src/pipeline/session-metadata.ts"() {
45979
46072
  "use strict";
45980
46073
  init_session_metadata();
45981
46074
  init_title();
@@ -45986,7 +46079,7 @@ var init_session_metadata2 = __esm({
45986
46079
  }
45987
46080
  });
45988
46081
 
45989
- // ../../packages/companion-core/src/pipeline/index.ts
46082
+ // ../../packages/daemon-core/src/pipeline/index.ts
45990
46083
  async function buildSegmentsForSession(events, adapters2, onProgress) {
45991
46084
  if (events.length === 0) return [];
45992
46085
  const bySession = /* @__PURE__ */ new Map();
@@ -46288,7 +46381,7 @@ function inferEnvironment(branch) {
46288
46381
  }
46289
46382
  var SEGMENT_TIME_GAP_MS, SEGMENT_TOPIC_THRESHOLD, SEGMENT_MAX_TURNS, SEGMENT_MAX_DURATION_MS, SEGMENT_MAX_CONTENT_CHARS, ABSTRACT_MAX_CHARS;
46290
46383
  var init_pipeline = __esm({
46291
- "../../packages/companion-core/src/pipeline/index.ts"() {
46384
+ "../../packages/daemon-core/src/pipeline/index.ts"() {
46292
46385
  "use strict";
46293
46386
  init_ids();
46294
46387
  init_redact();
@@ -46309,9 +46402,9 @@ var init_pipeline = __esm({
46309
46402
  }
46310
46403
  });
46311
46404
 
46312
- // ../../packages/companion-core/src/index.ts
46405
+ // ../../packages/daemon-core/src/index.ts
46313
46406
  var init_src3 = __esm({
46314
- "../../packages/companion-core/src/index.ts"() {
46407
+ "../../packages/daemon-core/src/index.ts"() {
46315
46408
  "use strict";
46316
46409
  init_contracts();
46317
46410
  init_ids2();
@@ -46323,12 +46416,12 @@ var init_src3 = __esm({
46323
46416
  }
46324
46417
  });
46325
46418
 
46326
- // ../../packages/companion-core/src/node/file-queue-store.ts
46419
+ // ../../packages/daemon-core/src/node/file-queue-store.ts
46327
46420
  import { promises as fs3 } from "fs";
46328
46421
  import { dirname as dirname4 } from "path";
46329
46422
  var SENT_TTL_MS, FileQueueStore;
46330
46423
  var init_file_queue_store = __esm({
46331
- "../../packages/companion-core/src/node/file-queue-store.ts"() {
46424
+ "../../packages/daemon-core/src/node/file-queue-store.ts"() {
46332
46425
  "use strict";
46333
46426
  SENT_TTL_MS = 24 * 60 * 60 * 1e3;
46334
46427
  FileQueueStore = class {
@@ -46446,7 +46539,7 @@ var init_file_queue_store = __esm({
46446
46539
  }
46447
46540
  });
46448
46541
 
46449
- // ../../packages/companion-core/src/node/llama.ts
46542
+ // ../../packages/daemon-core/src/node/llama.ts
46450
46543
  import { existsSync as existsSync7 } from "fs";
46451
46544
  import { mkdir } from "fs/promises";
46452
46545
  import { homedir as homedir5 } from "os";
@@ -46742,7 +46835,7 @@ function llamaExtractLinks(cfg = defaultLlamaConfig()) {
46742
46835
  }
46743
46836
  var DEFAULT_LLAMA_MODEL_URL, LLAMA_MAX_TOKENS, loaded, loadPromise, inflight, llamaInstance;
46744
46837
  var init_llama = __esm({
46745
- "../../packages/companion-core/src/node/llama.ts"() {
46838
+ "../../packages/daemon-core/src/node/llama.ts"() {
46746
46839
  "use strict";
46747
46840
  init_cognition();
46748
46841
  init_prompts();
@@ -46758,7 +46851,7 @@ var init_llama = __esm({
46758
46851
  }
46759
46852
  });
46760
46853
 
46761
- // ../../packages/companion-core/src/node/ollama.ts
46854
+ // ../../packages/daemon-core/src/node/ollama.ts
46762
46855
  function defaultOllamaConfig() {
46763
46856
  const base = globalThis.process?.env ?? {};
46764
46857
  return {
@@ -46861,14 +46954,14 @@ function ollamaCognize(cfg = defaultOllamaConfig()) {
46861
46954
  };
46862
46955
  }
46863
46956
  var init_ollama = __esm({
46864
- "../../packages/companion-core/src/node/ollama.ts"() {
46957
+ "../../packages/daemon-core/src/node/ollama.ts"() {
46865
46958
  "use strict";
46866
46959
  init_prompts();
46867
46960
  init_cognition();
46868
46961
  }
46869
46962
  });
46870
46963
 
46871
- // ../../packages/companion-core/src/optional-module.ts
46964
+ // ../../packages/daemon-core/src/optional-module.ts
46872
46965
  function isMissingOptionalModuleError(err) {
46873
46966
  const code = err?.code;
46874
46967
  if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") return true;
@@ -46877,13 +46970,13 @@ function isMissingOptionalModuleError(err) {
46877
46970
  }
46878
46971
  var OPTIONAL_MODULE_MAX_LOAD_ATTEMPTS;
46879
46972
  var init_optional_module = __esm({
46880
- "../../packages/companion-core/src/optional-module.ts"() {
46973
+ "../../packages/daemon-core/src/optional-module.ts"() {
46881
46974
  "use strict";
46882
46975
  OPTIONAL_MODULE_MAX_LOAD_ATTEMPTS = 3;
46883
46976
  }
46884
46977
  });
46885
46978
 
46886
- // ../../packages/companion-core/src/node/transformersjs-embed.ts
46979
+ // ../../packages/daemon-core/src/node/transformersjs-embed.ts
46887
46980
  async function loadPipeline(model) {
46888
46981
  if (cached) return cached;
46889
46982
  if (loadFailedPermanently) return null;
@@ -46937,7 +47030,7 @@ function createTransformersJsEmbedder(model = DEFAULT_MODEL) {
46937
47030
  }
46938
47031
  var cached, loadPromise2, loadFailedPermanently, loadAttempts, warnedUnavailable, warnedInferenceError, DEFAULT_MODEL, importModule;
46939
47032
  var init_transformersjs_embed = __esm({
46940
- "../../packages/companion-core/src/node/transformersjs-embed.ts"() {
47033
+ "../../packages/daemon-core/src/node/transformersjs-embed.ts"() {
46941
47034
  "use strict";
46942
47035
  init_optional_module();
46943
47036
  cached = null;
@@ -46954,7 +47047,7 @@ var init_transformersjs_embed = __esm({
46954
47047
  }
46955
47048
  });
46956
47049
 
46957
- // ../../packages/companion-core/src/node/index.ts
47050
+ // ../../packages/daemon-core/src/node/index.ts
46958
47051
  var node_exports = {};
46959
47052
  __export(node_exports, {
46960
47053
  DEFAULT_LLAMA_MODEL_URL: () => DEFAULT_LLAMA_MODEL_URL,
@@ -46976,7 +47069,7 @@ __export(node_exports, {
46976
47069
  ollamaTokenize: () => ollamaTokenize
46977
47070
  });
46978
47071
  var init_node2 = __esm({
46979
- "../../packages/companion-core/src/node/index.ts"() {
47072
+ "../../packages/daemon-core/src/node/index.ts"() {
46980
47073
  "use strict";
46981
47074
  init_file_queue_store();
46982
47075
  init_llama();
@@ -46985,7 +47078,7 @@ var init_node2 = __esm({
46985
47078
  }
46986
47079
  });
46987
47080
 
46988
- // ../../packages/companion-core/src/redact/privacy-filter.ts
47081
+ // ../../packages/daemon-core/src/redact/privacy-filter.ts
46989
47082
  async function createPrivacyFilterRedactor(opts = {}) {
46990
47083
  const isBrowser = typeof globalThis !== "undefined" && typeof globalThis.window !== "undefined";
46991
47084
  const device = opts.device ?? (isBrowser ? "webgpu" : "cpu");
@@ -47091,7 +47184,7 @@ async function createPrivacyFilterRedactor(opts = {}) {
47091
47184
  };
47092
47185
  }
47093
47186
  var init_privacy_filter = __esm({
47094
- "../../packages/companion-core/src/redact/privacy-filter.ts"() {
47187
+ "../../packages/daemon-core/src/redact/privacy-filter.ts"() {
47095
47188
  "use strict";
47096
47189
  init_optional_module();
47097
47190
  }
@@ -47217,7 +47310,7 @@ async function getAdapters() {
47217
47310
  await import("node-llama-cpp");
47218
47311
  } catch (err) {
47219
47312
  throw new Error(
47220
- `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}`
47313
+ `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}`
47221
47314
  );
47222
47315
  }
47223
47316
  console.log("[modelstat] using bundled local summariser (Qwen3.5-4B, runs on this machine)");
@@ -47235,7 +47328,8 @@ async function buildSessionMetadata2(segments, events) {
47235
47328
  const a = await getAdapters();
47236
47329
  return buildSessionMetadata(segments, events, {
47237
47330
  resolveGit: resolveGitContext,
47238
- extractLinks: a.extractLinks
47331
+ extractLinks: a.extractLinks,
47332
+ checkPrOutcome: checkPullRequestOutcome
47239
47333
  });
47240
47334
  }
47241
47335
  async function enrichScripts(drafts, contexts = []) {
@@ -47288,7 +47382,7 @@ function withNonNullTokens(e) {
47288
47382
  }
47289
47383
  async function scanAll(cb = {}) {
47290
47384
  const deviceId = state.deviceId;
47291
- if (!deviceId) throw new Error("agent not enrolled \u2014 run `register` first");
47385
+ if (!deviceId) throw new Error("daemon not enrolled \u2014 run `register` first");
47292
47386
  const jobs = [];
47293
47387
  try {
47294
47388
  const base = join6(homedir6(), ".claude/projects");
@@ -47393,7 +47487,7 @@ async function scanAll(cb = {}) {
47393
47487
  const batch = {
47394
47488
  batch_id: batchId(),
47395
47489
  device_id: deviceId,
47396
- companion_version: AGENT_VERSION,
47490
+ daemon_version: DAEMON_VERSION,
47397
47491
  events,
47398
47492
  segments,
47399
47493
  tool_calls: attachSegmentIdsByMap(toolCallBuffer, callSegmentByEvent),
@@ -47468,7 +47562,7 @@ async function scanAll(cb = {}) {
47468
47562
  morePending
47469
47563
  };
47470
47564
  }
47471
- var AGENT_VERSION, BATCH_MAX_EVENTS, BATCH_MAX_TOOL_CALLS, BATCH_BUFFER_HARD_CAP, ZERO_TOKENS;
47565
+ var DAEMON_VERSION, BATCH_MAX_EVENTS, BATCH_MAX_TOOL_CALLS, BATCH_BUFFER_HARD_CAP, ZERO_TOKENS;
47472
47566
  var init_scan = __esm({
47473
47567
  "src/scan.ts"() {
47474
47568
  "use strict";
@@ -47479,7 +47573,7 @@ var init_scan = __esm({
47479
47573
  init_api();
47480
47574
  init_config2();
47481
47575
  init_pipeline2();
47482
- AGENT_VERSION = true ? "agent-0.1.3" : "agent-dev";
47576
+ DAEMON_VERSION = true ? "daemon-0.3.0" : "daemon-dev";
47483
47577
  BATCH_MAX_EVENTS = INGEST_BATCH_MAX_EVENTS;
47484
47578
  BATCH_MAX_TOOL_CALLS = 2e4;
47485
47579
  BATCH_BUFFER_HARD_CAP = BATCH_MAX_EVENTS * 2;
@@ -47526,7 +47620,7 @@ function readDaemonLock(lockFile = LOCK_FILE) {
47526
47620
  return {
47527
47621
  pid: obj.pid,
47528
47622
  startedAt: obj.startedAt ?? "unknown",
47529
- companionVersion: obj.companionVersion ?? "unknown",
47623
+ daemonVersion: obj.daemonVersion ?? "unknown",
47530
47624
  apiUrl: obj.apiUrl ?? "unknown"
47531
47625
  };
47532
47626
  } catch {
@@ -47568,7 +47662,7 @@ function acquireDaemonLock(opts) {
47568
47662
  const meta = {
47569
47663
  pid: process.pid,
47570
47664
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
47571
- companionVersion: opts.companionVersion,
47665
+ daemonVersion: opts.daemonVersion,
47572
47666
  apiUrl: opts.apiUrl
47573
47667
  };
47574
47668
  writeLockAtomic(meta);
@@ -47886,7 +47980,7 @@ var init_node3 = __esm({
47886
47980
  }
47887
47981
  });
47888
47982
 
47889
- // ../../packages/companion-core/src/policies/index.ts
47983
+ // ../../packages/daemon-core/src/policies/index.ts
47890
47984
  var policies_exports = {};
47891
47985
  __export(policies_exports, {
47892
47986
  createPolicyRefresher: () => createPolicyRefresher
@@ -47930,7 +48024,7 @@ function createPolicyRefresher(opts) {
47930
48024
  }
47931
48025
  var DEFAULT_REFRESH_MS, policiesKind;
47932
48026
  var init_policies2 = __esm({
47933
- "../../packages/companion-core/src/policies/index.ts"() {
48027
+ "../../packages/daemon-core/src/policies/index.ts"() {
47934
48028
  "use strict";
47935
48029
  init_src();
47936
48030
  init_src4();
@@ -49710,7 +49804,7 @@ function snapshotBody() {
49710
49804
  queue_size: status.queueSize,
49711
49805
  stats: status.stats,
49712
49806
  last_event_at: status.lastEventAt,
49713
- companion_version: AGENT_VERSION2,
49807
+ daemon_version: DAEMON_VERSION2,
49714
49808
  machine_id: machineKey()
49715
49809
  };
49716
49810
  }
@@ -49735,7 +49829,7 @@ async function sendHeartbeat() {
49735
49829
  if (!bearer || !deviceId) return;
49736
49830
  const body = { ...snapshotBody(), device_id: deviceId };
49737
49831
  try {
49738
- const res = await (0, import_undici2.request)(`${state.apiUrl}/v1/agent/heartbeat`, {
49832
+ const res = await (0, import_undici2.request)(`${state.apiUrl}/v1/daemon/heartbeat`, {
49739
49833
  method: "POST",
49740
49834
  headers: { "content-type": "application/json", authorization: `Bearer ${bearer}` },
49741
49835
  body: JSON.stringify(body)
@@ -49866,7 +49960,7 @@ async function runDaemon(opts = {}) {
49866
49960
  throw new Error("not enrolled \u2014 run `npx modelstat@latest` first");
49867
49961
  }
49868
49962
  const lock = acquireDaemonLock({
49869
- companionVersion: AGENT_VERSION2,
49963
+ daemonVersion: DAEMON_VERSION2,
49870
49964
  apiUrl: state.apiUrl,
49871
49965
  force: opts.force === true,
49872
49966
  // If a racing daemon out-renamed us for the lock (see lock.ts
@@ -49884,7 +49978,7 @@ async function runDaemon(opts = {}) {
49884
49978
  console.log(
49885
49979
  `modelstat daemon is already running \u2014 PID ${lock.owner.pid}, started ${formatAge(
49886
49980
  lock.ageSec
49887
- )} ago, agent ${lock.owner.companionVersion}.`
49981
+ )} ago, daemon ${lock.owner.daemonVersion}.`
49888
49982
  );
49889
49983
  console.log(" \u2192 to stop it: kill " + lock.owner.pid);
49890
49984
  console.log(" \u2192 to force-replace it: modelstat start --force");
@@ -49976,7 +50070,7 @@ async function runDaemon(opts = {}) {
49976
50070
  await new Promise(() => {
49977
50071
  });
49978
50072
  }
49979
- 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;
50073
+ 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;
49980
50074
  var init_daemon = __esm({
49981
50075
  "src/daemon.ts"() {
49982
50076
  "use strict";
@@ -49989,7 +50083,7 @@ var init_daemon = __esm({
49989
50083
  init_machine_key();
49990
50084
  init_scan();
49991
50085
  init_single_flight();
49992
- AGENT_VERSION2 = true ? "agent-0.1.3" : "agent-dev";
50086
+ DAEMON_VERSION2 = true ? "daemon-0.3.0" : "daemon-dev";
49993
50087
  HEARTBEAT_INTERVAL_MS = 1e4;
49994
50088
  SCAN_INTERVAL_MS = 5 * 60 * 1e3;
49995
50089
  DISCOVERY_INTERVAL_MS = 6e4;
@@ -50134,7 +50228,7 @@ import { createRequire } from "module";
50134
50228
  import { homedir as homedir7, platform as platform3, userInfo } from "os";
50135
50229
  import { dirname as dirname6, join as join7 } from "path";
50136
50230
  import { fileURLToPath as fileURLToPath2 } from "url";
50137
- var SERVICE_LABEL = "ai.modelstat.agent";
50231
+ var SERVICE_LABEL = "ai.modelstat.daemon";
50138
50232
  var SYSTEMD_UNIT = "modelstat";
50139
50233
  function home() {
50140
50234
  return homedir7();
@@ -50341,7 +50435,7 @@ function writeSystemdUnit(cliPath) {
50341
50435
  const unitPath = systemdUnitPath();
50342
50436
  mkdirSync3(dirname6(unitPath), { recursive: true });
50343
50437
  const unit = `[Unit]
50344
- Description=modelstat agent
50438
+ Description=modelstat daemon
50345
50439
  Documentation=https://modelstat.ai
50346
50440
  After=network-online.target
50347
50441
  Wants=network-online.target
@@ -50408,7 +50502,7 @@ function installService() {
50408
50502
  return { path: systemdUnitPath(), logs: logDir() };
50409
50503
  }
50410
50504
  throw new Error(
50411
- `Service installation isn't supported on ${p}. Run 'modelstat start' manually to keep the agent running.`
50505
+ `Service installation isn't supported on ${p}. Run 'modelstat start' manually to keep the daemon running.`
50412
50506
  );
50413
50507
  }
50414
50508
  function uninstallService() {
@@ -50445,7 +50539,7 @@ async function bundledTrayAppPath(progress) {
50445
50539
  const candidates = [
50446
50540
  // Pre-built .app — CI with codesigning drops one here.
50447
50541
  join7(here2, "..", "vendor", "ModelstatTray.app"),
50448
- // Local dev layout: apps/agent-dev/src/service.ts → ../../tray-mac/build/ModelstatTray.app
50542
+ // Local dev layout: apps/daemon/src/service.ts → ../../tray-mac/build/ModelstatTray.app
50449
50543
  join7(here2, "..", "..", "tray-mac", "build", "ModelstatTray.app")
50450
50544
  ];
50451
50545
  for (const c of candidates) {
@@ -50519,7 +50613,7 @@ function decideSupervision(input) {
50519
50613
  const fresh = input.statusFreshMs ?? STATUS_FRESH_MS;
50520
50614
  const grace = input.bootGraceMs ?? BOOT_GRACE_MS;
50521
50615
  if (!input.lock || !input.ownerAlive) return "spawn";
50522
- if (input.myCompanionVersion && input.lock.companionVersion !== "unknown" && input.lock.companionVersion !== input.myCompanionVersion) {
50616
+ if (input.myDaemonVersion && input.lock.daemonVersion !== "unknown" && input.lock.daemonVersion !== input.myDaemonVersion) {
50523
50617
  return "replace";
50524
50618
  }
50525
50619
  if (input.statusAgeMs !== null && input.statusAgeMs <= fresh) return "adopt";
@@ -50547,7 +50641,7 @@ function daemonHealth(opts = {}) {
50547
50641
  ownerAlive,
50548
50642
  lockAgeMs,
50549
50643
  statusAgeMs,
50550
- myCompanionVersion: opts.myCompanionVersion
50644
+ myDaemonVersion: opts.myDaemonVersion
50551
50645
  }),
50552
50646
  lock,
50553
50647
  ownerAlive,
@@ -50591,7 +50685,7 @@ function tryOpenBrowser(url) {
50591
50685
  return false;
50592
50686
  }
50593
50687
  }
50594
- var AGENT_VERSION3 = true ? "agent-0.1.3" : "agent-dev";
50688
+ var DAEMON_VERSION3 = true ? "daemon-0.3.0" : "daemon-dev";
50595
50689
  function osFamily() {
50596
50690
  const p = platform5();
50597
50691
  if (p === "darwin") return "macos";
@@ -50609,17 +50703,30 @@ function intendedDeviceUuid() {
50609
50703
  const key = salt ? `${machineKey()}:${salt}` : machineKey();
50610
50704
  return deviceUuidFromMachineKey(key);
50611
50705
  }
50706
+ function isNonInteractive() {
50707
+ return Boolean(process.env.CI) || process.stdin.isTTY !== true;
50708
+ }
50709
+ function prodRegisterOptIn() {
50710
+ const v = process.env.MODELSTAT_ALLOW_PROD_REGISTER?.trim().toLowerCase();
50711
+ return v === "1" || v === "true" || v === "yes";
50712
+ }
50612
50713
  async function cmdSelfRegister() {
50613
50714
  const deviceUuid = state.deviceUuid ?? intendedDeviceUuid();
50614
50715
  const derived = !state.deviceUuid;
50716
+ if (derived && state.isProdDefaultApi && isNonInteractive() && !prodRegisterOptIn()) {
50717
+ process.stderr.write(
50718
+ "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"
50719
+ );
50720
+ process.exit(2);
50721
+ }
50615
50722
  const mid = machineKey();
50616
50723
  const fingerprint = {
50617
50724
  hostname: hostname2(),
50618
50725
  os_family: osFamily(),
50619
50726
  os_version: release(),
50620
50727
  arch: osArch(),
50621
- companion: "modelstat-agent-dev",
50622
- companion_version: AGENT_VERSION3,
50728
+ daemon: "modelstat-daemon",
50729
+ daemon_version: DAEMON_VERSION3,
50623
50730
  // Stable, install-method-independent machine key. The server
50624
50731
  // dedupes self-register on this so the same physical machine can
50625
50732
  // never become two device rows, even if the UUID somehow differs
@@ -50845,7 +50952,7 @@ async function cmdConnect(opts) {
50845
50952
  warn(`couldn't prepare summariser model: ${e.message}`);
50846
50953
  warn("the background service will retry the download on its first scan");
50847
50954
  }
50848
- step("Installing/refreshing background service so the agent survives reboots");
50955
+ step("Installing/refreshing background service so the daemon survives reboots");
50849
50956
  let serviceOk = false;
50850
50957
  try {
50851
50958
  const svc = installService();
@@ -50859,7 +50966,7 @@ async function cmdConnect(opts) {
50859
50966
  } catch (e) {
50860
50967
  emitEvent(opts, "service_install_failed", { error: e.message });
50861
50968
  warn(`couldn't install service: ${e.message}`);
50862
- warn("the agent will not run in the background \u2014 re-run after fixing the issue");
50969
+ warn("the daemon will not run in the background \u2014 re-run after fixing the issue");
50863
50970
  }
50864
50971
  step("Detecting installed AI tools and signed-in accounts");
50865
50972
  let discovered = null;
@@ -51081,7 +51188,7 @@ async function cmdStats(args) {
51081
51188
  console.log(` ${dashboard}`);
51082
51189
  if (local) {
51083
51190
  console.log(
51084
- `local agent: ${local.status ?? "?"}${local.message ? ` \xB7 ${local.message}` : ""}`
51191
+ `local daemon: ${local.status ?? "?"}${local.message ? ` \xB7 ${local.message}` : ""}`
51085
51192
  );
51086
51193
  const stats = local.stats ?? {};
51087
51194
  for (const [k, v] of Object.entries(stats)) console.log(` ${k}: ${v}`);
@@ -51096,9 +51203,9 @@ async function cmdStats(args) {
51096
51203
  }
51097
51204
  console.log(`device: ${view.device.id}`);
51098
51205
  console.log(`host: ${view.device.hostname ?? "(unknown)"} (${view.device.os_family ?? "?"})`);
51099
- console.log(`companion: ${view.device.companion_version ?? "(unknown)"}`);
51206
+ console.log(`daemon: ${view.device.daemon_version ?? "(unknown)"}`);
51100
51207
  console.log(
51101
- `status: ${view.device.companion_status ?? "(unknown)"}${view.device.last_seen_at ? ` \xB7 last seen ${view.device.last_seen_at}` : ""}`
51208
+ `status: ${view.device.daemon_status ?? "(unknown)"}${view.device.last_seen_at ? ` \xB7 last seen ${view.device.last_seen_at}` : ""}`
51102
51209
  );
51103
51210
  console.log(
51104
51211
  `claim: ${view.status}${view.status === "unclaimed" ? ` (at ${view.claim_url})` : ""}`
@@ -51226,7 +51333,7 @@ async function main() {
51226
51333
  }
51227
51334
  case "_daemon-health": {
51228
51335
  try {
51229
- console.log(JSON.stringify(daemonHealth({ myCompanionVersion: AGENT_VERSION3 })));
51336
+ console.log(JSON.stringify(daemonHealth({ myDaemonVersion: DAEMON_VERSION3 })));
51230
51337
  } catch (e) {
51231
51338
  console.log(JSON.stringify({ decision: "spawn", error: e.message }));
51232
51339
  }