@agentmonitors/cli 0.4.0 → 0.6.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/index.cjs CHANGED
@@ -1218,7 +1218,7 @@ var require_command = __commonJS({
1218
1218
  init_cjs_shims();
1219
1219
  var EventEmitter2 = require("events").EventEmitter;
1220
1220
  var childProcess = require("child_process");
1221
- var path12 = require("path");
1221
+ var path13 = require("path");
1222
1222
  var fs = require("fs");
1223
1223
  var process3 = require("process");
1224
1224
  var { Argument: Argument2, humanReadableArgName } = require_argument();
@@ -2231,9 +2231,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
2231
2231
  let launchWithNode = false;
2232
2232
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
2233
2233
  function findFile(baseDir, baseName) {
2234
- const localBin = path12.resolve(baseDir, baseName);
2234
+ const localBin = path13.resolve(baseDir, baseName);
2235
2235
  if (fs.existsSync(localBin)) return localBin;
2236
- if (sourceExt.includes(path12.extname(baseName))) return void 0;
2236
+ if (sourceExt.includes(path13.extname(baseName))) return void 0;
2237
2237
  const foundExt = sourceExt.find(
2238
2238
  (ext2) => fs.existsSync(`${localBin}${ext2}`)
2239
2239
  );
@@ -2251,17 +2251,17 @@ Expecting one of '${allowedValues.join("', '")}'`);
2251
2251
  } catch {
2252
2252
  resolvedScriptPath = this._scriptPath;
2253
2253
  }
2254
- executableDir = path12.resolve(
2255
- path12.dirname(resolvedScriptPath),
2254
+ executableDir = path13.resolve(
2255
+ path13.dirname(resolvedScriptPath),
2256
2256
  executableDir
2257
2257
  );
2258
2258
  }
2259
2259
  if (executableDir) {
2260
2260
  let localFile = findFile(executableDir, executableFile);
2261
2261
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
2262
- const legacyName = path12.basename(
2262
+ const legacyName = path13.basename(
2263
2263
  this._scriptPath,
2264
- path12.extname(this._scriptPath)
2264
+ path13.extname(this._scriptPath)
2265
2265
  );
2266
2266
  if (legacyName !== this._name) {
2267
2267
  localFile = findFile(
@@ -2272,7 +2272,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2272
2272
  }
2273
2273
  executableFile = localFile || executableFile;
2274
2274
  }
2275
- launchWithNode = sourceExt.includes(path12.extname(executableFile));
2275
+ launchWithNode = sourceExt.includes(path13.extname(executableFile));
2276
2276
  let proc2;
2277
2277
  if (process3.platform !== "win32") {
2278
2278
  if (launchWithNode) {
@@ -3187,7 +3187,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
3187
3187
  * @return {Command}
3188
3188
  */
3189
3189
  nameFromFilename(filename) {
3190
- this._name = path12.basename(filename, path12.extname(filename));
3190
+ this._name = path13.basename(filename, path13.extname(filename));
3191
3191
  return this;
3192
3192
  }
3193
3193
  /**
@@ -3201,9 +3201,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
3201
3201
  * @param {string} [path]
3202
3202
  * @return {(string|null|Command)}
3203
3203
  */
3204
- executableDir(path13) {
3205
- if (path13 === void 0) return this._executableDir;
3206
- this._executableDir = path13;
3204
+ executableDir(path14) {
3205
+ if (path14 === void 0) return this._executableDir;
3206
+ this._executableDir = path14;
3207
3207
  return this;
3208
3208
  }
3209
3209
  /**
@@ -10226,8 +10226,8 @@ var require_utils2 = __commonJS({
10226
10226
  }
10227
10227
  return ind;
10228
10228
  }
10229
- function removeDotSegments(path12) {
10230
- let input = path12;
10229
+ function removeDotSegments(path13) {
10230
+ let input = path13;
10231
10231
  const output = [];
10232
10232
  let nextSlash = -1;
10233
10233
  let len = 0;
@@ -10427,8 +10427,8 @@ var require_schemes = __commonJS({
10427
10427
  wsComponent.secure = void 0;
10428
10428
  }
10429
10429
  if (wsComponent.resourceName) {
10430
- const [path12, query] = wsComponent.resourceName.split("?");
10431
- wsComponent.path = path12 && path12 !== "/" ? path12 : void 0;
10430
+ const [path13, query] = wsComponent.resourceName.split("?");
10431
+ wsComponent.path = path13 && path13 !== "/" ? path13 : void 0;
10432
10432
  wsComponent.query = query;
10433
10433
  wsComponent.resourceName = void 0;
10434
10434
  }
@@ -13851,8 +13851,8 @@ var require_dist = __commonJS({
13851
13851
 
13852
13852
  // src/index.ts
13853
13853
  init_cjs_shims();
13854
- var import_node_fs6 = require("fs");
13855
- var import_node_path9 = require("path");
13854
+ var import_node_fs7 = require("fs");
13855
+ var import_node_path10 = require("path");
13856
13856
  var import_node_url4 = require("url");
13857
13857
 
13858
13858
  // ../../node_modules/.pnpm/commander@14.0.3/node_modules/commander/esm.mjs
@@ -13971,6 +13971,7 @@ Edit the file to configure your monitor, then run:`);
13971
13971
 
13972
13972
  // src/commands/validate.ts
13973
13973
  init_cjs_shims();
13974
+ var import_node_fs3 = require("fs");
13974
13975
 
13975
13976
  // ../../libs/core/dist/index.js
13976
13977
  init_cjs_shims();
@@ -14468,8 +14469,8 @@ function getErrorMap() {
14468
14469
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
14469
14470
  init_cjs_shims();
14470
14471
  var makeIssue = (params) => {
14471
- const { data, path: path12, errorMaps, issueData } = params;
14472
- const fullPath = [...path12, ...issueData.path || []];
14472
+ const { data, path: path13, errorMaps, issueData } = params;
14473
+ const fullPath = [...path13, ...issueData.path || []];
14473
14474
  const fullIssue = {
14474
14475
  ...issueData,
14475
14476
  path: fullPath
@@ -14589,11 +14590,11 @@ var errorUtil;
14589
14590
 
14590
14591
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
14591
14592
  var ParseInputLazyPath = class {
14592
- constructor(parent, value, path12, key) {
14593
+ constructor(parent, value, path13, key) {
14593
14594
  this._cachedPath = [];
14594
14595
  this.parent = parent;
14595
14596
  this.data = value;
14596
- this._path = path12;
14597
+ this._path = path13;
14597
14598
  this._key = key;
14598
14599
  }
14599
14600
  get path() {
@@ -22659,12 +22660,12 @@ var PathBase = class {
22659
22660
  /**
22660
22661
  * Get the Path object referenced by the string path, resolved from this Path
22661
22662
  */
22662
- resolve(path12) {
22663
- if (!path12) {
22663
+ resolve(path13) {
22664
+ if (!path13) {
22664
22665
  return this;
22665
22666
  }
22666
- const rootPath = this.getRootString(path12);
22667
- const dir = path12.substring(rootPath.length);
22667
+ const rootPath = this.getRootString(path13);
22668
+ const dir = path13.substring(rootPath.length);
22668
22669
  const dirParts = dir.split(this.splitSep);
22669
22670
  const result = rootPath ? this.getRoot(rootPath).#resolveParts(dirParts) : this.#resolveParts(dirParts);
22670
22671
  return result;
@@ -23416,8 +23417,8 @@ var PathWin32 = class _PathWin32 extends PathBase {
23416
23417
  /**
23417
23418
  * @internal
23418
23419
  */
23419
- getRootString(path12) {
23420
- return import_node_path2.win32.parse(path12).root;
23420
+ getRootString(path13) {
23421
+ return import_node_path2.win32.parse(path13).root;
23421
23422
  }
23422
23423
  /**
23423
23424
  * @internal
@@ -23463,8 +23464,8 @@ var PathPosix = class _PathPosix extends PathBase {
23463
23464
  /**
23464
23465
  * @internal
23465
23466
  */
23466
- getRootString(path12) {
23467
- return path12.startsWith("/") ? "/" : "";
23467
+ getRootString(path13) {
23468
+ return path13.startsWith("/") ? "/" : "";
23468
23469
  }
23469
23470
  /**
23470
23471
  * @internal
@@ -23553,11 +23554,11 @@ var PathScurryBase = class {
23553
23554
  /**
23554
23555
  * Get the depth of a provided path, string, or the cwd
23555
23556
  */
23556
- depth(path12 = this.cwd) {
23557
- if (typeof path12 === "string") {
23558
- path12 = this.cwd.resolve(path12);
23557
+ depth(path13 = this.cwd) {
23558
+ if (typeof path13 === "string") {
23559
+ path13 = this.cwd.resolve(path13);
23559
23560
  }
23560
- return path12.depth();
23561
+ return path13.depth();
23561
23562
  }
23562
23563
  /**
23563
23564
  * Return the cache of child entries. Exposed so subclasses can create
@@ -24044,9 +24045,9 @@ var PathScurryBase = class {
24044
24045
  process3();
24045
24046
  return results;
24046
24047
  }
24047
- chdir(path12 = this.cwd) {
24048
+ chdir(path13 = this.cwd) {
24048
24049
  const oldCwd = this.cwd;
24049
- this.cwd = typeof path12 === "string" ? this.cwd.resolve(path12) : path12;
24050
+ this.cwd = typeof path13 === "string" ? this.cwd.resolve(path13) : path13;
24050
24051
  this.cwd[setAsCwd](oldCwd);
24051
24052
  }
24052
24053
  };
@@ -24408,8 +24409,8 @@ var MatchRecord = class {
24408
24409
  }
24409
24410
  // match, absolute, ifdir
24410
24411
  entries() {
24411
- return [...this.store.entries()].map(([path12, n]) => [
24412
- path12,
24412
+ return [...this.store.entries()].map(([path13, n]) => [
24413
+ path13,
24413
24414
  !!(n & 2),
24414
24415
  !!(n & 1)
24415
24416
  ]);
@@ -24614,9 +24615,9 @@ var GlobUtil = class {
24614
24615
  signal;
24615
24616
  maxDepth;
24616
24617
  includeChildMatches;
24617
- constructor(patterns, path12, opts) {
24618
+ constructor(patterns, path13, opts) {
24618
24619
  this.patterns = patterns;
24619
- this.path = path12;
24620
+ this.path = path13;
24620
24621
  this.opts = opts;
24621
24622
  this.#sep = !opts.posix && opts.platform === "win32" ? "\\" : "/";
24622
24623
  this.includeChildMatches = opts.includeChildMatches !== false;
@@ -24635,11 +24636,11 @@ var GlobUtil = class {
24635
24636
  });
24636
24637
  }
24637
24638
  }
24638
- #ignored(path12) {
24639
- return this.seen.has(path12) || !!this.#ignore?.ignored?.(path12);
24639
+ #ignored(path13) {
24640
+ return this.seen.has(path13) || !!this.#ignore?.ignored?.(path13);
24640
24641
  }
24641
- #childrenIgnored(path12) {
24642
- return !!this.#ignore?.childrenIgnored?.(path12);
24642
+ #childrenIgnored(path13) {
24643
+ return !!this.#ignore?.childrenIgnored?.(path13);
24643
24644
  }
24644
24645
  // backpressure mechanism
24645
24646
  pause() {
@@ -24854,8 +24855,8 @@ var GlobUtil = class {
24854
24855
  };
24855
24856
  var GlobWalker = class extends GlobUtil {
24856
24857
  matches = /* @__PURE__ */ new Set();
24857
- constructor(patterns, path12, opts) {
24858
- super(patterns, path12, opts);
24858
+ constructor(patterns, path13, opts) {
24859
+ super(patterns, path13, opts);
24859
24860
  }
24860
24861
  matchEmit(e) {
24861
24862
  this.matches.add(e);
@@ -24892,8 +24893,8 @@ var GlobWalker = class extends GlobUtil {
24892
24893
  };
24893
24894
  var GlobStream = class extends GlobUtil {
24894
24895
  results;
24895
- constructor(patterns, path12, opts) {
24896
- super(patterns, path12, opts);
24896
+ constructor(patterns, path13, opts) {
24897
+ super(patterns, path13, opts);
24897
24898
  this.results = new Minipass({
24898
24899
  signal: this.signal,
24899
24900
  objectMode: true
@@ -26401,7 +26402,7 @@ Subquery.prototype.getSQL = function() {
26401
26402
  function mapResultRow(columns, row, joinsNotNullableMap) {
26402
26403
  const nullifyMap = {};
26403
26404
  const result = columns.reduce(
26404
- (result2, { path: path12, field }, columnIndex) => {
26405
+ (result2, { path: path13, field }, columnIndex) => {
26405
26406
  let decoder;
26406
26407
  if (is(field, Column)) {
26407
26408
  decoder = field;
@@ -26413,8 +26414,8 @@ function mapResultRow(columns, row, joinsNotNullableMap) {
26413
26414
  decoder = field.sql.decoder;
26414
26415
  }
26415
26416
  let node = result2;
26416
- for (const [pathChunkIndex, pathChunk] of path12.entries()) {
26417
- if (pathChunkIndex < path12.length - 1) {
26417
+ for (const [pathChunkIndex, pathChunk] of path13.entries()) {
26418
+ if (pathChunkIndex < path13.length - 1) {
26418
26419
  if (!(pathChunk in node)) {
26419
26420
  node[pathChunk] = {};
26420
26421
  }
@@ -26422,8 +26423,8 @@ function mapResultRow(columns, row, joinsNotNullableMap) {
26422
26423
  } else {
26423
26424
  const rawValue = row[columnIndex];
26424
26425
  const value = node[pathChunk] = rawValue === null ? null : decoder.mapFromDriverValue(rawValue);
26425
- if (joinsNotNullableMap && is(field, Column) && path12.length === 2) {
26426
- const objectName = path12[0];
26426
+ if (joinsNotNullableMap && is(field, Column) && path13.length === 2) {
26427
+ const objectName = path13[0];
26427
26428
  if (!(objectName in nullifyMap)) {
26428
26429
  nullifyMap[objectName] = value === null ? getTableName(field.table) : false;
26429
26430
  } else if (typeof nullifyMap[objectName] === "string" && nullifyMap[objectName] !== getTableName(field.table)) {
@@ -31375,14 +31376,19 @@ function assertValidSegment(path72, segment) {
31375
31376
  );
31376
31377
  }
31377
31378
  }
31378
- function resolveDottedPath(root, path72) {
31379
- if (path72 === "$") return root;
31380
- if (!path72.startsWith("$.")) {
31379
+ function normalizeDottedPath(path72) {
31380
+ if (path72 === "$" || path72.startsWith("$.")) return path72;
31381
+ if (path72.startsWith("$")) {
31381
31382
  throw new Error(
31382
- `Invalid collection path "${path72}": must start with "$." (e.g. "$.tasks")`
31383
+ `Invalid collection path "${path72}": a path beginning with "$" must be explicit-root form ("$" or "$.field"). Write "${path72.slice(1)}" for a bare root-relative path, or "$.${path72.slice(1)}" for explicit-root form.`
31383
31384
  );
31384
31385
  }
31385
- const segments = path72.slice(2).split(".");
31386
+ return `$.${path72}`;
31387
+ }
31388
+ function resolveDottedPath(root, path72) {
31389
+ const normalizedPath = normalizeDottedPath(path72);
31390
+ if (normalizedPath === "$") return root;
31391
+ const segments = normalizedPath.slice(2).split(".");
31386
31392
  let current = root;
31387
31393
  for (const segment of segments) {
31388
31394
  if (segment.length === 0) {
@@ -31397,13 +31403,9 @@ function resolveDottedPath(root, path72) {
31397
31403
  return current;
31398
31404
  }
31399
31405
  function removeDottedPath(value, path72) {
31400
- if (path72 === "$") return;
31401
- if (!path72.startsWith("$.")) {
31402
- throw new Error(
31403
- `Invalid ignore-paths entry "${path72}": must start with "$." (e.g. "$.fetchedAt")`
31404
- );
31405
- }
31406
- const segments = path72.slice(2).split(".");
31406
+ const normalizedPath = normalizeDottedPath(path72);
31407
+ if (normalizedPath === "$") return;
31408
+ const segments = normalizedPath.slice(2).split(".");
31407
31409
  let current = value;
31408
31410
  for (let i = 0; i < segments.length - 1; i++) {
31409
31411
  const segment = segments[i];
@@ -31677,6 +31679,11 @@ function rowToEvent(row) {
31677
31679
  createdAt: row.createdAt
31678
31680
  };
31679
31681
  }
31682
+ function deliveryStateForRow(row) {
31683
+ if (row.acknowledgedAt) return "acknowledged";
31684
+ if (row.firstNotifiedAt) return "claimed";
31685
+ return "unread";
31686
+ }
31680
31687
  function scopeMatches(eventScope, requested) {
31681
31688
  if (!requested) return true;
31682
31689
  for (const [key, value] of Object.entries(requested)) {
@@ -31844,6 +31851,13 @@ var RuntimeStore = class {
31844
31851
  if (query.objectKey)
31845
31852
  conditions.push(eq(monitorEvents.objectKey, query.objectKey));
31846
31853
  if (query.since) conditions.push(gt(monitorEvents.createdAt, query.since));
31854
+ if (query.workspacePath !== void 0) {
31855
+ const workspaceCondition = or(
31856
+ eq(monitorEvents.workspacePath, query.workspacePath),
31857
+ isNull(monitorEvents.workspacePath)
31858
+ );
31859
+ if (workspaceCondition) conditions.push(workspaceCondition);
31860
+ }
31847
31861
  let rows = query.sessionId ? asInternalDb2(this.db).select({
31848
31862
  event: monitorEvents,
31849
31863
  state: sessionEventState
@@ -31918,6 +31932,37 @@ var RuntimeStore = class {
31918
31932
  createdAt: row.createdAt
31919
31933
  }));
31920
31934
  }
31935
+ listDeliveryProjectionsForMonitor(monitorId, workspacePath) {
31936
+ const conditions = [eq(monitorEvents.monitorId, monitorId)];
31937
+ if (workspacePath !== void 0) {
31938
+ const workspaceCondition = or(
31939
+ eq(agentSessions.workspacePath, workspacePath),
31940
+ isNull(agentSessions.workspacePath)
31941
+ );
31942
+ if (workspaceCondition) conditions.push(workspaceCondition);
31943
+ }
31944
+ const rows = asInternalDb2(this.db).select({
31945
+ event: monitorEvents,
31946
+ state: sessionEventState,
31947
+ session: agentSessions
31948
+ }).from(sessionEventState).innerJoin(monitorEvents, eq(sessionEventState.eventId, monitorEvents.id)).innerJoin(
31949
+ agentSessions,
31950
+ eq(sessionEventState.sessionId, agentSessions.id)
31951
+ ).where(and(...conditions)).orderBy(desc(monitorEvents.createdAt)).all();
31952
+ return rows.map(({ event, state, session }) => ({
31953
+ eventId: event.id,
31954
+ sessionId: session.id,
31955
+ sessionRole: session.role,
31956
+ sessionStatus: session.status,
31957
+ deliveryState: deliveryStateForRow(state),
31958
+ workspacePath: session.workspacePath ?? null,
31959
+ createdAt: state.createdAt,
31960
+ ...state.firstNotifiedAt ? { firstNotifiedAt: state.firstNotifiedAt } : {},
31961
+ ...state.lastClaimAt ? { lastClaimAt: state.lastClaimAt } : {},
31962
+ ...state.lastClaimLifecycle ? { lastClaimLifecycle: state.lastClaimLifecycle } : {},
31963
+ ...state.acknowledgedAt ? { acknowledgedAt: state.acknowledgedAt } : {}
31964
+ }));
31965
+ }
31921
31966
  sessionsForWorkspace(workspacePath) {
31922
31967
  return asInternalDb2(this.db).select().from(agentSessions).where(
31923
31968
  workspacePath == null ? isNull(agentSessions.workspacePath) : or(
@@ -32025,6 +32070,14 @@ var DEFAULT_FILE_FINGERPRINT_POLL_MS = 3e4;
32025
32070
  var DEFAULT_API_POLL_MS = 3e5;
32026
32071
  var DEFAULT_HIGH_URGENCY_SETTLE_MS = 15e3;
32027
32072
  var MAX_RECAP_EVENTS = 10;
32073
+ var EXPLAIN_STAGE_LABELS = {
32074
+ definition: "Definition",
32075
+ scheduling: "Scheduling",
32076
+ observation: "Observation",
32077
+ notify: "Notify state",
32078
+ materialization: "Materialization",
32079
+ delivery: "Projection and delivery"
32080
+ };
32028
32081
  function watchConfig(watch) {
32029
32082
  const { type: _type, ...config2 } = watch;
32030
32083
  return config2;
@@ -32043,6 +32096,28 @@ function writeJsonAtomic(filePath2, payload) {
32043
32096
  (0, import_fs3.writeFileSync)(tmpPath, JSON.stringify(payload, null, 2), "utf-8");
32044
32097
  (0, import_fs3.renameSync)(tmpPath, filePath2);
32045
32098
  }
32099
+ function monitorIdFromFilePath(filePath2) {
32100
+ const base = import_path4.default.basename(filePath2);
32101
+ return base === "MONITOR.md" ? import_path4.default.basename(import_path4.default.dirname(filePath2)) : import_path4.default.parse(filePath2).name;
32102
+ }
32103
+ function explainStage(id, status, reason, details) {
32104
+ return {
32105
+ id,
32106
+ label: EXPLAIN_STAGE_LABELS[id],
32107
+ status,
32108
+ reason,
32109
+ ...details ? { details } : {}
32110
+ };
32111
+ }
32112
+ function explainVerdict(stages) {
32113
+ const stopped = stages.find((stage2) => stage2.status !== "ok");
32114
+ const stage = stopped ?? stages[stages.length - 1];
32115
+ return {
32116
+ status: stage?.status ?? "ok",
32117
+ stage: stage?.id ?? "delivery",
32118
+ reason: stage?.reason ?? "Monitor delivered successfully."
32119
+ };
32120
+ }
32046
32121
  function serializeObservation(monitor, observation, observedAt) {
32047
32122
  return { monitor, observation, observedAt };
32048
32123
  }
@@ -32157,6 +32232,320 @@ var AgentMonitorRuntime = class {
32157
32232
  listObservationHistory(query = {}) {
32158
32233
  return this.store.listObservationHistory(query);
32159
32234
  }
32235
+ async explainMonitor(input) {
32236
+ const now = input.now ?? /* @__PURE__ */ new Date();
32237
+ const historyLimit = input.historyLimit ?? 10;
32238
+ const eventLimit = input.eventLimit ?? 10;
32239
+ const stages = [];
32240
+ const scan = await scanMonitors(input.monitorsDir);
32241
+ const parseError = scan.errors.find(
32242
+ (error2) => monitorIdFromFilePath(error2.filePath) === input.monitorId
32243
+ );
32244
+ const monitor = scan.monitors.find(
32245
+ (candidate) => candidate.monitor.id === input.monitorId
32246
+ )?.monitor;
32247
+ const duplicate = scan.duplicateIds.find(
32248
+ (candidate) => candidate.id === input.monitorId
32249
+ );
32250
+ if (parseError) {
32251
+ stages.push(
32252
+ explainStage(
32253
+ "definition",
32254
+ "failure",
32255
+ `MONITOR.md failed to parse or validate: ${parseError.error}`,
32256
+ { filePath: parseError.filePath }
32257
+ )
32258
+ );
32259
+ return {
32260
+ monitorId: input.monitorId,
32261
+ generatedAt: now,
32262
+ stages,
32263
+ verdict: explainVerdict(stages),
32264
+ observations: [],
32265
+ events: [],
32266
+ projections: [],
32267
+ leadSessions: []
32268
+ };
32269
+ }
32270
+ if (!monitor) {
32271
+ stages.push(
32272
+ explainStage(
32273
+ "definition",
32274
+ "failure",
32275
+ `Monitor "${input.monitorId}" was not found in ${input.monitorsDir}.`
32276
+ )
32277
+ );
32278
+ return {
32279
+ monitorId: input.monitorId,
32280
+ generatedAt: now,
32281
+ stages,
32282
+ verdict: explainVerdict(stages),
32283
+ observations: [],
32284
+ events: [],
32285
+ projections: [],
32286
+ leadSessions: []
32287
+ };
32288
+ }
32289
+ if (duplicate) {
32290
+ stages.push(
32291
+ explainStage(
32292
+ "definition",
32293
+ "failure",
32294
+ `Monitor id "${input.monitorId}" is duplicated across ${String(duplicate.filePaths.length)} files.`,
32295
+ { filePaths: duplicate.filePaths }
32296
+ )
32297
+ );
32298
+ return {
32299
+ monitorId: input.monitorId,
32300
+ generatedAt: now,
32301
+ monitor: {
32302
+ id: monitor.id,
32303
+ displayName: monitor.displayName,
32304
+ filePath: monitor.filePath,
32305
+ sourceName: monitor.frontmatter.watch.type,
32306
+ urgency: monitor.frontmatter.urgency
32307
+ },
32308
+ stages,
32309
+ verdict: explainVerdict(stages),
32310
+ observations: [],
32311
+ events: [],
32312
+ projections: [],
32313
+ leadSessions: []
32314
+ };
32315
+ }
32316
+ const sourceName = monitor.frontmatter.watch.type;
32317
+ const source6 = this.registry.get(sourceName);
32318
+ const scopeErrors = source6 ? validateScope(
32319
+ watchConfig(monitor.frontmatter.watch),
32320
+ source6.scopeSchema
32321
+ ) : [`Unknown source "${sourceName}".`];
32322
+ if (scopeErrors.length > 0) {
32323
+ stages.push(
32324
+ explainStage(
32325
+ "definition",
32326
+ "failure",
32327
+ `Monitor definition is invalid: ${scopeErrors.join("; ")}`,
32328
+ { filePath: monitor.filePath, sourceName }
32329
+ )
32330
+ );
32331
+ return {
32332
+ monitorId: input.monitorId,
32333
+ generatedAt: now,
32334
+ monitor: {
32335
+ id: monitor.id,
32336
+ displayName: monitor.displayName,
32337
+ filePath: monitor.filePath,
32338
+ sourceName,
32339
+ urgency: monitor.frontmatter.urgency
32340
+ },
32341
+ stages,
32342
+ verdict: explainVerdict(stages),
32343
+ observations: [],
32344
+ events: [],
32345
+ projections: [],
32346
+ leadSessions: []
32347
+ };
32348
+ }
32349
+ stages.push(
32350
+ explainStage("definition", "ok", "Monitor definition is valid.", {
32351
+ filePath: monitor.filePath,
32352
+ sourceName
32353
+ })
32354
+ );
32355
+ const runtimeState = this.store.getMonitorState(input.monitorId);
32356
+ const schedule = this.scheduleForMonitor(monitor, now);
32357
+ const lastObservationAt = runtimeState.lastObservationAt;
32358
+ const nextDueAt = schedule.due ? now : new Date(
32359
+ (lastObservationAt?.getTime() ?? now.getTime()) + schedule.nextPollMs
32360
+ );
32361
+ stages.push(
32362
+ explainStage(
32363
+ "scheduling",
32364
+ "ok",
32365
+ lastObservationAt ? `Last tick completed at ${lastObservationAt.toISOString()}; next due ${schedule.due ? "now" : nextDueAt.toISOString()}.` : "No completed tick is recorded yet; the monitor is due on the next daemon tick.",
32366
+ {
32367
+ due: schedule.due,
32368
+ nextPollMs: schedule.nextPollMs,
32369
+ nextDueAt: nextDueAt.toISOString(),
32370
+ ...lastObservationAt ? { lastObservationAt: lastObservationAt.toISOString() } : {}
32371
+ }
32372
+ )
32373
+ );
32374
+ const observations = this.store.listObservationHistory({
32375
+ monitorId: input.monitorId,
32376
+ limit: historyLimit
32377
+ });
32378
+ const latestObservation = observations[0];
32379
+ if (!latestObservation) {
32380
+ stages.push(
32381
+ explainStage(
32382
+ "observation",
32383
+ "failure",
32384
+ "No observation history has been recorded for this monitor."
32385
+ )
32386
+ );
32387
+ } else if (latestObservation.result === "errored") {
32388
+ stages.push(
32389
+ explainStage(
32390
+ "observation",
32391
+ "failure",
32392
+ "The most recent source observation errored.",
32393
+ latestObservation.observationData
32394
+ )
32395
+ );
32396
+ } else if (latestObservation.result === "no-change") {
32397
+ stages.push(
32398
+ explainStage(
32399
+ "observation",
32400
+ "healthy",
32401
+ "Source ran, observed 0 changes \u2014 your watched target genuinely hasn\u2019t changed (not a bug).",
32402
+ latestObservation.observationData
32403
+ )
32404
+ );
32405
+ } else if (latestObservation.result === "rebaselined") {
32406
+ stages.push(
32407
+ explainStage(
32408
+ "observation",
32409
+ "healthy",
32410
+ "Source rebaselined and emitted no change \u2014 your watched target is being tracked, nothing to report (not a bug).",
32411
+ latestObservation.observationData
32412
+ )
32413
+ );
32414
+ } else {
32415
+ stages.push(
32416
+ explainStage(
32417
+ "observation",
32418
+ "ok",
32419
+ `The latest observation outcome was ${latestObservation.result}.`,
32420
+ latestObservation.observationData
32421
+ )
32422
+ );
32423
+ }
32424
+ const observationHealthy = latestObservation?.result === "no-change" || latestObservation?.result === "rebaselined";
32425
+ const notifyState = runtimeState.notifyState;
32426
+ const pendingDebounce = notifyState.pendingDebounce;
32427
+ const suppressedUntil = notifyState.suppressedUntil ? new Date(notifyState.suppressedUntil) : null;
32428
+ if (pendingDebounce && new Date(pendingDebounce.dueAt) > now) {
32429
+ stages.push(
32430
+ explainStage(
32431
+ "notify",
32432
+ "pending",
32433
+ `debounce is holding ${String(pendingDebounce.observations.length)} observation(s) until ${pendingDebounce.dueAt}.`,
32434
+ {
32435
+ dueAt: pendingDebounce.dueAt,
32436
+ observations: pendingDebounce.observations.length
32437
+ }
32438
+ )
32439
+ );
32440
+ } else if (suppressedUntil && suppressedUntil > now) {
32441
+ stages.push(
32442
+ explainStage(
32443
+ "notify",
32444
+ "pending",
32445
+ `throttle is suppressing new notifications until ${suppressedUntil.toISOString()}.`,
32446
+ { suppressedUntil: suppressedUntil.toISOString() }
32447
+ )
32448
+ );
32449
+ } else {
32450
+ stages.push(
32451
+ explainStage(
32452
+ "notify",
32453
+ "ok",
32454
+ "No debounce or throttle hold is currently active."
32455
+ )
32456
+ );
32457
+ }
32458
+ const events = this.store.listEvents({
32459
+ monitorId: input.monitorId,
32460
+ // Scope to the explained workspace so a same-id monitor in another
32461
+ // workspace cannot leak its events into this report (issue #94 review).
32462
+ ...input.workspacePath !== void 0 ? { workspacePath: input.workspacePath } : {}
32463
+ }).slice(0, eventLimit);
32464
+ if (events.length === 0) {
32465
+ stages.push(
32466
+ observationHealthy ? explainStage(
32467
+ "materialization",
32468
+ "healthy",
32469
+ "No events materialized \u2014 expected, because the source observed no changes (not a bug)."
32470
+ ) : explainStage(
32471
+ "materialization",
32472
+ "failure",
32473
+ "No monitor_events rows exist for this monitor."
32474
+ )
32475
+ );
32476
+ } else {
32477
+ stages.push(
32478
+ explainStage(
32479
+ "materialization",
32480
+ "ok",
32481
+ `${String(events.length)} recent monitor_events row(s) found.`,
32482
+ { eventIds: events.map((event) => event.id) }
32483
+ )
32484
+ );
32485
+ }
32486
+ const projections = this.store.listDeliveryProjectionsForMonitor(
32487
+ input.monitorId,
32488
+ // Scope to the explained workspace's sessions (plus global sessions) so
32489
+ // projections from other workspaces are not overcounted (issue #94 review).
32490
+ input.workspacePath
32491
+ );
32492
+ const leadSessions = this.store.sessionsForWorkspace(input.workspacePath).filter((session) => session.role === "lead");
32493
+ if (events.length > 0 && projections.length === 0) {
32494
+ stages.push(
32495
+ explainStage(
32496
+ "delivery",
32497
+ "failure",
32498
+ leadSessions.length === 0 ? "No lead session is registered for this workspace, so events were not projected." : "No session_event_state projections exist for the materialized events.",
32499
+ { leadSessions: leadSessions.map((session) => session.id) }
32500
+ )
32501
+ );
32502
+ } else if (events.length > 0) {
32503
+ const counts = projections.reduce(
32504
+ (acc, projection) => {
32505
+ acc[projection.deliveryState] = (acc[projection.deliveryState] ?? 0) + 1;
32506
+ return acc;
32507
+ },
32508
+ {}
32509
+ );
32510
+ stages.push(
32511
+ explainStage(
32512
+ "delivery",
32513
+ "ok",
32514
+ `Events are projected to lead sessions (${Object.entries(counts).map(([state, count]) => `${state}: ${String(count)}`).join(", ")}).`,
32515
+ counts
32516
+ )
32517
+ );
32518
+ } else {
32519
+ stages.push(
32520
+ observationHealthy ? explainStage(
32521
+ "delivery",
32522
+ "healthy",
32523
+ "Nothing to deliver \u2014 the source observed no changes, so there is no signal to project (not a bug)."
32524
+ ) : explainStage(
32525
+ "delivery",
32526
+ "pending",
32527
+ "Delivery has not started because no event has materialized yet."
32528
+ )
32529
+ );
32530
+ }
32531
+ return {
32532
+ monitorId: input.monitorId,
32533
+ generatedAt: now,
32534
+ monitor: {
32535
+ id: monitor.id,
32536
+ displayName: monitor.displayName,
32537
+ filePath: monitor.filePath,
32538
+ sourceName,
32539
+ urgency: monitor.frontmatter.urgency
32540
+ },
32541
+ stages,
32542
+ verdict: explainVerdict(stages),
32543
+ observations,
32544
+ events,
32545
+ projections,
32546
+ leadSessions
32547
+ };
32548
+ }
32160
32549
  acknowledgeSession(sessionId, eventIds) {
32161
32550
  const ids = eventIds ?? this.store.unreadEventsForSession(sessionId).map((e) => e.id);
32162
32551
  this.store.acknowledgeEvents(sessionId, ids);
@@ -32301,6 +32690,7 @@ var AgentMonitorRuntime = class {
32301
32690
  const now = /* @__PURE__ */ new Date();
32302
32691
  const emittedEventIds = [];
32303
32692
  const evaluated = [];
32693
+ const erroredObservations = [];
32304
32694
  for (const parsed of result.monitors) {
32305
32695
  const monitor = parsed.monitor;
32306
32696
  const sourceName = monitor.frontmatter.watch.type;
@@ -32325,13 +32715,15 @@ var AgentMonitorRuntime = class {
32325
32715
  }
32326
32716
  );
32327
32717
  } catch (observeError) {
32718
+ const message = observeError instanceof Error ? observeError.message : String(observeError);
32719
+ erroredObservations.push({ monitorId: monitor.id, message });
32328
32720
  try {
32329
32721
  this.store.recordObservationHistory({
32330
32722
  monitorId: monitor.id,
32331
32723
  sourceName,
32332
32724
  result: "errored",
32333
32725
  observationData: {
32334
- error: observeError instanceof Error ? observeError.message : String(observeError)
32726
+ error: message
32335
32727
  }
32336
32728
  });
32337
32729
  } catch {
@@ -32347,13 +32739,15 @@ var AgentMonitorRuntime = class {
32347
32739
  })
32348
32740
  );
32349
32741
  } catch (ingestError) {
32742
+ const message = ingestError instanceof Error ? ingestError.message : String(ingestError);
32743
+ erroredObservations.push({ monitorId: monitor.id, message });
32350
32744
  try {
32351
32745
  this.store.recordObservationHistory({
32352
32746
  monitorId: monitor.id,
32353
32747
  sourceName,
32354
32748
  result: "errored",
32355
32749
  observationData: {
32356
- error: ingestError instanceof Error ? ingestError.message : String(ingestError)
32750
+ error: message
32357
32751
  }
32358
32752
  });
32359
32753
  } catch {
@@ -32361,7 +32755,11 @@ var AgentMonitorRuntime = class {
32361
32755
  }
32362
32756
  }
32363
32757
  this.refreshWorkspaceSessions(workspacePath);
32364
- return { evaluatedMonitors: evaluated, emittedEventIds };
32758
+ return {
32759
+ evaluatedMonitors: evaluated,
32760
+ emittedEventIds,
32761
+ erroredObservations
32762
+ };
32365
32763
  }
32366
32764
  /**
32367
32765
  * Funnel a batch of observations through notify dispatch, persist the updated
@@ -32969,7 +33367,7 @@ var scopeSchema2 = {
32969
33367
  properties: {
32970
33368
  path: {
32971
33369
  type: "string",
32972
- description: 'Dotted $.-path to the array within the parsed JSON (e.g. "$.tasks")'
33370
+ description: 'Dotted path to the array within the parsed JSON (e.g. "tasks" or "$.tasks")'
32973
33371
  },
32974
33372
  key: {
32975
33373
  type: "string",
@@ -32978,7 +33376,7 @@ var scopeSchema2 = {
32978
33376
  "ignore-paths": {
32979
33377
  type: "array",
32980
33378
  items: { type: "string" },
32981
- description: "Dotted $.-paths (relative to each element) removed before comparison"
33379
+ description: 'Dotted paths (relative to each element, e.g. "fetchedAt" or "$.fetchedAt") removed before comparison'
32982
33380
  }
32983
33381
  },
32984
33382
  required: ["path", "key"]
@@ -33076,6 +33474,12 @@ function parseScopeConfig3(config2) {
33076
33474
  const cd = config2["change-detection"];
33077
33475
  const rawStrategy = cd?.strategy;
33078
33476
  const strategy = rawStrategy === "json-diff" || rawStrategy === "exit-code" ? rawStrategy : "text-diff";
33477
+ const ignorePaths = parseTopLevelIgnorePaths(cd);
33478
+ if (ignorePaths.length > 0 && strategy !== "json-diff") {
33479
+ throw new Error(
33480
+ "change-detection.ignore-paths requires strategy: json-diff"
33481
+ );
33482
+ }
33079
33483
  const collection = parseKeyedCollectionConfig(config2["change-detection"]);
33080
33484
  if (collection && strategy !== "json-diff") {
33081
33485
  throw new Error("change-detection.collection requires strategy: json-diff");
@@ -33087,7 +33491,16 @@ function parseScopeConfig3(config2) {
33087
33491
  const timeoutMs = typeof rawTimeout === "string" ? parseDuration(rawTimeout) : DEFAULT_TIMEOUT_MS;
33088
33492
  const key = config2["key"];
33089
33493
  const objectKey = typeof key === "string" && key.length > 0 ? key : command.join(" ");
33090
- return { command, cwd, env, timeoutMs, objectKey, strategy, collection };
33494
+ return {
33495
+ command,
33496
+ cwd,
33497
+ env,
33498
+ timeoutMs,
33499
+ objectKey,
33500
+ strategy,
33501
+ ignorePaths,
33502
+ collection
33503
+ };
33091
33504
  }
33092
33505
  async function runCommand(scope) {
33093
33506
  return new Promise((resolve) => {
@@ -33173,18 +33586,75 @@ function sortKeys3(value) {
33173
33586
  }
33174
33587
  return value;
33175
33588
  }
33176
- function hasChanged2(strategy, prev, curr) {
33589
+ function parseTopLevelIgnorePaths(changeDetection) {
33590
+ const rawIgnorePaths = changeDetection?.["ignore-paths"];
33591
+ if (rawIgnorePaths === void 0) return [];
33592
+ if (!Array.isArray(rawIgnorePaths) || !rawIgnorePaths.every((entry) => typeof entry === "string")) {
33593
+ throw new Error(
33594
+ "change-detection.ignore-paths must be an array of strings"
33595
+ );
33596
+ }
33597
+ return rawIgnorePaths;
33598
+ }
33599
+ function normalizeJsonPath(path13) {
33600
+ if (path13 === "$" || path13.startsWith("$.")) return path13;
33601
+ return `$.${path13}`;
33602
+ }
33603
+ function assertValidJsonPathSegment(path13, segment) {
33604
+ if (segment.length === 0) {
33605
+ throw new Error(
33606
+ `Invalid change-detection.ignore-paths entry "${path13}": empty path segment`
33607
+ );
33608
+ }
33609
+ if (/[.[\]*?]/.test(segment)) {
33610
+ throw new Error(
33611
+ `Invalid change-detection.ignore-paths entry "${path13}": unsupported path segment "${segment}"`
33612
+ );
33613
+ }
33614
+ }
33615
+ function stripIgnoredJsonPaths(value, ignorePaths) {
33616
+ if (ignorePaths.length === 0) return value;
33617
+ const cloned = structuredClone(value);
33618
+ for (const path13 of ignorePaths) {
33619
+ removeJsonPath(cloned, path13);
33620
+ }
33621
+ return cloned;
33622
+ }
33623
+ function removeJsonPath(value, path13) {
33624
+ const normalizedPath = normalizeJsonPath(path13);
33625
+ if (normalizedPath === "$") return;
33626
+ const segments = normalizedPath.slice(2).split(".");
33627
+ let current = value;
33628
+ for (let i = 0; i < segments.length - 1; i++) {
33629
+ const segment = segments[i];
33630
+ assertValidJsonPathSegment(path13, segment ?? "");
33631
+ if (current === null || typeof current !== "object") return;
33632
+ if (!Object.hasOwn(current, segment ?? "")) return;
33633
+ current = current[segment ?? ""];
33634
+ }
33635
+ const last = segments.at(-1) ?? "";
33636
+ assertValidJsonPathSegment(path13, last);
33637
+ if (current !== null && typeof current === "object") {
33638
+ Reflect.deleteProperty(current, last);
33639
+ }
33640
+ }
33641
+ function hasChanged2(strategy, ignorePaths, prev, curr) {
33177
33642
  switch (strategy) {
33178
33643
  case "exit-code":
33179
33644
  return prev.exitCode !== curr.exitCode;
33180
- case "json-diff":
33645
+ case "json-diff": {
33646
+ let prevParsed;
33647
+ let currParsed;
33181
33648
  try {
33182
- const prevJson = JSON.stringify(sortKeys3(JSON.parse(prev.stdout)));
33183
- const currJson = JSON.stringify(sortKeys3(JSON.parse(curr.stdout)));
33184
- return prevJson !== currJson;
33649
+ prevParsed = JSON.parse(prev.stdout);
33650
+ currParsed = JSON.parse(curr.stdout);
33185
33651
  } catch {
33186
33652
  return prev.stdout !== curr.stdout;
33187
33653
  }
33654
+ return JSON.stringify(
33655
+ sortKeys3(stripIgnoredJsonPaths(prevParsed, ignorePaths))
33656
+ ) !== JSON.stringify(sortKeys3(stripIgnoredJsonPaths(currParsed, ignorePaths)));
33657
+ }
33188
33658
  case "text-diff":
33189
33659
  return prev.stdout !== curr.stdout;
33190
33660
  }
@@ -33266,7 +33736,7 @@ var scopeSchema3 = {
33266
33736
  properties: {
33267
33737
  path: {
33268
33738
  type: "string",
33269
- description: 'Dotted $.-path to the array within the parsed JSON (e.g. "$.tasks")'
33739
+ description: 'Dotted path to the array within the parsed JSON (e.g. "tasks" or "$.tasks")'
33270
33740
  },
33271
33741
  key: {
33272
33742
  type: "string",
@@ -33275,20 +33745,38 @@ var scopeSchema3 = {
33275
33745
  "ignore-paths": {
33276
33746
  type: "array",
33277
33747
  items: { type: "string" },
33278
- description: "Dotted $.-paths (relative to each element) removed before comparison"
33748
+ description: 'Dotted paths (relative to each element, e.g. "fetchedAt" or "$.fetchedAt") removed before comparison'
33279
33749
  }
33280
33750
  },
33281
- required: ["path", "key"]
33751
+ required: ["path", "key"],
33752
+ additionalProperties: false
33753
+ },
33754
+ "ignore-paths": {
33755
+ type: "array",
33756
+ items: { type: "string" },
33757
+ description: "Dotted paths removed from parsed JSON before plain json-diff comparison"
33282
33758
  }
33283
33759
  },
33760
+ additionalProperties: false,
33284
33761
  // BP3: change-detection.collection requires strategy: json-diff. Under any
33285
33762
  // other strategy (or the defaulted text-diff), presence of `collection` is an
33286
33763
  // authoring-time error.
33287
- if: { required: ["collection"] },
33288
- then: {
33289
- properties: { strategy: { const: "json-diff" } },
33290
- required: ["strategy"]
33291
- }
33764
+ allOf: [
33765
+ {
33766
+ if: { required: ["collection"] },
33767
+ then: {
33768
+ properties: { strategy: { const: "json-diff" } },
33769
+ required: ["strategy"]
33770
+ }
33771
+ },
33772
+ {
33773
+ if: { required: ["ignore-paths"] },
33774
+ then: {
33775
+ properties: { strategy: { const: "json-diff" } },
33776
+ required: ["strategy"]
33777
+ }
33778
+ }
33779
+ ]
33292
33780
  }
33293
33781
  },
33294
33782
  required: ["command"]
@@ -33353,7 +33841,7 @@ var source3 = {
33353
33841
  health: "ok",
33354
33842
  baselined: true
33355
33843
  };
33356
- if (prev !== void 0 && hadBaseline && hasChanged2(scope.strategy, prev, result)) {
33844
+ if (prev !== void 0 && hadBaseline && hasChanged2(scope.strategy, scope.ignorePaths, prev, result)) {
33357
33845
  observations.push(changedObservation(scope, result));
33358
33846
  }
33359
33847
  return { observations, nextState };
@@ -33696,6 +34184,23 @@ function changeDetectionCollectionError(watchConfig2) {
33696
34184
  if (strategy === "json-diff") return void 0;
33697
34185
  return "change-detection.collection requires strategy: json-diff";
33698
34186
  }
34187
+ function oldSourceScopeShapeHint(filePath2) {
34188
+ let content;
34189
+ try {
34190
+ content = (0, import_node_fs3.readFileSync)(filePath2, "utf-8");
34191
+ } catch {
34192
+ return null;
34193
+ }
34194
+ const normalised = content.replace(/^\uFEFF/, "").replace(/\r\n/g, "\n");
34195
+ const frontmatter = /^---\n(?<frontmatter>[\s\S]*?)\n---/.exec(normalised)?.groups?.["frontmatter"];
34196
+ if (!frontmatter) return null;
34197
+ const rawSource = /^source:\s*(?<source>[^\n#]+)/m.exec(frontmatter)?.groups?.["source"];
34198
+ const source6 = rawSource?.trim().replace(/^['"]|['"]$/g, "");
34199
+ const hasScope = /^scope\s*:/m.test(frontmatter);
34200
+ const hasWatch = /^watch\s*:/m.test(frontmatter);
34201
+ if (!source6 || !hasScope || hasWatch) return null;
34202
+ return `did you mean to use the current watch shape? Move source/scope into watch:, for example: watch: { type: ${source6}, ... }`;
34203
+ }
33699
34204
  var validateCommand = new Command("validate").description("Validate MONITOR.md files in a directory").argument("[path]", "Path to monitors directory", ".claude/monitors").addOption(
33700
34205
  new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")
33701
34206
  ).action(async (monitorPath, options2) => {
@@ -33728,12 +34233,12 @@ var validateCommand = new Command("validate").description("Validate MONITOR.md f
33728
34233
  });
33729
34234
  const duplicateErrors = result.duplicateIds.map((dup) => ({
33730
34235
  filePath: dup.filePaths.join(", "),
33731
- error: `Duplicate monitor id "${dup.id}" \u2014 ids are derived from folder names and must be unique within a tree`
34236
+ error: `Duplicate monitor id "${dup.id}" -- ids are derived from folder names and must be unique within a tree`
33732
34237
  }));
33733
34238
  const allErrors = [
33734
34239
  ...result.errors.map((e) => ({
33735
34240
  filePath: e.filePath,
33736
- error: e.error
34241
+ error: [e.error, oldSourceScopeShapeHint(e.filePath)].filter(Boolean).join("; ")
33737
34242
  })),
33738
34243
  ...scopeErrors.map((e) => ({
33739
34244
  filePath: e.id,
@@ -33983,19 +34488,14 @@ inboxCommand.command("archive").description("Archive a completed or failed inbox
33983
34488
 
33984
34489
  // src/commands/monitor-test.ts
33985
34490
  init_cjs_shims();
33986
- var import_node_fs4 = require("fs");
34491
+ var import_node_path5 = __toESM(require("path"), 1);
34492
+ var import_node_fs5 = require("fs");
33987
34493
  var import_promises4 = require("timers/promises");
33988
34494
 
33989
- // src/runtime-client.ts
33990
- init_cjs_shims();
33991
-
33992
- // src/runtime.ts
33993
- init_cjs_shims();
33994
-
33995
34495
  // src/daemon-ipc.ts
33996
34496
  init_cjs_shims();
33997
34497
  var import_node_crypto = require("crypto");
33998
- var import_node_fs3 = require("fs");
34498
+ var import_node_fs4 = require("fs");
33999
34499
  var import_node_os2 = require("os");
34000
34500
  var import_node_path4 = __toESM(require("path"), 1);
34001
34501
  var import_node_net = __toESM(require("net"), 1);
@@ -34018,6 +34518,7 @@ var daemonMethodSchema = external_exports.enum([
34018
34518
  "events.ack",
34019
34519
  "hook.claim",
34020
34520
  "history.list",
34521
+ "monitor.explain",
34021
34522
  "daemon.tick"
34022
34523
  ]);
34023
34524
  var daemonResponseSchema = external_exports.object({
@@ -34066,6 +34567,13 @@ var historyListParamsSchema = external_exports.object({
34066
34567
  monitorId: external_exports.string().optional(),
34067
34568
  limit: external_exports.number().int().positive().optional()
34068
34569
  });
34570
+ var monitorExplainParamsSchema = external_exports.object({
34571
+ monitorId: external_exports.string(),
34572
+ monitorsDir: external_exports.string(),
34573
+ workspacePath: external_exports.string().optional(),
34574
+ historyLimit: external_exports.number().int().positive().optional(),
34575
+ eventLimit: external_exports.number().int().positive().optional()
34576
+ });
34069
34577
  var daemonTickParamsSchema = external_exports.object({
34070
34578
  monitorsDir: external_exports.string(),
34071
34579
  workspacePath: external_exports.string().optional()
@@ -34075,6 +34583,13 @@ var daemonRequestSchema = external_exports.object({
34075
34583
  method: daemonMethodSchema,
34076
34584
  params: external_exports.record(external_exports.string(), external_exports.unknown())
34077
34585
  });
34586
+ var DaemonConnectionError = class extends Error {
34587
+ constructor(message, cause) {
34588
+ super(message);
34589
+ this.cause = cause;
34590
+ }
34591
+ name = "DaemonConnectionError";
34592
+ };
34078
34593
  function isErrnoException(error2) {
34079
34594
  return typeof error2 === "object" && error2 !== null && "code" in error2;
34080
34595
  }
@@ -34095,7 +34610,7 @@ function resolveSocketPath(overridePath) {
34095
34610
  }
34096
34611
  function cleanupSocket(socketPath) {
34097
34612
  try {
34098
- (0, import_node_fs3.rmSync)(socketPath);
34613
+ (0, import_node_fs4.rmSync)(socketPath);
34099
34614
  } catch (error2) {
34100
34615
  const code = isErrnoException(error2) ? String(error2.code) : "";
34101
34616
  if (code !== "ENOENT") throw error2;
@@ -34136,8 +34651,8 @@ function acquireStartupLock(socketPath) {
34136
34651
  const pidFile = import_node_path4.default.join(lock, "pid");
34137
34652
  const tryMkdir = () => {
34138
34653
  try {
34139
- (0, import_node_fs3.mkdirSync)(lock);
34140
- (0, import_node_fs3.writeFileSync)(pidFile, String(process.pid), "utf-8");
34654
+ (0, import_node_fs4.mkdirSync)(lock);
34655
+ (0, import_node_fs4.writeFileSync)(pidFile, String(process.pid), "utf-8");
34141
34656
  return true;
34142
34657
  } catch (err) {
34143
34658
  if (!isErrnoException(err) || err.code !== "EEXIST") throw err;
@@ -34147,7 +34662,7 @@ function acquireStartupLock(socketPath) {
34147
34662
  if (tryMkdir()) return true;
34148
34663
  let holderPid;
34149
34664
  try {
34150
- holderPid = parseInt((0, import_node_fs3.readFileSync)(pidFile, "utf-8"), 10);
34665
+ holderPid = parseInt((0, import_node_fs4.readFileSync)(pidFile, "utf-8"), 10);
34151
34666
  } catch {
34152
34667
  return false;
34153
34668
  }
@@ -34168,11 +34683,11 @@ function acquireStartupLock(socketPath) {
34168
34683
  return false;
34169
34684
  }
34170
34685
  try {
34171
- (0, import_node_fs3.unlinkSync)(pidFile);
34686
+ (0, import_node_fs4.unlinkSync)(pidFile);
34172
34687
  } catch {
34173
34688
  }
34174
34689
  try {
34175
- (0, import_node_fs3.rmdirSync)(lock);
34690
+ (0, import_node_fs4.rmdirSync)(lock);
34176
34691
  } catch {
34177
34692
  }
34178
34693
  return tryMkdir();
@@ -34181,11 +34696,11 @@ function releaseStartupLock(socketPath) {
34181
34696
  const lock = lockPath(socketPath);
34182
34697
  const pidFile = import_node_path4.default.join(lock, "pid");
34183
34698
  try {
34184
- (0, import_node_fs3.unlinkSync)(pidFile);
34699
+ (0, import_node_fs4.unlinkSync)(pidFile);
34185
34700
  } catch {
34186
34701
  }
34187
34702
  try {
34188
- (0, import_node_fs3.rmdirSync)(lock);
34703
+ (0, import_node_fs4.rmdirSync)(lock);
34189
34704
  } catch {
34190
34705
  }
34191
34706
  }
@@ -34256,6 +34771,16 @@ function handleRequest(runtime, request, stop) {
34256
34771
  })
34257
34772
  );
34258
34773
  }
34774
+ case "monitor.explain": {
34775
+ const params = monitorExplainParamsSchema.parse(request.params);
34776
+ return runtime.explainMonitor({
34777
+ monitorId: params.monitorId,
34778
+ monitorsDir: params.monitorsDir,
34779
+ ...params.workspacePath ? { workspacePath: params.workspacePath } : {},
34780
+ ...params.historyLimit ? { historyLimit: params.historyLimit } : {},
34781
+ ...params.eventLimit ? { eventLimit: params.eventLimit } : {}
34782
+ });
34783
+ }
34259
34784
  case "daemon.tick": {
34260
34785
  const params = daemonTickParamsSchema.parse(request.params);
34261
34786
  return runtime.tick(params.monitorsDir, params.workspacePath);
@@ -34267,7 +34792,7 @@ function createDaemonServer({
34267
34792
  socketPath,
34268
34793
  onStop
34269
34794
  }) {
34270
- (0, import_node_fs3.mkdirSync)(import_node_path4.default.dirname(socketPath), { recursive: true });
34795
+ (0, import_node_fs4.mkdirSync)(import_node_path4.default.dirname(socketPath), { recursive: true });
34271
34796
  let serverClosed = false;
34272
34797
  const server = import_node_net.default.createServer((socket) => {
34273
34798
  let buffer = "";
@@ -34325,7 +34850,7 @@ function createDaemonServer({
34325
34850
  const live = await probeSocket(socketPath);
34326
34851
  if (!live) {
34327
34852
  try {
34328
- (0, import_node_fs3.unlinkSync)(socketPath);
34853
+ (0, import_node_fs4.unlinkSync)(socketPath);
34329
34854
  } catch (unlinkErr) {
34330
34855
  const code = isErrnoException(unlinkErr) ? String(unlinkErr.code) : "";
34331
34856
  if (code !== "ENOENT") throw unlinkErr;
@@ -34391,11 +34916,20 @@ async function callDaemon(method, params = {}, options2 = {}) {
34391
34916
  });
34392
34917
  };
34393
34918
  socket.setTimeout(timeoutMs, () => {
34394
- fail(new Error(`Timed out waiting for AgentMon daemon at ${socketPath}`));
34919
+ fail(
34920
+ new DaemonConnectionError(
34921
+ `Timed out waiting for AgentMon daemon at ${socketPath}`
34922
+ )
34923
+ );
34395
34924
  });
34396
34925
  socket.setEncoding("utf-8");
34397
34926
  socket.on("error", (error2) => {
34398
- fail(error2);
34927
+ fail(
34928
+ new DaemonConnectionError(
34929
+ error2 instanceof Error ? error2.message : String(error2),
34930
+ error2
34931
+ )
34932
+ );
34399
34933
  });
34400
34934
  socket.on("data", (chunk) => {
34401
34935
  buffer += typeof chunk === "string" ? chunk : chunk.toString("utf-8");
@@ -34436,7 +34970,11 @@ async function daemonAvailable(socketPath) {
34436
34970
  }
34437
34971
  }
34438
34972
 
34973
+ // src/runtime-client.ts
34974
+ init_cjs_shims();
34975
+
34439
34976
  // src/runtime.ts
34977
+ init_cjs_shims();
34440
34978
  function createRuntime(dbPath = resolveDbPath()) {
34441
34979
  const db = createDb(dbPath);
34442
34980
  const registry2 = new SourceRegistry();
@@ -34499,6 +35037,13 @@ async function listObservationHistoryClient(query, socketPath) {
34499
35037
  socketPath ? { socketPath } : {}
34500
35038
  );
34501
35039
  }
35040
+ async function explainMonitorClient(input, socketPath) {
35041
+ return await callDaemon(
35042
+ "monitor.explain",
35043
+ input,
35044
+ socketPath ? { socketPath } : {}
35045
+ );
35046
+ }
34502
35047
  async function daemonStatusClient(socketPath) {
34503
35048
  return await callDaemon(
34504
35049
  "status",
@@ -34515,6 +35060,127 @@ async function daemonTickClient(monitorsDir, workspacePath) {
34515
35060
  var monitorTestCommand = new Command("monitor").description(
34516
35061
  "Monitor utilities"
34517
35062
  );
35063
+ var EXPLAIN_STAGE_LABELS2 = {
35064
+ definition: "Definition",
35065
+ scheduling: "Scheduling",
35066
+ observation: "Observation",
35067
+ notify: "Notify state",
35068
+ materialization: "Materialization",
35069
+ delivery: "Projection and delivery"
35070
+ };
35071
+ function monitorIdFromFilePath2(filePath2) {
35072
+ const base = import_node_path5.default.basename(filePath2);
35073
+ return base === "MONITOR.md" ? import_node_path5.default.basename(import_node_path5.default.dirname(filePath2)) : import_node_path5.default.parse(filePath2).name;
35074
+ }
35075
+ function explainStage2(id, status, reason, details) {
35076
+ return {
35077
+ id,
35078
+ label: EXPLAIN_STAGE_LABELS2[id],
35079
+ status,
35080
+ reason,
35081
+ ...details ? { details } : {}
35082
+ };
35083
+ }
35084
+ function explainVerdict2(stages) {
35085
+ const stopped = stages.find((stage2) => stage2.status !== "ok");
35086
+ const stage = stopped ?? stages[stages.length - 1];
35087
+ return {
35088
+ status: stage?.status ?? "ok",
35089
+ stage: stage?.id ?? "delivery",
35090
+ reason: stage?.reason ?? "Monitor delivered successfully."
35091
+ };
35092
+ }
35093
+ function statusGlyph(status) {
35094
+ if (status === "ok") return "\u2713";
35095
+ if (status === "pending") return "\u23F3";
35096
+ if (status === "healthy") return "\u25CB";
35097
+ return "\u2717";
35098
+ }
35099
+ function printExplainText(report) {
35100
+ console.log(`Monitor ${report.monitorId}`);
35101
+ for (const stage of report.stages) {
35102
+ console.log(`${statusGlyph(stage.status)} ${stage.label}: ${stage.reason}`);
35103
+ }
35104
+ console.log(
35105
+ `Verdict: ${report.verdict.status} at ${EXPLAIN_STAGE_LABELS2[report.verdict.stage]} - ${report.verdict.reason}`
35106
+ );
35107
+ }
35108
+ async function buildDaemonUnavailableReport(input) {
35109
+ const generatedAt = /* @__PURE__ */ new Date();
35110
+ const stages = [];
35111
+ const scan = await scanMonitors(input.monitorsDir);
35112
+ const parseError = scan.errors.find(
35113
+ (error2) => monitorIdFromFilePath2(error2.filePath) === input.monitorId
35114
+ );
35115
+ const monitor = scan.monitors.find(
35116
+ (candidate) => candidate.monitor.id === input.monitorId
35117
+ )?.monitor;
35118
+ if (parseError) {
35119
+ stages.push(
35120
+ explainStage2(
35121
+ "definition",
35122
+ "failure",
35123
+ `MONITOR.md failed to parse or validate: ${parseError.error}`,
35124
+ { filePath: parseError.filePath }
35125
+ )
35126
+ );
35127
+ } else if (!monitor) {
35128
+ stages.push(
35129
+ explainStage2(
35130
+ "definition",
35131
+ "failure",
35132
+ `Monitor "${input.monitorId}" was not found in ${input.monitorsDir}.`
35133
+ )
35134
+ );
35135
+ } else {
35136
+ const registry2 = new SourceRegistry();
35137
+ registerCoreSources(registry2);
35138
+ const sourceName = monitor.frontmatter.watch.type;
35139
+ const source6 = registry2.get(sourceName);
35140
+ const { type: _type, ...monitorWatchConfig } = monitor.frontmatter.watch;
35141
+ const scopeErrors = source6 ? validateScope(monitorWatchConfig, source6.scopeSchema) : [`Unknown source "${sourceName}".`];
35142
+ stages.push(
35143
+ scopeErrors.length === 0 ? explainStage2("definition", "ok", "Monitor definition is valid.", {
35144
+ filePath: monitor.filePath,
35145
+ sourceName
35146
+ }) : explainStage2(
35147
+ "definition",
35148
+ "failure",
35149
+ `Monitor definition is invalid: ${scopeErrors.join("; ")}`,
35150
+ { filePath: monitor.filePath, sourceName }
35151
+ )
35152
+ );
35153
+ }
35154
+ if (stages[0]?.status === "ok") {
35155
+ stages.push(
35156
+ explainStage2(
35157
+ "scheduling",
35158
+ "failure",
35159
+ `The daemon is not running or unreachable: ${input.errorMessage}`,
35160
+ { workspacePath: input.workspacePath }
35161
+ )
35162
+ );
35163
+ }
35164
+ return {
35165
+ monitorId: input.monitorId,
35166
+ generatedAt,
35167
+ ...monitor ? {
35168
+ monitor: {
35169
+ id: monitor.id,
35170
+ displayName: monitor.displayName,
35171
+ filePath: monitor.filePath,
35172
+ sourceName: monitor.frontmatter.watch.type,
35173
+ urgency: monitor.frontmatter.urgency
35174
+ }
35175
+ } : {},
35176
+ stages,
35177
+ verdict: explainVerdict2(stages),
35178
+ observations: [],
35179
+ events: [],
35180
+ projections: [],
35181
+ leadSessions: []
35182
+ };
35183
+ }
34518
35184
  function printJsonResult(monitorName, sourceName, baseline, observations) {
34519
35185
  console.log(
34520
35186
  JSON.stringify(
@@ -34598,13 +35264,13 @@ monitorTestCommand.command("test").description("Dry-run a monitor observation so
34598
35264
  new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")
34599
35265
  ).action(async (filePath2, options2) => {
34600
35266
  const json = options2.format === "json";
34601
- if (!(0, import_node_fs4.existsSync)(filePath2)) {
35267
+ if (!(0, import_node_fs5.existsSync)(filePath2)) {
34602
35268
  reportError(`Monitor file not found: ${filePath2}`, json);
34603
35269
  return;
34604
35270
  }
34605
35271
  let content;
34606
35272
  try {
34607
- content = (0, import_node_fs4.readFileSync)(filePath2, "utf-8");
35273
+ content = (0, import_node_fs5.readFileSync)(filePath2, "utf-8");
34608
35274
  } catch (err) {
34609
35275
  const msg = err instanceof Error ? err.message : String(err);
34610
35276
  reportError(`Cannot read monitor file: ${msg}`, json);
@@ -34660,6 +35326,55 @@ monitorTestCommand.command("test").description("Dry-run a monitor observation so
34660
35326
  reportError(`Observation failed: ${message}`, json);
34661
35327
  }
34662
35328
  });
35329
+ monitorTestCommand.command("explain").description("Explain where a monitor's signal currently stops").argument("<monitorId>", "Monitor id to diagnose").option(
35330
+ "--dir <path>",
35331
+ "Directory containing monitor definitions",
35332
+ ".claude/monitors"
35333
+ ).option("--workspace <path>", "Workspace path used by the daemon").option("--socket <path>", "Unix domain socket path for the daemon").option("--history-limit <n>", "Observation history rows to include", "10").option("--event-limit <n>", "Materialized event rows to include", "10").addOption(
35334
+ new Option("--format <format>", "Output format").choices(["text", "json"]).default("text")
35335
+ ).action(
35336
+ async (monitorId, options2) => {
35337
+ const json = options2.format === "json";
35338
+ const monitorsDir = import_node_path5.default.resolve(options2.dir);
35339
+ const workspacePath = import_node_path5.default.resolve(options2.workspace ?? process.cwd());
35340
+ const historyLimit = Number.parseInt(options2.historyLimit, 10);
35341
+ const eventLimit = Number.parseInt(options2.eventLimit, 10);
35342
+ try {
35343
+ const report = await explainMonitorClient(
35344
+ {
35345
+ monitorId,
35346
+ monitorsDir,
35347
+ workspacePath,
35348
+ ...Number.isFinite(historyLimit) && historyLimit > 0 ? { historyLimit } : {},
35349
+ ...Number.isFinite(eventLimit) && eventLimit > 0 ? { eventLimit } : {}
35350
+ },
35351
+ options2.socket
35352
+ );
35353
+ if (json) {
35354
+ console.log(JSON.stringify(report, null, 2));
35355
+ return;
35356
+ }
35357
+ printExplainText(report);
35358
+ } catch (err) {
35359
+ const message = err instanceof Error ? err.message : String(err);
35360
+ if (!(err instanceof DaemonConnectionError)) {
35361
+ reportError(`Explain failed: ${message}`, json);
35362
+ return;
35363
+ }
35364
+ const report = await buildDaemonUnavailableReport({
35365
+ monitorId,
35366
+ monitorsDir,
35367
+ workspacePath,
35368
+ errorMessage: message
35369
+ });
35370
+ if (json) {
35371
+ console.log(JSON.stringify(report, null, 2));
35372
+ return;
35373
+ }
35374
+ printExplainText(report);
35375
+ }
35376
+ }
35377
+ );
34663
35378
  monitorTestCommand.command("history").description(
34664
35379
  "Show recent observation outcomes per tick (triggered / suppressed / no-change / errored / rebaselined)"
34665
35380
  ).argument("[monitorId]", "Filter to a single monitor id").option("--socket <path>", "Unix domain socket path for the daemon").option("--limit <n>", "Maximum rows to return", "50").addOption(
@@ -34711,9 +35426,11 @@ sourceCommand.command("list").description("List installed observation sources").
34711
35426
  const output = sources.map((source6) => {
34712
35427
  const requiredFields = source6.scopeSchema["required"] ?? [];
34713
35428
  const properties = source6.scopeSchema["properties"] ?? {};
35429
+ const configFields = Object.keys(properties);
34714
35430
  return {
34715
35431
  name: source6.name,
34716
- scopeFields: Object.keys(properties),
35432
+ configFields,
35433
+ scopeFields: configFields,
34717
35434
  required: requiredFields
34718
35435
  };
34719
35436
  });
@@ -34729,7 +35446,7 @@ sourceCommand.command("list").description("List installed observation sources").
34729
35446
  const requiredFields = source6.scopeSchema["required"] ?? [];
34730
35447
  const properties = source6.scopeSchema["properties"] ?? {};
34731
35448
  console.log(` ${source6.name}`);
34732
- console.log(` Scope fields: ${Object.keys(properties).join(", ")}`);
35449
+ console.log(` Config fields: ${Object.keys(properties).join(", ")}`);
34733
35450
  console.log(` Required: ${requiredFields.join(", ") || "(none)"}`);
34734
35451
  console.log("");
34735
35452
  }
@@ -34817,6 +35534,11 @@ function shouldReap(s) {
34817
35534
 
34818
35535
  // src/commands/daemon.ts
34819
35536
  var DEFAULT_REAP_AFTER_MS = 5 * 60 * 1e3;
35537
+ function appendErroredLines(summary, errored) {
35538
+ if (errored.length === 0) return summary;
35539
+ const lines = errored.map((e) => ` ${e.monitorId}: ${e.message}`);
35540
+ return [summary, ...lines].join("\n");
35541
+ }
34820
35542
  async function runLoop(monitorsDir, workspacePath, pollMs, socketPath, reapAfterMs) {
34821
35543
  const runtime = createRuntime();
34822
35544
  let stopping = false;
@@ -34862,10 +35584,12 @@ async function runLoop(monitorsDir, workspacePath, pollMs, socketPath, reapAfter
34862
35584
  while (!isStoppingRequested()) {
34863
35585
  try {
34864
35586
  const result = await runtime.tick(monitorsDir, workspacePath);
34865
- if (result.emittedEventIds.length > 0) {
34866
- console.log(
34867
- `Emitted ${String(result.emittedEventIds.length)} event(s) from ${String(result.evaluatedMonitors.length)} monitor(s).`
35587
+ if (result.emittedEventIds.length > 0 || result.erroredObservations.length > 0) {
35588
+ const summary = appendErroredLines(
35589
+ `Emitted ${String(result.emittedEventIds.length)} event(s) from ${String(result.evaluatedMonitors.length)} monitor(s)${result.erroredObservations.length > 0 ? `, ${String(result.erroredObservations.length)} errored:` : "."}`,
35590
+ result.erroredObservations
34868
35591
  );
35592
+ console.log(summary);
34869
35593
  }
34870
35594
  } catch (error2) {
34871
35595
  const message = error2 instanceof Error ? error2.message : String(error2);
@@ -34931,9 +35655,9 @@ daemonCommand.command("once").description("Run one runtime observation cycle").a
34931
35655
  console.log(JSON.stringify(result, null, 2));
34932
35656
  return;
34933
35657
  }
34934
- console.log(
34935
- `Evaluated ${String(result.evaluatedMonitors.length)} monitor(s), emitted ${String(result.emittedEventIds.length)} event(s).`
34936
- );
35658
+ const erroredCount = result.erroredObservations.length;
35659
+ const base = `Evaluated ${String(result.evaluatedMonitors.length)} monitor(s), emitted ${String(result.emittedEventIds.length)} event(s)${erroredCount > 0 ? `, ${String(erroredCount)} errored:` : "."}`;
35660
+ console.log(appendErroredLines(base, result.erroredObservations));
34937
35661
  } catch (error2) {
34938
35662
  const message = error2 instanceof Error ? error2.message : String(error2);
34939
35663
  reportError(message, options2.format === "json");
@@ -35026,15 +35750,15 @@ daemonCommand.command("stop").description("Ask the local AgentMon daemon to stop
35026
35750
 
35027
35751
  // src/commands/session.ts
35028
35752
  init_cjs_shims();
35029
- var import_node_path8 = __toESM(require("path"), 1);
35753
+ var import_node_path9 = __toESM(require("path"), 1);
35030
35754
 
35031
35755
  // src/local-state.ts
35032
35756
  init_cjs_shims();
35033
- var import_node_fs5 = require("fs");
35034
- var import_node_path5 = __toESM(require("path"), 1);
35757
+ var import_node_fs6 = require("fs");
35758
+ var import_node_path6 = __toESM(require("path"), 1);
35035
35759
  var DEFAULT_REAP_AFTER_MS2 = 5 * 60 * 1e3;
35036
35760
  function filePath(workspacePath) {
35037
- return import_node_path5.default.join(workspacePath, ".claude", "agentmonitors.local.md");
35761
+ return import_node_path6.default.join(workspacePath, ".claude", "agentmonitors.local.md");
35038
35762
  }
35039
35763
  function parseFrontmatter(raw) {
35040
35764
  const lines = raw.split("\n");
@@ -35063,7 +35787,7 @@ function parseFrontmatter(raw) {
35063
35787
  function readLocalState(workspacePath) {
35064
35788
  let raw;
35065
35789
  try {
35066
- raw = (0, import_node_fs5.readFileSync)(filePath(workspacePath), "utf-8");
35790
+ raw = (0, import_node_fs6.readFileSync)(filePath(workspacePath), "utf-8");
35067
35791
  } catch {
35068
35792
  return { enabled: false };
35069
35793
  }
@@ -35084,7 +35808,7 @@ function readLocalState(workspacePath) {
35084
35808
  }
35085
35809
  function writeLocalState(workspacePath, state) {
35086
35810
  const target = filePath(workspacePath);
35087
- (0, import_node_fs5.mkdirSync)(import_node_path5.default.dirname(target), { recursive: true });
35811
+ (0, import_node_fs6.mkdirSync)(import_node_path6.default.dirname(target), { recursive: true });
35088
35812
  const lines = [
35089
35813
  "---",
35090
35814
  `enabled: ${String(state.enabled)}`,
@@ -35096,25 +35820,25 @@ function writeLocalState(workspacePath, state) {
35096
35820
  "> Local AgentMon coordination state. Gitignored; safe to delete (it is regenerated).",
35097
35821
  ""
35098
35822
  ];
35099
- (0, import_node_fs5.writeFileSync)(target, lines.join("\n"), "utf-8");
35823
+ (0, import_node_fs6.writeFileSync)(target, lines.join("\n"), "utf-8");
35100
35824
  }
35101
35825
 
35102
35826
  // src/workspace-paths.ts
35103
35827
  init_cjs_shims();
35104
35828
  var import_node_crypto2 = require("crypto");
35105
35829
  var import_node_os3 = __toESM(require("os"), 1);
35106
- var import_node_path6 = __toESM(require("path"), 1);
35830
+ var import_node_path7 = __toESM(require("path"), 1);
35107
35831
  function workspacePaths(workspacePath) {
35108
- const hash = (0, import_node_crypto2.createHash)("sha256").update(import_node_path6.default.resolve(workspacePath)).digest("hex").slice(0, 16);
35109
- const dataRoot = process.env["XDG_DATA_HOME"] ?? import_node_path6.default.join(import_node_os3.default.homedir(), ".local", "share");
35110
- const dir = import_node_path6.default.join(dataRoot, "agentmonitors", "workspaces", hash);
35832
+ const hash = (0, import_node_crypto2.createHash)("sha256").update(import_node_path7.default.resolve(workspacePath)).digest("hex").slice(0, 16);
35833
+ const dataRoot = process.env["XDG_DATA_HOME"] ?? import_node_path7.default.join(import_node_os3.default.homedir(), ".local", "share");
35834
+ const dir = import_node_path7.default.join(dataRoot, "agentmonitors", "workspaces", hash);
35111
35835
  return {
35112
35836
  dir,
35113
- db: import_node_path6.default.join(dir, "inbox.db"),
35837
+ db: import_node_path7.default.join(dir, "inbox.db"),
35114
35838
  // Keep the socket short: a 16-char hash under the data dir stays well under
35115
35839
  // the 100-char limit on most setups; resolveSocketPath's /tmp fallback still
35116
35840
  // applies if a deep home dir pushes it over.
35117
- socket: import_node_path6.default.join(dir, "agentmonitors.sock")
35841
+ socket: import_node_path7.default.join(dir, "agentmonitors.sock")
35118
35842
  };
35119
35843
  }
35120
35844
 
@@ -35122,15 +35846,15 @@ function workspacePaths(workspacePath) {
35122
35846
  init_cjs_shims();
35123
35847
  var import_node_child_process = require("child_process");
35124
35848
  var import_node_url3 = require("url");
35125
- var import_node_path7 = __toESM(require("path"), 1);
35849
+ var import_node_path8 = __toESM(require("path"), 1);
35126
35850
  function cliEntry() {
35127
35851
  const thisFile = (0, import_node_url3.fileURLToPath)(importMetaUrl);
35128
- const isBundle = import_node_path7.default.basename(import_node_path7.default.dirname(thisFile)) === "dist";
35852
+ const isBundle = import_node_path8.default.basename(import_node_path8.default.dirname(thisFile)) === "dist";
35129
35853
  if (isBundle) {
35130
35854
  return thisFile;
35131
35855
  }
35132
- const packageRoot = import_node_path7.default.resolve(import_node_path7.default.dirname(thisFile), "..");
35133
- return import_node_path7.default.join(packageRoot, "dist", "index.cjs");
35856
+ const packageRoot = import_node_path8.default.resolve(import_node_path8.default.dirname(thisFile), "..");
35857
+ return import_node_path8.default.join(packageRoot, "dist", "index.cjs");
35134
35858
  }
35135
35859
  function spawnDetachedDaemon(options2) {
35136
35860
  const args = [
@@ -35337,7 +36061,7 @@ sessionCommand.command("start").description(
35337
36061
  const paths = workspacePaths(workspacePath);
35338
36062
  const socket = resolveSocketPath(state.socket ?? paths.socket);
35339
36063
  const db = state.db ?? paths.db;
35340
- const monitorsDir = import_node_path8.default.join(workspacePath, ".claude", "monitors");
36064
+ const monitorsDir = import_node_path9.default.join(workspacePath, ".claude", "monitors");
35341
36065
  const BOOT_TIMEOUT_MS = 8e3;
35342
36066
  if (!await daemonAvailable(socket)) {
35343
36067
  spawnDetachedDaemon({
@@ -35771,10 +36495,10 @@ function assignProp(target, prop, value) {
35771
36495
  configurable: true
35772
36496
  });
35773
36497
  }
35774
- function getElementAtPath(obj, path12) {
35775
- if (!path12)
36498
+ function getElementAtPath(obj, path13) {
36499
+ if (!path13)
35776
36500
  return obj;
35777
- return path12.reduce((acc, key) => acc?.[key], obj);
36501
+ return path13.reduce((acc, key) => acc?.[key], obj);
35778
36502
  }
35779
36503
  function promiseAllObject(promisesObj) {
35780
36504
  const keys = Object.keys(promisesObj);
@@ -36094,11 +36818,11 @@ function aborted(x2, startIndex = 0) {
36094
36818
  }
36095
36819
  return false;
36096
36820
  }
36097
- function prefixIssues(path12, issues) {
36821
+ function prefixIssues(path13, issues) {
36098
36822
  return issues.map((iss) => {
36099
36823
  var _a2;
36100
36824
  (_a2 = iss).path ?? (_a2.path = []);
36101
- iss.path.unshift(path12);
36825
+ iss.path.unshift(path13);
36102
36826
  return iss;
36103
36827
  });
36104
36828
  }
@@ -43007,9 +43731,9 @@ async function runChannelServe(options2) {
43007
43731
  // src/index.ts
43008
43732
  function getVersion() {
43009
43733
  try {
43010
- const dir = (0, import_node_path9.dirname)((0, import_node_url4.fileURLToPath)(importMetaUrl));
43734
+ const dir = (0, import_node_path10.dirname)((0, import_node_url4.fileURLToPath)(importMetaUrl));
43011
43735
  const pkg = JSON.parse(
43012
- (0, import_node_fs6.readFileSync)((0, import_node_path9.join)(dir, "..", "package.json"), "utf8")
43736
+ (0, import_node_fs7.readFileSync)((0, import_node_path10.join)(dir, "..", "package.json"), "utf8")
43013
43737
  );
43014
43738
  return typeof pkg.version === "string" ? pkg.version : "0.0.0";
43015
43739
  } catch {