gossipcat 0.6.5 → 0.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -34010,8 +34010,8 @@ Keep it SHORT \u2014 under 30 lines. This is a working document, not a design do
34010
34010
  ]);
34011
34011
  const specContent = response.text || "";
34012
34012
  try {
34013
- const { mkdirSync: mkdirSync44, writeFileSync: writeFS } = require("fs");
34014
- mkdirSync44((0, import_path35.join)(this.projectRoot, ".gossip"), { recursive: true });
34013
+ const { mkdirSync: mkdirSync45, writeFileSync: writeFS } = require("fs");
34014
+ mkdirSync45((0, import_path35.join)(this.projectRoot, ".gossip"), { recursive: true });
34015
34015
  writeFS(specPath, specContent, "utf-8");
34016
34016
  } catch (err) {
34017
34017
  return { text: `Spec generated but failed to save: ${err.message}
@@ -35274,8 +35274,8 @@ message: Your question?
35274
35274
  }
35275
35275
  /** Start all worker agents (connect to relay) */
35276
35276
  async start() {
35277
- const { existsSync: existsSync73, readFileSync: readFileSync68 } = await import("fs");
35278
- const { join: join90 } = await import("path");
35277
+ const { existsSync: existsSync73, readFileSync: readFileSync69 } = await import("fs");
35278
+ const { join: join91 } = await import("path");
35279
35279
  for (const config2 of this.registry.getAll()) {
35280
35280
  if (config2.native) continue;
35281
35281
  if (this.workers.has(config2.id)) continue;
@@ -35293,8 +35293,8 @@ message: Your question?
35293
35293
  void 0,
35294
35294
  config2.key_ref
35295
35295
  );
35296
- const instructionsPath = join90(this.projectRoot, ".gossip", "agents", config2.id, "instructions.md");
35297
- const instructions = existsSync73(instructionsPath) ? readFileSync68(instructionsPath, "utf-8") : void 0;
35296
+ const instructionsPath = join91(this.projectRoot, ".gossip", "agents", config2.id, "instructions.md");
35297
+ const instructions = existsSync73(instructionsPath) ? readFileSync69(instructionsPath, "utf-8") : void 0;
35298
35298
  const enableWebSearch = config2.preset === "researcher" || config2.skills.includes("research");
35299
35299
  const worker = new WorkerAgent(config2.id, llm, this.relayUrl, ALL_TOOLS, instructions, enableWebSearch, this.relayApiKey, config2.maxToolTurns);
35300
35300
  await worker.start();
@@ -35491,8 +35491,8 @@ message: Your question?
35491
35491
  this.registry.register(config2);
35492
35492
  }
35493
35493
  async syncWorkers(keyProvider) {
35494
- const { existsSync: existsSync73, readFileSync: readFileSync68 } = await import("fs");
35495
- const { join: join90 } = await import("path");
35494
+ const { existsSync: existsSync73, readFileSync: readFileSync69 } = await import("fs");
35495
+ const { join: join91 } = await import("path");
35496
35496
  let added = 0;
35497
35497
  for (const ac of this.registry.getAll()) {
35498
35498
  if (ac.native) continue;
@@ -35509,8 +35509,8 @@ message: Your question?
35509
35509
  this.workers.delete(ac.id);
35510
35510
  }
35511
35511
  const llm = createProviderForAgent(ac.id, ac.provider, ac.model, key ?? void 0, ac.base_url, void 0, ac.key_ref);
35512
- const instructionsPath = join90(this.projectRoot, ".gossip", "agents", ac.id, "instructions.md");
35513
- const instructions = existsSync73(instructionsPath) ? readFileSync68(instructionsPath, "utf-8") : void 0;
35512
+ const instructionsPath = join91(this.projectRoot, ".gossip", "agents", ac.id, "instructions.md");
35513
+ const instructions = existsSync73(instructionsPath) ? readFileSync69(instructionsPath, "utf-8") : void 0;
35514
35514
  const enableWebSearch = ac.preset === "researcher" || ac.skills.includes("research");
35515
35515
  const worker = new WorkerAgent(ac.id, llm, this.relayUrl, ALL_TOOLS, instructions, enableWebSearch, this.relayApiKey, ac.maxToolTurns);
35516
35516
  await worker.start();
@@ -44211,18 +44211,163 @@ var init_api_chat = __esm({
44211
44211
  }
44212
44212
  });
44213
44213
 
44214
+ // packages/relay/src/dashboard/chat-store.ts
44215
+ function isMirrorFrameShaped(obj) {
44216
+ if (!obj || typeof obj !== "object") return false;
44217
+ const f = obj;
44218
+ return f.type === "mirror" && typeof f.chat_id === "string" && typeof f.role === "string" && typeof f.text === "string" && typeof f.ts === "string" && typeof f.id === "number";
44219
+ }
44220
+ var import_fs70, import_path78, CHAT_ID_RE, PERSIST_CAP, MAX_LINE_BYTES, NullChatStore, FileChatStore;
44221
+ var init_chat_store = __esm({
44222
+ "packages/relay/src/dashboard/chat-store.ts"() {
44223
+ "use strict";
44224
+ import_fs70 = require("fs");
44225
+ import_path78 = require("path");
44226
+ CHAT_ID_RE = /^[a-zA-Z0-9_-]{1,128}$/;
44227
+ PERSIST_CAP = 1e3;
44228
+ MAX_LINE_BYTES = 256 * 1024;
44229
+ NullChatStore = class {
44230
+ append(_chatId, _frame) {
44231
+ }
44232
+ load(_chatId, _max) {
44233
+ return [];
44234
+ }
44235
+ drop(_chatId) {
44236
+ }
44237
+ dispose() {
44238
+ }
44239
+ };
44240
+ FileChatStore = class {
44241
+ constructor(chatDir) {
44242
+ this.chatDir = chatDir;
44243
+ }
44244
+ chatDir;
44245
+ /**
44246
+ * Per-chat append counter. Used to gate maybeRetain() calls so we do not
44247
+ * read the file on every append (O(1) amortized instead of O(N)):
44248
+ * - First append for a chatId in this process: one-time retention check
44249
+ * (bounds a large pre-existing file from a prior run).
44250
+ * - Subsequent appends: only call maybeRetain every PERSIST_CAP writes.
44251
+ */
44252
+ appendCounters = /* @__PURE__ */ new Map();
44253
+ isValidChatId(chatId) {
44254
+ return CHAT_ID_RE.test(chatId);
44255
+ }
44256
+ filePath(chatId) {
44257
+ return (0, import_path78.join)(this.chatDir, `${chatId}.jsonl`);
44258
+ }
44259
+ append(chatId, frame) {
44260
+ if (!this.isValidChatId(chatId)) return;
44261
+ if (chatId.startsWith("_")) return;
44262
+ try {
44263
+ (0, import_fs70.mkdirSync)(this.chatDir, { recursive: true });
44264
+ const line = JSON.stringify(frame) + "\n";
44265
+ (0, import_fs70.appendFileSync)(this.filePath(chatId), line, "utf8");
44266
+ const prev = this.appendCounters.get(chatId);
44267
+ const count = (prev ?? 0) + 1;
44268
+ this.appendCounters.set(chatId, count);
44269
+ const isFirst = prev === void 0;
44270
+ if (isFirst || count % PERSIST_CAP === 0) {
44271
+ this.maybeRetain(chatId);
44272
+ }
44273
+ } catch (err) {
44274
+ console.warn(`[chat-store] append failed for chatId=${chatId}:`, err);
44275
+ }
44276
+ }
44277
+ maybeRetain(chatId) {
44278
+ if (chatId.startsWith("_")) return;
44279
+ try {
44280
+ const fp = this.filePath(chatId);
44281
+ const content = (0, import_fs70.readFileSync)(fp, "utf8");
44282
+ const lines = content.split("\n").filter((l) => l.length > 0);
44283
+ if (lines.length < 2 * PERSIST_CAP) return;
44284
+ const kept = lines.slice(lines.length - PERSIST_CAP);
44285
+ const tmp = fp + ".tmp";
44286
+ (0, import_fs70.writeFileSync)(tmp, kept.join("\n") + "\n", "utf8");
44287
+ (0, import_fs70.renameSync)(tmp, fp);
44288
+ } catch (err) {
44289
+ this.cleanupStaleTmp(chatId);
44290
+ console.warn(`[chat-store] retain failed for chatId=${chatId}`, err);
44291
+ }
44292
+ }
44293
+ /**
44294
+ * Best-effort removal of a stale `<chatId>.jsonl.tmp` left by a crash between
44295
+ * maybeRetain's writeFileSync and renameSync (the live .jsonl is always
44296
+ * intact; only the tmp orphans). Called on cold load() so orphans from a
44297
+ * prior process run are reclaimed when the chat is next hydrated, and from
44298
+ * maybeRetain's catch. ENOENT (the normal case) is silent.
44299
+ */
44300
+ cleanupStaleTmp(chatId) {
44301
+ try {
44302
+ (0, import_fs70.unlinkSync)(this.filePath(chatId) + ".tmp");
44303
+ } catch (err) {
44304
+ if (err?.code !== "ENOENT") {
44305
+ console.warn(`[chat-store] stale .tmp cleanup failed for chatId=${chatId}:`, err);
44306
+ }
44307
+ }
44308
+ }
44309
+ load(chatId, max) {
44310
+ if (!this.isValidChatId(chatId)) return [];
44311
+ this.cleanupStaleTmp(chatId);
44312
+ let content;
44313
+ try {
44314
+ content = (0, import_fs70.readFileSync)(this.filePath(chatId), "utf8");
44315
+ } catch (err) {
44316
+ if (err?.code === "ENOENT") return [];
44317
+ console.warn(`[chat-store] load failed for chatId=${chatId}:`, err);
44318
+ return [];
44319
+ }
44320
+ const lines = content.split("\n").filter((l) => l.length > 0);
44321
+ const frames = [];
44322
+ for (const line of lines) {
44323
+ if (line.length > MAX_LINE_BYTES) {
44324
+ console.warn(`[chat-store] skipping oversized line (${line.length} bytes) for chatId=${chatId}`);
44325
+ continue;
44326
+ }
44327
+ try {
44328
+ const parsed = JSON.parse(line);
44329
+ if (isMirrorFrameShaped(parsed)) {
44330
+ frames.push(parsed);
44331
+ } else {
44332
+ console.warn(`[chat-store] skipping line with valid JSON but wrong MirrorFrame shape for chatId=${chatId}`);
44333
+ }
44334
+ } catch {
44335
+ console.warn(`[chat-store] skipping unparseable line for chatId=${chatId}`);
44336
+ }
44337
+ }
44338
+ if (frames.length <= max) return frames;
44339
+ return frames.slice(frames.length - max);
44340
+ }
44341
+ drop(chatId) {
44342
+ if (!this.isValidChatId(chatId)) return;
44343
+ try {
44344
+ (0, import_fs70.unlinkSync)(this.filePath(chatId));
44345
+ } catch (err) {
44346
+ if (err?.code !== "ENOENT") {
44347
+ console.warn(`[chat-store] drop failed for chatId=${chatId}:`, err);
44348
+ }
44349
+ }
44350
+ }
44351
+ dispose() {
44352
+ }
44353
+ };
44354
+ }
44355
+ });
44356
+
44214
44357
  // packages/relay/src/dashboard/api-mirror-events.ts
44215
44358
  var MIRROR_RING_MAX, MIRROR_RING_TTL_MS, MIRROR_SWEEP_INTERVAL_MS, MirrorEventStore;
44216
44359
  var init_api_mirror_events = __esm({
44217
44360
  "packages/relay/src/dashboard/api-mirror-events.ts"() {
44218
44361
  "use strict";
44362
+ init_chat_store();
44219
44363
  MIRROR_RING_MAX = 100;
44220
44364
  MIRROR_RING_TTL_MS = 2 * 60 * 60 * 1e3;
44221
44365
  MIRROR_SWEEP_INTERVAL_MS = 5 * 60 * 1e3;
44222
44366
  MirrorEventStore = class {
44223
- constructor(ringMax = MIRROR_RING_MAX, ttlMs = MIRROR_RING_TTL_MS, sweepIntervalMs = MIRROR_SWEEP_INTERVAL_MS) {
44367
+ constructor(ringMax = MIRROR_RING_MAX, ttlMs = MIRROR_RING_TTL_MS, sweepIntervalMs = MIRROR_SWEEP_INTERVAL_MS, store = new NullChatStore()) {
44224
44368
  this.ringMax = ringMax;
44225
44369
  this.ttlMs = ttlMs;
44370
+ this.store = store;
44226
44371
  if (sweepIntervalMs > 0) {
44227
44372
  this.sweepTimer = setInterval(() => this.sweep(Date.now()), sweepIntervalMs);
44228
44373
  this.sweepTimer.unref?.();
@@ -44232,18 +44377,44 @@ var init_api_mirror_events = __esm({
44232
44377
  ttlMs;
44233
44378
  rings = /* @__PURE__ */ new Map();
44234
44379
  sweepTimer = null;
44380
+ store;
44381
+ /**
44382
+ * Allocate or hydrate a ring for a chat_id. On first access when the ring is
44383
+ * not in memory, loads persisted frames from the ChatStore so post-restart
44384
+ * reads see durable history and the id sequence continues (preventing false
44385
+ * restart-sentinels). Returns null when neither an in-memory ring exists NOR
44386
+ * any persisted frames were found — callers treat null as "no data."
44387
+ *
44388
+ * Renamed from `ensureRing` to `getOrHydrate` to reflect the new semantics.
44389
+ * push() uses a variant that always creates the ring (even if empty), since a
44390
+ * push always establishes a new ring. replaySlice/highestId use this
44391
+ * conditional form so they don't pollute ringCount() with empty rings on miss.
44392
+ */
44393
+ getOrHydrate(chatId, now) {
44394
+ const existing = this.rings.get(chatId);
44395
+ if (existing) return existing;
44396
+ const persisted = this.store.load(chatId, this.ringMax);
44397
+ if (persisted.length === 0) return null;
44398
+ const ring2 = {
44399
+ frames: persisted.slice(),
44400
+ // Continue the id sequence from the highest persisted id so push() does
44401
+ // not reset to 1 after a restart (preventing the false restart-sentinel).
44402
+ nextId: persisted[persisted.length - 1].id,
44403
+ touchedAt: now
44404
+ };
44405
+ this.rings.set(chatId, ring2);
44406
+ return ring2;
44407
+ }
44235
44408
  /**
44236
- * Allocate the next id for a chat_id WITHOUT pushing a frame. The hub stamps
44237
- * id + ts server-side, so it asks the store to mint the id, builds the frame,
44238
- * then pushes it. Keeping mint+push as one call (push) avoids a torn counter,
44239
- * so this is internal — callers use push().
44409
+ * Ensure a ring exists for a chat_id, creating it from persisted data or
44410
+ * fresh. Always returns a ring (never null). Used only by push() since a
44411
+ * push always establishes a ring.
44240
44412
  */
44241
44413
  ensureRing(chatId, now) {
44242
- let ring2 = this.rings.get(chatId);
44243
- if (!ring2) {
44244
- ring2 = { frames: [], nextId: 0, touchedAt: now };
44245
- this.rings.set(chatId, ring2);
44246
- }
44414
+ const hydrated = this.getOrHydrate(chatId, now);
44415
+ if (hydrated) return hydrated;
44416
+ const ring2 = { frames: [], nextId: 0, touchedAt: now };
44417
+ this.rings.set(chatId, ring2);
44247
44418
  return ring2;
44248
44419
  }
44249
44420
  /**
@@ -44266,6 +44437,7 @@ var init_api_mirror_events = __esm({
44266
44437
  ring2.frames.push(frame);
44267
44438
  while (ring2.frames.length > this.ringMax) ring2.frames.shift();
44268
44439
  ring2.touchedAt = now;
44440
+ this.store.append(chatId, frame);
44269
44441
  return frame;
44270
44442
  }
44271
44443
  /**
@@ -44275,7 +44447,7 @@ var init_api_mirror_events = __esm({
44275
44447
  * actively-observed stream isn't swept out from under a reconnecting client.
44276
44448
  */
44277
44449
  replaySlice(chatId, lastId, now = Date.now()) {
44278
- const ring2 = this.rings.get(chatId);
44450
+ const ring2 = this.getOrHydrate(chatId, now);
44279
44451
  if (!ring2) return [];
44280
44452
  ring2.touchedAt = now;
44281
44453
  if (lastId <= 0) return ring2.frames.slice();
@@ -44307,6 +44479,7 @@ var init_api_mirror_events = __esm({
44307
44479
  }
44308
44480
  const carried = src.frames.slice();
44309
44481
  this.rings.delete(fromChatId);
44482
+ this.store.drop(fromChatId);
44310
44483
  const transferred = [];
44311
44484
  for (const f of carried) {
44312
44485
  transferred.push(this.push(toChatId, f.role, f.text, now));
@@ -44321,7 +44494,7 @@ var init_api_mirror_events = __esm({
44321
44494
  * sentinel in api-bridge.handleStream).
44322
44495
  */
44323
44496
  highestId(chatId) {
44324
- const ring2 = this.rings.get(chatId);
44497
+ const ring2 = this.getOrHydrate(chatId, Date.now());
44325
44498
  if (!ring2 || ring2.frames.length === 0) return 0;
44326
44499
  return ring2.frames[ring2.frames.length - 1].id;
44327
44500
  }
@@ -44433,15 +44606,16 @@ function isMirrorRole(v) {
44433
44606
  function validateChatId(raw) {
44434
44607
  if (typeof raw !== "string") return null;
44435
44608
  const v = raw.trim();
44436
- if (!CHAT_ID_RE.test(v)) return null;
44609
+ if (!CHAT_ID_RE2.test(v)) return null;
44437
44610
  return v;
44438
44611
  }
44439
- var import_crypto26, ASK_MAX_QUESTIONS, ASK_MAX_OPTIONS, ASK_MAX_HEADER, ASK_MAX_QUESTION, ASK_MAX_LABEL, ASK_MAX_DESCRIPTION, ASK_MAX_OTHER, MIRROR_ROLES, MIRROR_MAX_TEXT, MIRROR_MAX_FRAMES, CHAT_ID_RE, MAX_MESSAGE_LENGTH, MAX_BRIDGE_CLIENTS, KEEPALIVE_MS2, BACKPRESSURE_EVICT_THRESHOLD2, BridgeHub;
44612
+ var import_crypto26, ASK_MAX_QUESTIONS, ASK_MAX_OPTIONS, ASK_MAX_HEADER, ASK_MAX_QUESTION, ASK_MAX_LABEL, ASK_MAX_DESCRIPTION, ASK_MAX_OTHER, MIRROR_ROLES, MIRROR_MAX_TEXT, MIRROR_MAX_FRAMES, CHAT_ID_RE2, MAX_MESSAGE_LENGTH, MAX_BRIDGE_CLIENTS, KEEPALIVE_MS2, BACKPRESSURE_EVICT_THRESHOLD2, BridgeHub;
44440
44613
  var init_api_bridge = __esm({
44441
44614
  "packages/relay/src/dashboard/api-bridge.ts"() {
44442
44615
  "use strict";
44443
44616
  import_crypto26 = require("crypto");
44444
44617
  init_api_mirror_events();
44618
+ init_chat_store();
44445
44619
  ASK_MAX_QUESTIONS = 4;
44446
44620
  ASK_MAX_OPTIONS = 8;
44447
44621
  ASK_MAX_HEADER = 120;
@@ -44452,7 +44626,7 @@ var init_api_bridge = __esm({
44452
44626
  MIRROR_ROLES = ["user", "assistant", "activity"];
44453
44627
  MIRROR_MAX_TEXT = 2 * 1024;
44454
44628
  MIRROR_MAX_FRAMES = 64;
44455
- CHAT_ID_RE = /^[a-zA-Z0-9_-]{1,128}$/;
44629
+ CHAT_ID_RE2 = /^[a-zA-Z0-9_-]{1,128}$/;
44456
44630
  MAX_MESSAGE_LENGTH = 32 * 1024;
44457
44631
  MAX_BRIDGE_CLIENTS = 20;
44458
44632
  KEEPALIVE_MS2 = 25e3;
@@ -44507,7 +44681,8 @@ var init_api_bridge = __esm({
44507
44681
  // through here. Established only from a validated dashboard inbound POST.
44508
44682
  sessionToChatId = /* @__PURE__ */ new Map();
44509
44683
  // Per-chat_id mirror rings (bounded FIFO + TTL + proactive sweep).
44510
- mirror = new MirrorEventStore();
44684
+ // Constructed with a FileChatStore when chatDir is provided, else NullChatStore.
44685
+ mirror;
44511
44686
  // PROVISIONAL buffer (P1#5 fallback): a purely-terminal mirror POST with no
44512
44687
  // resolvable chat_id (no body chat_id, no known session mapping) is buffered
44513
44688
  // under a provisional id so a later observer can backfill it — OR dropped,
@@ -44522,6 +44697,10 @@ var init_api_bridge = __esm({
44522
44697
  dropUnresolvedMirror = false;
44523
44698
  // session→chat_id entries share the same 2h TTL ceiling as knownChatIds.
44524
44699
  static MAX_SESSION_MAPPINGS = 64;
44700
+ constructor(opts = {}) {
44701
+ const store = opts.chatDir ? new FileChatStore(opts.chatDir) : new NullChatStore();
44702
+ this.mirror = new MirrorEventStore(MIRROR_RING_MAX, MIRROR_RING_TTL_MS, void 0, store);
44703
+ }
44525
44704
  /** Register (or clear) the in-process inbound sink. Called by RelayServer. */
44526
44705
  registerSink(fn) {
44527
44706
  this.sink = fn;
@@ -45373,15 +45552,15 @@ function normalizeLegacyDegradedFields(report) {
45373
45552
  }
45374
45553
  function resolveDashboardRoot(projectRoot) {
45375
45554
  const candidates = [
45376
- (0, import_path78.resolve)(__dirname, "..", "dist-dashboard"),
45555
+ (0, import_path79.resolve)(__dirname, "..", "dist-dashboard"),
45377
45556
  // bundled: dist-mcp/mcp-server.js → ../dist-dashboard
45378
- (0, import_path78.resolve)(__dirname, "..", "..", "..", "..", "dist-dashboard"),
45557
+ (0, import_path79.resolve)(__dirname, "..", "..", "..", "..", "dist-dashboard"),
45379
45558
  // tsc dev: packages/relay/dist/dashboard → repo-root
45380
- (0, import_path78.join)(projectRoot, "dist-dashboard")
45559
+ (0, import_path79.join)(projectRoot, "dist-dashboard")
45381
45560
  // legacy dev fallback (git-clone running from repo root)
45382
45561
  ];
45383
45562
  for (const p of candidates) {
45384
- if ((0, import_fs70.existsSync)(p)) return p;
45563
+ if ((0, import_fs71.existsSync)(p)) return p;
45385
45564
  }
45386
45565
  return null;
45387
45566
  }
@@ -45408,7 +45587,7 @@ function readBody(req, maxBytes = MAX_BODY_SIZE) {
45408
45587
  });
45409
45588
  });
45410
45589
  }
45411
- var import_fs70, import_path78, import_crypto28, AUTH_MAX_ATTEMPTS, AUTH_LOCKOUT_MS, AUTH_ATTEMPT_TTL_MS, AUTH_ATTEMPTS_HARD_CAP, CHAT_MAX_BODY, BRIDGE_MAX_BODY, MIRROR_MAX_BODY, MIRROR_MIN_INTERVAL_MS, MIRROR_LAST_TURN_HARD_CAP, CHAT_MIN_INTERVAL_MS, DashboardRouter, MAX_BODY_SIZE;
45590
+ var import_fs71, import_path79, import_crypto28, AUTH_MAX_ATTEMPTS, AUTH_LOCKOUT_MS, AUTH_ATTEMPT_TTL_MS, AUTH_ATTEMPTS_HARD_CAP, CHAT_MAX_BODY, BRIDGE_MAX_BODY, MIRROR_MAX_BODY, MIRROR_MIN_INTERVAL_MS, MIRROR_LAST_TURN_HARD_CAP, CHAT_MIN_INTERVAL_MS, DashboardRouter, MAX_BODY_SIZE;
45412
45591
  var init_routes = __esm({
45413
45592
  "packages/relay/src/dashboard/routes.ts"() {
45414
45593
  "use strict";
@@ -45436,8 +45615,8 @@ var init_routes = __esm({
45436
45615
  init_api_bridge();
45437
45616
  init_chat_session_store();
45438
45617
  init_src4();
45439
- import_fs70 = require("fs");
45440
- import_path78 = require("path");
45618
+ import_fs71 = require("fs");
45619
+ import_path79 = require("path");
45441
45620
  import_crypto28 = require("crypto");
45442
45621
  AUTH_MAX_ATTEMPTS = 10;
45443
45622
  AUTH_LOCKOUT_MS = 6e4;
@@ -45455,6 +45634,7 @@ var init_routes = __esm({
45455
45634
  this.projectRoot = projectRoot;
45456
45635
  this.ctx = ctx2;
45457
45636
  this.dashboardRoot = resolveDashboardRoot(projectRoot);
45637
+ this.bridge = new BridgeHub({ chatDir: (0, import_path79.join)(projectRoot, ".gossip", "chat") });
45458
45638
  }
45459
45639
  auth;
45460
45640
  projectRoot;
@@ -45476,7 +45656,9 @@ var init_routes = __esm({
45476
45656
  // Dashboard ⇄ live-CC bridge transport (P1). Owns the outbound SSE client set
45477
45657
  // and the in-process inbound sink the MCP server registers. Distinct from the
45478
45658
  // dormant chatbot (consensus f14 — no dual-brain; /api/chat stays dormant).
45479
- bridge = new BridgeHub();
45659
+ // Constructed in the constructor body (not as a field initializer) so it can
45660
+ // receive the chatDir option derived from projectRoot.
45661
+ bridge;
45480
45662
  /**
45481
45663
  * Inject (or clear) the read-only chatbot agent. Called by the app layer
45482
45664
  * after boot via RelayServer.setChatbot. Passing null disables chat with a
@@ -45965,13 +46147,13 @@ var init_routes = __esm({
45965
46147
  res.end("Dashboard assets not found. Reinstall gossipcat or rebuild from source.");
45966
46148
  return true;
45967
46149
  }
45968
- const htmlPath = (0, import_path78.join)(this.dashboardRoot, "index.html");
45969
- if (!(0, import_fs70.existsSync)(htmlPath)) {
46150
+ const htmlPath = (0, import_path79.join)(this.dashboardRoot, "index.html");
46151
+ if (!(0, import_fs71.existsSync)(htmlPath)) {
45970
46152
  res.writeHead(503, { "Content-Type": "text/plain" });
45971
46153
  res.end(`Dashboard index.html missing at ${this.dashboardRoot}. Reinstall gossipcat.`);
45972
46154
  return true;
45973
46155
  }
45974
- const html = (0, import_fs70.readFileSync)(htmlPath, "utf-8");
46156
+ const html = (0, import_fs71.readFileSync)(htmlPath, "utf-8");
45975
46157
  res.writeHead(200, {
45976
46158
  "Content-Type": "text/html; charset=utf-8",
45977
46159
  "Cache-Control": "no-cache"
@@ -46000,16 +46182,16 @@ var init_routes = __esm({
46000
46182
  const ext = "." + (relativePath.split(".").pop() || "");
46001
46183
  const mime = MIME[ext];
46002
46184
  if (!mime) return false;
46003
- const filePath = (0, import_path78.join)(this.dashboardRoot, relativePath);
46185
+ const filePath = (0, import_path79.join)(this.dashboardRoot, relativePath);
46004
46186
  try {
46005
- const realFile = (0, import_fs70.realpathSync)(filePath);
46006
- const realBase = (0, import_fs70.realpathSync)(this.dashboardRoot);
46187
+ const realFile = (0, import_fs71.realpathSync)(filePath);
46188
+ const realBase = (0, import_fs71.realpathSync)(this.dashboardRoot);
46007
46189
  if (!realFile.startsWith(realBase + "/")) {
46008
46190
  res.writeHead(404);
46009
46191
  res.end();
46010
46192
  return true;
46011
46193
  }
46012
- const data = (0, import_fs70.readFileSync)(realFile);
46194
+ const data = (0, import_fs71.readFileSync)(realFile);
46013
46195
  res.writeHead(200, { "Content-Type": mime, "Cache-Control": "public, max-age=86400" });
46014
46196
  res.end(data);
46015
46197
  return true;
@@ -46024,8 +46206,8 @@ var init_routes = __esm({
46024
46206
  return match ? match[1] : null;
46025
46207
  }
46026
46208
  getConsensusReports(page = 1, pageSize = 5) {
46027
- const { readdirSync: readdirSync22, readFileSync: readFileSync68, existsSync: existsSync73 } = require("fs");
46028
- const reportsDir = (0, import_path78.join)(this.projectRoot, ".gossip", "consensus-reports");
46209
+ const { readdirSync: readdirSync22, readFileSync: readFileSync69, existsSync: existsSync73 } = require("fs");
46210
+ const reportsDir = (0, import_path79.join)(this.projectRoot, ".gossip", "consensus-reports");
46029
46211
  let retractedConsensusIds = [];
46030
46212
  let roundRetractions = [];
46031
46213
  try {
@@ -46040,8 +46222,8 @@ var init_routes = __esm({
46040
46222
  const { statSync: statSync36 } = require("fs");
46041
46223
  const allFiles = readdirSync22(reportsDir).filter((f) => f.endsWith(".json")).sort((a, b) => {
46042
46224
  try {
46043
- const aTime = statSync36((0, import_path78.join)(reportsDir, a)).mtimeMs;
46044
- const bTime = statSync36((0, import_path78.join)(reportsDir, b)).mtimeMs;
46225
+ const aTime = statSync36((0, import_path79.join)(reportsDir, a)).mtimeMs;
46226
+ const bTime = statSync36((0, import_path79.join)(reportsDir, b)).mtimeMs;
46045
46227
  return bTime - aTime;
46046
46228
  } catch {
46047
46229
  return 0;
@@ -46052,13 +46234,13 @@ var init_routes = __esm({
46052
46234
  const clampedPage = Math.max(page, 1);
46053
46235
  const start = (clampedPage - 1) * clampedPageSize;
46054
46236
  const files = allFiles.slice(start, start + clampedPageSize);
46055
- const realReportsDir = (0, import_fs70.realpathSync)(reportsDir);
46237
+ const realReportsDir = (0, import_fs71.realpathSync)(reportsDir);
46056
46238
  const reports = files.map((f) => {
46057
46239
  try {
46058
- const filePath = (0, import_path78.join)(reportsDir, f);
46059
- const realFile = (0, import_fs70.realpathSync)(filePath);
46240
+ const filePath = (0, import_path79.join)(reportsDir, f);
46241
+ const realFile = (0, import_fs71.realpathSync)(filePath);
46060
46242
  if (!realFile.startsWith(realReportsDir + "/")) return null;
46061
- return normalizeLegacyDegradedFields(JSON.parse(readFileSync68(realFile, "utf-8")));
46243
+ return normalizeLegacyDegradedFields(JSON.parse(readFileSync69(realFile, "utf-8")));
46062
46244
  } catch {
46063
46245
  return null;
46064
46246
  }
@@ -46069,29 +46251,29 @@ var init_routes = __esm({
46069
46251
  }
46070
46252
  }
46071
46253
  archiveFindings() {
46072
- const { readdirSync: readdirSync22, readFileSync: readFileSync68, renameSync: renameSync17, writeFileSync: writeFileSync37, mkdirSync: mkdirSync44, existsSync: existsSync73 } = require("fs");
46073
- const reportsDir = (0, import_path78.join)(this.projectRoot, ".gossip", "consensus-reports");
46074
- const archiveDir = (0, import_path78.join)(this.projectRoot, ".gossip", "consensus-reports-archive");
46254
+ const { readdirSync: readdirSync22, readFileSync: readFileSync69, renameSync: renameSync18, writeFileSync: writeFileSync38, mkdirSync: mkdirSync45, existsSync: existsSync73 } = require("fs");
46255
+ const reportsDir = (0, import_path79.join)(this.projectRoot, ".gossip", "consensus-reports");
46256
+ const archiveDir = (0, import_path79.join)(this.projectRoot, ".gossip", "consensus-reports-archive");
46075
46257
  let archived = 0;
46076
46258
  if (existsSync73(reportsDir)) {
46077
46259
  const files = readdirSync22(reportsDir).filter((f) => f.endsWith(".json")).sort().reverse();
46078
46260
  if (files.length > 5) {
46079
- mkdirSync44(archiveDir, { recursive: true });
46261
+ mkdirSync45(archiveDir, { recursive: true });
46080
46262
  const toArchive = files.slice(5);
46081
46263
  for (const f of toArchive) {
46082
46264
  try {
46083
- renameSync17((0, import_path78.join)(reportsDir, f), (0, import_path78.join)(archiveDir, f));
46265
+ renameSync18((0, import_path79.join)(reportsDir, f), (0, import_path79.join)(archiveDir, f));
46084
46266
  archived++;
46085
46267
  } catch {
46086
46268
  }
46087
46269
  }
46088
46270
  }
46089
46271
  }
46090
- const findingsPath = (0, import_path78.join)(this.projectRoot, ".gossip", "implementation-findings.jsonl");
46272
+ const findingsPath = (0, import_path79.join)(this.projectRoot, ".gossip", "implementation-findings.jsonl");
46091
46273
  let findingsCleared = 0;
46092
46274
  if (existsSync73(findingsPath)) {
46093
46275
  try {
46094
- const lines = readFileSync68(findingsPath, "utf-8").trim().split("\n").filter(Boolean);
46276
+ const lines = readFileSync69(findingsPath, "utf-8").trim().split("\n").filter(Boolean);
46095
46277
  const kept = lines.filter((line) => {
46096
46278
  try {
46097
46279
  const entry = JSON.parse(line);
@@ -46104,7 +46286,7 @@ var init_routes = __esm({
46104
46286
  return true;
46105
46287
  }
46106
46288
  });
46107
- writeFileSync37(findingsPath, kept.join("\n") + (kept.length > 0 ? "\n" : ""));
46289
+ writeFileSync38(findingsPath, kept.join("\n") + (kept.length > 0 ? "\n" : ""));
46108
46290
  } catch {
46109
46291
  }
46110
46292
  }
@@ -46121,14 +46303,14 @@ var init_routes = __esm({
46121
46303
  });
46122
46304
 
46123
46305
  // packages/relay/src/dashboard/ws.ts
46124
- var import_fs71, import_fs72, import_path79, DashboardWs;
46306
+ var import_fs72, import_fs73, import_path80, DashboardWs;
46125
46307
  var init_ws = __esm({
46126
46308
  "packages/relay/src/dashboard/ws.ts"() {
46127
46309
  "use strict";
46128
46310
  init_wrapper();
46129
- import_fs71 = require("fs");
46130
46311
  import_fs72 = require("fs");
46131
- import_path79 = require("path");
46312
+ import_fs73 = require("fs");
46313
+ import_path80 = require("path");
46132
46314
  DashboardWs = class {
46133
46315
  clients = /* @__PURE__ */ new Set();
46134
46316
  logWatcher = null;
@@ -46153,15 +46335,15 @@ var init_ws = __esm({
46153
46335
  /** Start watching mcp.log for new lines and broadcasting them to connected clients. */
46154
46336
  startLogWatcher(projectRoot) {
46155
46337
  this.stopLogWatcher();
46156
- this.logPath = (0, import_path79.join)(projectRoot, ".gossip", "mcp.log");
46157
- if (!(0, import_fs71.existsSync)(this.logPath)) return;
46338
+ this.logPath = (0, import_path80.join)(projectRoot, ".gossip", "mcp.log");
46339
+ if (!(0, import_fs72.existsSync)(this.logPath)) return;
46158
46340
  try {
46159
- this.logOffset = (0, import_fs71.statSync)(this.logPath).size;
46341
+ this.logOffset = (0, import_fs72.statSync)(this.logPath).size;
46160
46342
  } catch {
46161
46343
  this.logOffset = 0;
46162
46344
  }
46163
46345
  try {
46164
- this.logWatcher = (0, import_fs72.watch)(this.logPath, () => {
46346
+ this.logWatcher = (0, import_fs73.watch)(this.logPath, () => {
46165
46347
  if (this.clients.size === 0) return;
46166
46348
  this.readNewLines();
46167
46349
  });
@@ -46178,7 +46360,7 @@ var init_ws = __esm({
46178
46360
  if (this.logReading) return;
46179
46361
  this.logReading = true;
46180
46362
  try {
46181
- const currentSize = (0, import_fs71.statSync)(this.logPath).size;
46363
+ const currentSize = (0, import_fs72.statSync)(this.logPath).size;
46182
46364
  if (currentSize < this.logOffset) {
46183
46365
  this.logOffset = 0;
46184
46366
  this.logCarry = "";
@@ -46191,7 +46373,7 @@ var init_ws = __esm({
46191
46373
  const readFrom = capped ? (this.logCarry = "", this.logCapped = true, currentSize - 65536) : (this.logCapped = false, this.logOffset);
46192
46374
  let stream;
46193
46375
  try {
46194
- stream = (0, import_fs71.createReadStream)(this.logPath, { start: readFrom, end: currentSize - 1 });
46376
+ stream = (0, import_fs72.createReadStream)(this.logPath, { start: readFrom, end: currentSize - 1 });
46195
46377
  } catch {
46196
46378
  this.logReading = false;
46197
46379
  return;
@@ -47039,14 +47221,14 @@ __export(sandbox_exports, {
47039
47221
  });
47040
47222
  function rotateIfNeeded2(filePath, maxBytes) {
47041
47223
  try {
47042
- const st = (0, import_fs73.statSync)(filePath);
47224
+ const st = (0, import_fs74.statSync)(filePath);
47043
47225
  if (st.size < maxBytes) return;
47044
47226
  const rotated = filePath + ".1";
47045
47227
  try {
47046
- if ((0, import_fs73.existsSync)(rotated)) (0, import_fs73.unlinkSync)(rotated);
47228
+ if ((0, import_fs74.existsSync)(rotated)) (0, import_fs74.unlinkSync)(rotated);
47047
47229
  } catch {
47048
47230
  }
47049
- (0, import_fs73.renameSync)(filePath, rotated);
47231
+ (0, import_fs74.renameSync)(filePath, rotated);
47050
47232
  } catch {
47051
47233
  }
47052
47234
  }
@@ -47119,9 +47301,9 @@ Tests passing is NOT sufficient verification \u2014 the 2026-04-22 incident had
47119
47301
  }
47120
47302
  function readSandboxMode(projectRoot) {
47121
47303
  try {
47122
- const p = (0, import_path80.join)(projectRoot, ".gossip", "config.json");
47123
- if (!(0, import_fs73.existsSync)(p)) return "warn";
47124
- const raw = JSON.parse((0, import_fs73.readFileSync)(p, "utf-8"));
47304
+ const p = (0, import_path81.join)(projectRoot, ".gossip", "config.json");
47305
+ if (!(0, import_fs74.existsSync)(p)) return "warn";
47306
+ const raw = JSON.parse((0, import_fs74.readFileSync)(p, "utf-8"));
47125
47307
  const mode = raw?.sandboxEnforcement;
47126
47308
  if (mode === "off" || mode === "warn" || mode === "block") return mode;
47127
47309
  return "warn";
@@ -47131,8 +47313,8 @@ function readSandboxMode(projectRoot) {
47131
47313
  }
47132
47314
  function recordDispatchMetadata(projectRoot, meta3) {
47133
47315
  try {
47134
- const dir = (0, import_path80.join)(projectRoot, ".gossip");
47135
- (0, import_fs73.mkdirSync)(dir, { recursive: true });
47316
+ const dir = (0, import_path81.join)(projectRoot, ".gossip");
47317
+ (0, import_fs74.mkdirSync)(dir, { recursive: true });
47136
47318
  const snapshotted = { ...meta3 };
47137
47319
  if (meta3.writeMode === "scoped" || meta3.writeMode === "worktree") {
47138
47320
  try {
@@ -47150,15 +47332,15 @@ function recordDispatchMetadata(projectRoot, meta3) {
47150
47332
  } catch {
47151
47333
  }
47152
47334
  }
47153
- (0, import_fs73.appendFileSync)((0, import_path80.join)(dir, METADATA_FILE), JSON.stringify(snapshotted) + "\n");
47335
+ (0, import_fs74.appendFileSync)((0, import_path81.join)(dir, METADATA_FILE), JSON.stringify(snapshotted) + "\n");
47154
47336
  } catch {
47155
47337
  }
47156
47338
  }
47157
47339
  function updateDispatchMetadata(projectRoot, taskId, patch) {
47158
47340
  try {
47159
- const p = (0, import_path80.join)(projectRoot, ".gossip", METADATA_FILE);
47160
- if (!(0, import_fs73.existsSync)(p)) return false;
47161
- const raw = (0, import_fs73.readFileSync)(p, "utf-8");
47341
+ const p = (0, import_path81.join)(projectRoot, ".gossip", METADATA_FILE);
47342
+ if (!(0, import_fs74.existsSync)(p)) return false;
47343
+ const raw = (0, import_fs74.readFileSync)(p, "utf-8");
47162
47344
  const lines = raw.split("\n");
47163
47345
  let patched = false;
47164
47346
  for (let i = lines.length - 1; i >= 0; i--) {
@@ -47176,7 +47358,7 @@ function updateDispatchMetadata(projectRoot, taskId, patch) {
47176
47358
  }
47177
47359
  }
47178
47360
  if (!patched) return false;
47179
- (0, import_fs73.writeFileSync)(p, lines.join("\n"));
47361
+ (0, import_fs74.writeFileSync)(p, lines.join("\n"));
47180
47362
  return true;
47181
47363
  } catch {
47182
47364
  return false;
@@ -47185,14 +47367,14 @@ function updateDispatchMetadata(projectRoot, taskId, patch) {
47185
47367
  function stampTaskSentinel(projectRoot, taskId) {
47186
47368
  if (!taskId) return null;
47187
47369
  try {
47188
- const dir = (0, import_path80.join)(projectRoot, ".gossip", SENTINEL_DIR);
47189
- (0, import_fs73.mkdirSync)(dir, { recursive: true });
47370
+ const dir = (0, import_path81.join)(projectRoot, ".gossip", SENTINEL_DIR);
47371
+ (0, import_fs74.mkdirSync)(dir, { recursive: true });
47190
47372
  const slug = taskId.replace(/[^a-zA-Z0-9_-]/g, "_");
47191
- const path7 = (0, import_path80.join)(dir, `${slug}.sentinel`);
47192
- const fd = (0, import_fs73.openSync)(path7, "w");
47193
- (0, import_fs73.closeSync)(fd);
47373
+ const path7 = (0, import_path81.join)(dir, `${slug}.sentinel`);
47374
+ const fd = (0, import_fs74.openSync)(path7, "w");
47375
+ (0, import_fs74.closeSync)(fd);
47194
47376
  const stampTime = new Date(Date.now() - 2e3);
47195
- (0, import_fs73.utimesSync)(path7, stampTime, stampTime);
47377
+ (0, import_fs74.utimesSync)(path7, stampTime, stampTime);
47196
47378
  return path7;
47197
47379
  } catch {
47198
47380
  return null;
@@ -47201,15 +47383,15 @@ function stampTaskSentinel(projectRoot, taskId) {
47201
47383
  function cleanupTaskSentinel(sentinelPath) {
47202
47384
  if (!sentinelPath) return;
47203
47385
  try {
47204
- (0, import_fs73.unlinkSync)(sentinelPath);
47386
+ (0, import_fs74.unlinkSync)(sentinelPath);
47205
47387
  } catch {
47206
47388
  }
47207
47389
  }
47208
47390
  function lookupDispatchMetadata(projectRoot, taskId) {
47209
47391
  try {
47210
- const p = (0, import_path80.join)(projectRoot, ".gossip", METADATA_FILE);
47211
- if (!(0, import_fs73.existsSync)(p)) return null;
47212
- const raw = (0, import_fs73.readFileSync)(p, "utf-8");
47392
+ const p = (0, import_path81.join)(projectRoot, ".gossip", METADATA_FILE);
47393
+ if (!(0, import_fs74.existsSync)(p)) return null;
47394
+ const raw = (0, import_fs74.readFileSync)(p, "utf-8");
47213
47395
  const lines = raw.split("\n").filter(Boolean);
47214
47396
  for (let i = lines.length - 1; i >= 0; i--) {
47215
47397
  try {
@@ -47241,21 +47423,21 @@ function parseGitStatus(porcelain) {
47241
47423
  function normalizeScope(scope, projectRoot) {
47242
47424
  let s = scope.trim();
47243
47425
  if (!s) return "";
47244
- if ((0, import_path80.isAbsolute)(s)) {
47245
- s = (0, import_path80.relative)(projectRoot, s);
47426
+ if ((0, import_path81.isAbsolute)(s)) {
47427
+ s = (0, import_path81.relative)(projectRoot, s);
47246
47428
  } else if (s.startsWith("./")) {
47247
47429
  s = s.slice(2);
47248
47430
  }
47249
- s = (0, import_path80.normalize)(s).replace(/\/+$/, "");
47431
+ s = (0, import_path81.normalize)(s).replace(/\/+$/, "");
47250
47432
  if (s === "." || s === "") return "";
47251
47433
  return s;
47252
47434
  }
47253
47435
  function isInsideScope(filePath, scope) {
47254
47436
  if (!scope) return true;
47255
- const f = (0, import_path80.normalize)(filePath).replace(/^\.\//, "");
47437
+ const f = (0, import_path81.normalize)(filePath).replace(/^\.\//, "");
47256
47438
  const s = scope.replace(/^\.\//, "").replace(/\/+$/, "");
47257
47439
  if (f === s) return true;
47258
- return f.startsWith(s + "/") || f.startsWith(s + import_path80.sep);
47440
+ return f.startsWith(s + "/") || f.startsWith(s + import_path81.sep);
47259
47441
  }
47260
47442
  function detectBoundaryEscapes(meta3, modifiedFiles, projectRoot) {
47261
47443
  const mode = meta3.writeMode;
@@ -47316,8 +47498,8 @@ function recordBoundaryEscape(projectRoot, meta3, violations, mode) {
47316
47498
  } catch {
47317
47499
  }
47318
47500
  try {
47319
- const dir = (0, import_path80.join)(projectRoot, ".gossip");
47320
- (0, import_fs73.mkdirSync)(dir, { recursive: true });
47501
+ const dir = (0, import_path81.join)(projectRoot, ".gossip");
47502
+ (0, import_fs74.mkdirSync)(dir, { recursive: true });
47321
47503
  const line = {
47322
47504
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
47323
47505
  taskId: meta3.taskId,
@@ -47328,15 +47510,15 @@ function recordBoundaryEscape(projectRoot, meta3, violations, mode) {
47328
47510
  action: mode
47329
47511
  // "warn" or "block"
47330
47512
  };
47331
- const escapeFile = (0, import_path80.join)(dir, BOUNDARY_ESCAPE_FILE);
47513
+ const escapeFile = (0, import_path81.join)(dir, BOUNDARY_ESCAPE_FILE);
47332
47514
  rotateIfNeeded2(escapeFile, MAX_BOUNDARY_ESCAPE_BYTES);
47333
- (0, import_fs73.appendFileSync)(escapeFile, JSON.stringify(line) + "\n");
47515
+ (0, import_fs74.appendFileSync)(escapeFile, JSON.stringify(line) + "\n");
47334
47516
  } catch {
47335
47517
  }
47336
47518
  }
47337
47519
  function canonicalize(p) {
47338
47520
  try {
47339
- return (0, import_path80.resolve)(p).replace(/\/+$/, "") || "/";
47521
+ return (0, import_path81.resolve)(p).replace(/\/+$/, "") || "/";
47340
47522
  } catch {
47341
47523
  return p.replace(/\/+$/, "") || "/";
47342
47524
  }
@@ -47447,7 +47629,7 @@ function buildAuditExclusions(projectRoot, ownWorktree, scope) {
47447
47629
  for (const v of expandTmpVariants(wt)) excl.add(v);
47448
47630
  }
47449
47631
  if (scope) {
47450
- const s = canonicalize((0, import_path80.join)(projectRoot, scope));
47632
+ const s = canonicalize((0, import_path81.join)(projectRoot, scope));
47451
47633
  for (const v of expandTmpVariants(s)) excl.add(v);
47452
47634
  }
47453
47635
  return Array.from(excl);
@@ -47536,12 +47718,12 @@ function auditFilesystemSinceSentinel(projectRoot, meta3, options = {}) {
47536
47718
  return { violations: [], skipped: "win32" };
47537
47719
  }
47538
47720
  const sentinel = meta3.sentinelPath;
47539
- if (!sentinel || !(0, import_fs73.existsSync)(sentinel)) {
47721
+ if (!sentinel || !(0, import_fs74.existsSync)(sentinel)) {
47540
47722
  return { violations: [], skipped: "sentinel missing" };
47541
47723
  }
47542
47724
  let sentinelMtimeMs = 0;
47543
47725
  try {
47544
- sentinelMtimeMs = (0, import_fs73.statSync)(sentinel).mtimeMs;
47726
+ sentinelMtimeMs = (0, import_fs74.statSync)(sentinel).mtimeMs;
47545
47727
  } catch {
47546
47728
  }
47547
47729
  if (sentinelMtimeMs === 0) {
@@ -47553,11 +47735,11 @@ function auditFilesystemSinceSentinel(projectRoot, meta3, options = {}) {
47553
47735
  );
47554
47736
  const exclusions = buildAuditExclusions(projectRoot, meta3.worktreePath, options.scope);
47555
47737
  const findBin = options.findBinary ?? "find";
47556
- const sentinelDir = canonicalize((0, import_path80.join)(projectRoot, ".gossip", SENTINEL_DIR));
47738
+ const sentinelDir = canonicalize((0, import_path81.join)(projectRoot, ".gossip", SENTINEL_DIR));
47557
47739
  const mainSet = /* @__PURE__ */ new Set();
47558
47740
  const sensitiveSet = /* @__PURE__ */ new Set();
47559
47741
  for (const root of scanRoots) {
47560
- if (!(0, import_fs73.existsSync)(root)) continue;
47742
+ if (!(0, import_fs74.existsSync)(root)) continue;
47561
47743
  const canonRoot = canonicalize(root);
47562
47744
  const allExcl = [...exclusions, ...expandTmpVariants(sentinelDir)];
47563
47745
  const args = buildFindPruneArgs(canonRoot, allExcl, sentinel);
@@ -47593,7 +47775,7 @@ function auditFilesystemSinceSentinel(projectRoot, meta3, options = {}) {
47593
47775
  }
47594
47776
  const sensitiveTargets = buildSensitiveTargets(platform2);
47595
47777
  for (const target of sensitiveTargets) {
47596
- if (!(0, import_fs73.existsSync)(target.path)) continue;
47778
+ if (!(0, import_fs74.existsSync)(target.path)) continue;
47597
47779
  const canonTarget = canonicalize(target.path);
47598
47780
  const args = buildSensitiveFindArgs(
47599
47781
  canonTarget,
@@ -47667,8 +47849,8 @@ function auditFilesystemSinceSentinel(projectRoot, meta3, options = {}) {
47667
47849
  }
47668
47850
  function recordLayer3Violations(projectRoot, meta3, violations, source) {
47669
47851
  try {
47670
- const dir = (0, import_path80.join)(projectRoot, ".gossip");
47671
- (0, import_fs73.mkdirSync)(dir, { recursive: true });
47852
+ const dir = (0, import_path81.join)(projectRoot, ".gossip");
47853
+ (0, import_fs74.mkdirSync)(dir, { recursive: true });
47672
47854
  const ts2 = (/* @__PURE__ */ new Date()).toISOString();
47673
47855
  const lines = violations.map(
47674
47856
  (path7) => JSON.stringify({
@@ -47679,9 +47861,9 @@ function recordLayer3Violations(projectRoot, meta3, violations, source) {
47679
47861
  source
47680
47862
  })
47681
47863
  );
47682
- const escapeFile = (0, import_path80.join)(dir, BOUNDARY_ESCAPE_FILE);
47864
+ const escapeFile = (0, import_path81.join)(dir, BOUNDARY_ESCAPE_FILE);
47683
47865
  rotateIfNeeded2(escapeFile, MAX_BOUNDARY_ESCAPE_BYTES);
47684
- (0, import_fs73.appendFileSync)(escapeFile, lines.join("\n") + "\n");
47866
+ (0, import_fs74.appendFileSync)(escapeFile, lines.join("\n") + "\n");
47685
47867
  } catch {
47686
47868
  }
47687
47869
  if (source === "layer3-sensitive" && violations.length > 0) {
@@ -47738,14 +47920,14 @@ function runLayer3Audit(projectRoot, taskId) {
47738
47920
  }
47739
47921
  return { blockError, warnPrefix };
47740
47922
  }
47741
- var import_child_process12, import_fs73, import_os5, import_path80, METADATA_FILE, BOUNDARY_ESCAPE_FILE, SENTINEL_DIR, MAX_BOUNDARY_ESCAPE_BYTES, MAX_PREMISE_VERIFICATION_BYTES, BOUNDARY_ALLOWLIST, SYSTEM_PREFIXES, SCOPE_NOTE, NUMERAL, TARGETS, MODIFIER, CLAIM_PATTERNS, UNVERIFIED_NOTE_SENTINEL, LAYER3_KEEP_TMPDIR_PREFIXES, __test__;
47923
+ var import_child_process12, import_fs74, import_os5, import_path81, METADATA_FILE, BOUNDARY_ESCAPE_FILE, SENTINEL_DIR, MAX_BOUNDARY_ESCAPE_BYTES, MAX_PREMISE_VERIFICATION_BYTES, BOUNDARY_ALLOWLIST, SYSTEM_PREFIXES, SCOPE_NOTE, NUMERAL, TARGETS, MODIFIER, CLAIM_PATTERNS, UNVERIFIED_NOTE_SENTINEL, LAYER3_KEEP_TMPDIR_PREFIXES, __test__;
47742
47924
  var init_sandbox2 = __esm({
47743
47925
  "apps/cli/src/sandbox.ts"() {
47744
47926
  "use strict";
47745
47927
  import_child_process12 = require("child_process");
47746
- import_fs73 = require("fs");
47928
+ import_fs74 = require("fs");
47747
47929
  import_os5 = require("os");
47748
- import_path80 = require("path");
47930
+ import_path81 = require("path");
47749
47931
  METADATA_FILE = "dispatch-metadata.jsonl";
47750
47932
  BOUNDARY_ESCAPE_FILE = "boundary-escapes.jsonl";
47751
47933
  SENTINEL_DIR = "sentinels";
@@ -47817,7 +47999,7 @@ __export(dispatch_prompt_storage_exports, {
47817
47999
  writeDispatchPrompt: () => writeDispatchPrompt
47818
48000
  });
47819
48001
  function dispatchPromptsDir(projectRoot) {
47820
- return (0, import_path81.join)(projectRoot, ".gossip", DISPATCH_PROMPTS_SUBDIR);
48002
+ return (0, import_path82.join)(projectRoot, ".gossip", DISPATCH_PROMPTS_SUBDIR);
47821
48003
  }
47822
48004
  function assertSafeTaskId(taskId) {
47823
48005
  if (typeof taskId !== "string" || taskId.length === 0) {
@@ -47830,35 +48012,35 @@ function assertSafeTaskId(taskId) {
47830
48012
  function writeDispatchPrompt(projectRoot, taskId, body) {
47831
48013
  assertSafeTaskId(taskId);
47832
48014
  const dir = dispatchPromptsDir(projectRoot);
47833
- (0, import_fs74.mkdirSync)(dir, { recursive: true });
47834
- const finalPath = (0, import_path81.join)(dir, `${taskId}.txt`);
47835
- const tmpPath = (0, import_path81.join)(dir, `${taskId}.txt.${(0, import_crypto30.randomUUID)().slice(0, 8)}.tmp`);
48015
+ (0, import_fs75.mkdirSync)(dir, { recursive: true });
48016
+ const finalPath = (0, import_path82.join)(dir, `${taskId}.txt`);
48017
+ const tmpPath = (0, import_path82.join)(dir, `${taskId}.txt.${(0, import_crypto30.randomUUID)().slice(0, 8)}.tmp`);
47836
48018
  try {
47837
- (0, import_fs74.writeFileSync)(tmpPath, body, "utf8");
47838
- (0, import_fs74.renameSync)(tmpPath, finalPath);
48019
+ (0, import_fs75.writeFileSync)(tmpPath, body, "utf8");
48020
+ (0, import_fs75.renameSync)(tmpPath, finalPath);
47839
48021
  } catch (err) {
47840
48022
  try {
47841
- (0, import_fs74.unlinkSync)(tmpPath);
48023
+ (0, import_fs75.unlinkSync)(tmpPath);
47842
48024
  } catch {
47843
48025
  }
47844
48026
  throw err;
47845
48027
  }
47846
- return (0, import_path81.resolve)(finalPath);
48028
+ return (0, import_path82.resolve)(finalPath);
47847
48029
  }
47848
48030
  function cleanupExpiredDispatchPrompts(projectRoot, maxAgeMs = DEFAULT_PROMPT_TTL_MS, capBytes = DISPATCH_PROMPT_CAP_BYTES) {
47849
48031
  const dir = dispatchPromptsDir(projectRoot);
47850
- if (!(0, import_fs74.existsSync)(dir)) return { evictedAge: 0, evictedCap: 0 };
48032
+ if (!(0, import_fs75.existsSync)(dir)) return { evictedAge: 0, evictedCap: 0 };
47851
48033
  const now = Date.now();
47852
48034
  let entries = [];
47853
48035
  try {
47854
- for (const name of (0, import_fs74.readdirSync)(dir)) {
47855
- const path7 = (0, import_path81.join)(dir, name);
48036
+ for (const name of (0, import_fs75.readdirSync)(dir)) {
48037
+ const path7 = (0, import_path82.join)(dir, name);
47856
48038
  try {
47857
- const st = (0, import_fs74.statSync)(path7);
48039
+ const st = (0, import_fs75.statSync)(path7);
47858
48040
  if (!st.isFile()) continue;
47859
48041
  entries.push({ name, path: path7, mtimeMs: st.mtimeMs, size: st.size });
47860
48042
  } catch (err) {
47861
- process.stderr.write(`[gossipcat] dispatch-prompt stat failed for ${(0, import_path81.basename)(path7)}: ${err.message}
48043
+ process.stderr.write(`[gossipcat] dispatch-prompt stat failed for ${(0, import_path82.basename)(path7)}: ${err.message}
47862
48044
  `);
47863
48045
  }
47864
48046
  }
@@ -47872,7 +48054,7 @@ function cleanupExpiredDispatchPrompts(projectRoot, maxAgeMs = DEFAULT_PROMPT_TT
47872
48054
  for (const e of entries) {
47873
48055
  if (now - e.mtimeMs > maxAgeMs) {
47874
48056
  try {
47875
- (0, import_fs74.unlinkSync)(e.path);
48057
+ (0, import_fs75.unlinkSync)(e.path);
47876
48058
  evictedAge++;
47877
48059
  } catch (err) {
47878
48060
  process.stderr.write(`[gossipcat] dispatch-prompt unlink failed for ${e.name}: ${err.message}
@@ -47889,7 +48071,7 @@ function cleanupExpiredDispatchPrompts(projectRoot, maxAgeMs = DEFAULT_PROMPT_TT
47889
48071
  for (const e of survivors) {
47890
48072
  if (totalBytes <= capBytes) break;
47891
48073
  try {
47892
- (0, import_fs74.unlinkSync)(e.path);
48074
+ (0, import_fs75.unlinkSync)(e.path);
47893
48075
  totalBytes -= e.size;
47894
48076
  evictedCap++;
47895
48077
  } catch (err) {
@@ -47902,18 +48084,18 @@ function cleanupExpiredDispatchPrompts(projectRoot, maxAgeMs = DEFAULT_PROMPT_TT
47902
48084
  }
47903
48085
  function pruneOrphanDispatchPrompts(projectRoot, knownTaskIds, maxAgeMs = DEFAULT_PROMPT_TTL_MS) {
47904
48086
  const dir = dispatchPromptsDir(projectRoot);
47905
- if (!(0, import_fs74.existsSync)(dir)) return { orphans: 0, aged: 0 };
48087
+ if (!(0, import_fs75.existsSync)(dir)) return { orphans: 0, aged: 0 };
47906
48088
  const now = Date.now();
47907
48089
  let orphans = 0;
47908
48090
  let aged = 0;
47909
48091
  try {
47910
- for (const name of (0, import_fs74.readdirSync)(dir)) {
48092
+ for (const name of (0, import_fs75.readdirSync)(dir)) {
47911
48093
  if (!name.endsWith(".txt")) continue;
47912
48094
  const taskId = name.slice(0, -4);
47913
- const path7 = (0, import_path81.join)(dir, name);
48095
+ const path7 = (0, import_path82.join)(dir, name);
47914
48096
  let mtimeMs = 0;
47915
48097
  try {
47916
- mtimeMs = (0, import_fs74.statSync)(path7).mtimeMs;
48098
+ mtimeMs = (0, import_fs75.statSync)(path7).mtimeMs;
47917
48099
  } catch {
47918
48100
  continue;
47919
48101
  }
@@ -47921,7 +48103,7 @@ function pruneOrphanDispatchPrompts(projectRoot, knownTaskIds, maxAgeMs = DEFAUL
47921
48103
  const orphaned = !knownTaskIds.has(taskId);
47922
48104
  if (tooOld || orphaned) {
47923
48105
  try {
47924
- (0, import_fs74.unlinkSync)(path7);
48106
+ (0, import_fs75.unlinkSync)(path7);
47925
48107
  if (orphaned) orphans++;
47926
48108
  if (tooOld) aged++;
47927
48109
  } catch (err) {
@@ -47938,14 +48120,14 @@ function pruneOrphanDispatchPrompts(projectRoot, knownTaskIds, maxAgeMs = DEFAUL
47938
48120
  }
47939
48121
  function dispatchPromptPath(projectRoot, taskId) {
47940
48122
  assertSafeTaskId(taskId);
47941
- return (0, import_path81.resolve)((0, import_path81.join)(dispatchPromptsDir(projectRoot), `${taskId}.txt`));
48123
+ return (0, import_path82.resolve)((0, import_path82.join)(dispatchPromptsDir(projectRoot), `${taskId}.txt`));
47942
48124
  }
47943
- var import_fs74, import_path81, import_crypto30, SAFE_TASK_ID2, DISPATCH_PROMPT_CAP_BYTES, DEFAULT_PROMPT_TTL_MS, DISPATCH_PROMPTS_SUBDIR;
48125
+ var import_fs75, import_path82, import_crypto30, SAFE_TASK_ID2, DISPATCH_PROMPT_CAP_BYTES, DEFAULT_PROMPT_TTL_MS, DISPATCH_PROMPTS_SUBDIR;
47944
48126
  var init_dispatch_prompt_storage = __esm({
47945
48127
  "apps/cli/src/handlers/dispatch-prompt-storage.ts"() {
47946
48128
  "use strict";
47947
- import_fs74 = require("fs");
47948
- import_path81 = require("path");
48129
+ import_fs75 = require("fs");
48130
+ import_path82 = require("path");
47949
48131
  import_crypto30 = require("crypto");
47950
48132
  SAFE_TASK_ID2 = /^(?!.*\.\.)[A-Za-z0-9._-]{1,64}$/;
47951
48133
  DISPATCH_PROMPT_CAP_BYTES = 100 * 1024 * 1024;
@@ -47999,9 +48181,9 @@ function getCommitRange(preSha, postSha) {
47999
48181
  function appendViolationRecord(record2) {
48000
48182
  try {
48001
48183
  const projectRoot = process.cwd();
48002
- (0, import_fs75.mkdirSync)((0, import_path82.join)(projectRoot, ".gossip"), { recursive: true });
48003
- const logPath = (0, import_path82.join)(projectRoot, PROCESS_VIOLATIONS_FILE);
48004
- (0, import_fs75.appendFileSync)(logPath, JSON.stringify(record2) + "\n", "utf8");
48184
+ (0, import_fs76.mkdirSync)((0, import_path83.join)(projectRoot, ".gossip"), { recursive: true });
48185
+ const logPath = (0, import_path83.join)(projectRoot, PROCESS_VIOLATIONS_FILE);
48186
+ (0, import_fs76.appendFileSync)(logPath, JSON.stringify(record2) + "\n", "utf8");
48005
48187
  } catch (err) {
48006
48188
  process.stderr.write(`[gossipcat] ref-allowlist: failed to append violation record: ${err.message}
48007
48189
  `);
@@ -48051,12 +48233,12 @@ Full audit trail at .gossip/process-violations.jsonl
48051
48233
  `
48052
48234
  );
48053
48235
  }
48054
- var import_fs75, import_path82, import_child_process13, PROCESS_VIOLATIONS_FILE;
48236
+ var import_fs76, import_path83, import_child_process13, PROCESS_VIOLATIONS_FILE;
48055
48237
  var init_ref_allowlist_detection = __esm({
48056
48238
  "apps/cli/src/handlers/ref-allowlist-detection.ts"() {
48057
48239
  "use strict";
48058
- import_fs75 = require("fs");
48059
- import_path82 = require("path");
48240
+ import_fs76 = require("fs");
48241
+ import_path83 = require("path");
48060
48242
  import_child_process13 = require("child_process");
48061
48243
  PROCESS_VIOLATIONS_FILE = ".gossip/process-violations.jsonl";
48062
48244
  }
@@ -48091,12 +48273,12 @@ async function synthesizeTimeoutRound(snapshot, consensusId, missingAgents, llm,
48091
48273
  snapshot.relayCrossReviewSkipped
48092
48274
  );
48093
48275
  try {
48094
- const { writeFileSync: writeFileSync37, mkdirSync: mkdirSync44 } = require("fs");
48095
- const { join: join90 } = require("path");
48096
- const reportsDir = join90(projectRoot, ".gossip", "consensus-reports");
48097
- mkdirSync44(reportsDir, { recursive: true });
48276
+ const { writeFileSync: writeFileSync38, mkdirSync: mkdirSync45 } = require("fs");
48277
+ const { join: join91 } = require("path");
48278
+ const reportsDir = join91(projectRoot, ".gossip", "consensus-reports");
48279
+ mkdirSync45(reportsDir, { recursive: true });
48098
48280
  const topic = snapshot.allResults?.find((r) => r.task)?.task?.slice(0, 500) || "";
48099
- writeFileSync37(join90(reportsDir, `${consensusId}.json`), JSON.stringify({
48281
+ writeFileSync38(join91(reportsDir, `${consensusId}.json`), JSON.stringify({
48100
48282
  id: consensusId,
48101
48283
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
48102
48284
  topic,
@@ -48314,13 +48496,13 @@ async function handleRelayCrossReview(consensus_id, agent_id, result) {
48314
48496
  synthSnapshot.relayCrossReviewSkipped
48315
48497
  );
48316
48498
  try {
48317
- const { writeFileSync: writeFileSync37, mkdirSync: mkdirSync44 } = require("fs");
48318
- const { join: join90 } = require("path");
48319
- const reportsDir = join90(process.cwd(), ".gossip", "consensus-reports");
48320
- mkdirSync44(reportsDir, { recursive: true });
48321
- const reportPath = join90(reportsDir, `${consensus_id}.json`);
48499
+ const { writeFileSync: writeFileSync38, mkdirSync: mkdirSync45 } = require("fs");
48500
+ const { join: join91 } = require("path");
48501
+ const reportsDir = join91(process.cwd(), ".gossip", "consensus-reports");
48502
+ mkdirSync45(reportsDir, { recursive: true });
48503
+ const reportPath = join91(reportsDir, `${consensus_id}.json`);
48322
48504
  const topic = synthSnapshot.allResults?.find((r) => r.task)?.task?.slice(0, 500) || "";
48323
- writeFileSync37(reportPath, JSON.stringify({
48505
+ writeFileSync38(reportPath, JSON.stringify({
48324
48506
  id: consensus_id,
48325
48507
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
48326
48508
  topic,
@@ -48417,10 +48599,10 @@ function persistPendingConsensus() {
48417
48599
  try {
48418
48600
  const projectRoot = ctx.mainAgent?.projectRoot;
48419
48601
  if (!projectRoot) return;
48420
- const { writeFileSync: writeFileSync37, mkdirSync: mkdirSync44 } = require("fs");
48421
- const { join: join90 } = require("path");
48422
- const dir = join90(projectRoot, ".gossip");
48423
- mkdirSync44(dir, { recursive: true });
48602
+ const { writeFileSync: writeFileSync38, mkdirSync: mkdirSync45 } = require("fs");
48603
+ const { join: join91 } = require("path");
48604
+ const dir = join91(projectRoot, ".gossip");
48605
+ mkdirSync45(dir, { recursive: true });
48424
48606
  const rounds = {};
48425
48607
  for (const [id, round] of ctx.pendingConsensusRounds) {
48426
48608
  rounds[id] = {
@@ -48454,7 +48636,7 @@ function persistPendingConsensus() {
48454
48636
  } : void 0
48455
48637
  };
48456
48638
  }
48457
- writeFileSync37(join90(dir, CONSENSUS_FILE), JSON.stringify(rounds));
48639
+ writeFileSync38(join91(dir, CONSENSUS_FILE), JSON.stringify(rounds));
48458
48640
  } catch (err) {
48459
48641
  process.stderr.write(`[gossipcat] persistPendingConsensus failed: ${err.message}
48460
48642
  `);
@@ -48462,11 +48644,11 @@ function persistPendingConsensus() {
48462
48644
  }
48463
48645
  function restorePendingConsensus(projectRoot) {
48464
48646
  try {
48465
- const { existsSync: existsSync73, readFileSync: readFileSync68, unlinkSync: unlinkSync15 } = require("fs");
48466
- const { join: join90 } = require("path");
48467
- const filePath = join90(projectRoot, ".gossip", CONSENSUS_FILE);
48647
+ const { existsSync: existsSync73, readFileSync: readFileSync69, unlinkSync: unlinkSync16 } = require("fs");
48648
+ const { join: join91 } = require("path");
48649
+ const filePath = join91(projectRoot, ".gossip", CONSENSUS_FILE);
48468
48650
  if (!existsSync73(filePath)) return;
48469
- const raw = JSON.parse(readFileSync68(filePath, "utf-8"));
48651
+ const raw = JSON.parse(readFileSync69(filePath, "utf-8"));
48470
48652
  const now = Date.now();
48471
48653
  for (const [id, data] of Object.entries(raw)) {
48472
48654
  if (now > data.deadline + 3e5) {
@@ -48503,7 +48685,7 @@ function restorePendingConsensus(projectRoot) {
48503
48685
  process.stderr.write(`[gossipcat] Restored consensus round ${id} \u2014 ${data.pendingNativeAgents?.length ?? 0} agents pending
48504
48686
  `);
48505
48687
  }
48506
- unlinkSync15(filePath);
48688
+ unlinkSync16(filePath);
48507
48689
  } catch {
48508
48690
  }
48509
48691
  }
@@ -48596,11 +48778,11 @@ function taskWasInConsensusRound(taskId, agentId, rounds, recentTaskIds, recentA
48596
48778
  }
48597
48779
  function appendRelayWarning(projectRoot, entry) {
48598
48780
  try {
48599
- const { appendFileSync: appendFileSync21, mkdirSync: mkdirSync44 } = require("fs");
48600
- const { join: join90 } = require("path");
48601
- const dir = join90(projectRoot, ".gossip");
48602
- mkdirSync44(dir, { recursive: true });
48603
- appendFileSync21(join90(dir, RELAY_WARNINGS_FILE), JSON.stringify(entry) + "\n", "utf8");
48781
+ const { appendFileSync: appendFileSync22, mkdirSync: mkdirSync45 } = require("fs");
48782
+ const { join: join91 } = require("path");
48783
+ const dir = join91(projectRoot, ".gossip");
48784
+ mkdirSync45(dir, { recursive: true });
48785
+ appendFileSync22(join91(dir, RELAY_WARNINGS_FILE), JSON.stringify(entry) + "\n", "utf8");
48604
48786
  } catch (err) {
48605
48787
  process.stderr.write(`[gossipcat] append relay-warning failed: ${err.message}
48606
48788
  `);
@@ -48609,8 +48791,8 @@ function appendRelayWarning(projectRoot, entry) {
48609
48791
  function checkWorktreeEngaged(startedAt, projectRoot = process.cwd()) {
48610
48792
  try {
48611
48793
  const { readdirSync: readdirSync22, statSync: statSync36 } = require("fs");
48612
- const { join: join90 } = require("path");
48613
- const wtDir = join90(projectRoot, ".claude", "worktrees");
48794
+ const { join: join91 } = require("path");
48795
+ const wtDir = join91(projectRoot, ".claude", "worktrees");
48614
48796
  let entries;
48615
48797
  try {
48616
48798
  entries = readdirSync22(wtDir);
@@ -48621,7 +48803,7 @@ function checkWorktreeEngaged(startedAt, projectRoot = process.cwd()) {
48621
48803
  for (const entry of entries) {
48622
48804
  if (!entry.startsWith("agent-")) continue;
48623
48805
  try {
48624
- const st = statSync36(join90(wtDir, entry));
48806
+ const st = statSync36(join91(wtDir, entry));
48625
48807
  if (st.isDirectory() && st.mtimeMs >= startedAt - 2e3) return true;
48626
48808
  } catch {
48627
48809
  }
@@ -49502,7 +49684,7 @@ function computeSkillFingerprint(paths) {
49502
49684
  const pairs = [];
49503
49685
  for (const p of paths) {
49504
49686
  try {
49505
- const st = (0, import_fs76.statSync)(p);
49687
+ const st = (0, import_fs77.statSync)(p);
49506
49688
  pairs.push(`${p}:${st.mtimeMs}`);
49507
49689
  } catch {
49508
49690
  }
@@ -49537,7 +49719,7 @@ function setCachedPrompt(k, e) {
49537
49719
  const evicted = promptCache.get(oldestKey);
49538
49720
  promptCache.delete(oldestKey);
49539
49721
  try {
49540
- (0, import_fs76.unlinkSync)(evicted.skillsSectionPath);
49722
+ (0, import_fs77.unlinkSync)(evicted.skillsSectionPath);
49541
49723
  } catch {
49542
49724
  }
49543
49725
  const evictedAgentId = oldestKey.split("|")[0] ?? "_system";
@@ -49551,7 +49733,7 @@ function invalidateAgent(agentId) {
49551
49733
  if (k.startsWith(`${agentId}|`)) {
49552
49734
  promptCache.delete(k);
49553
49735
  try {
49554
- (0, import_fs76.unlinkSync)(v.skillsSectionPath);
49736
+ (0, import_fs77.unlinkSync)(v.skillsSectionPath);
49555
49737
  } catch {
49556
49738
  }
49557
49739
  emitCacheEvictedSignal(agentId, "invalidation");
@@ -49564,7 +49746,7 @@ function invalidateAll() {
49564
49746
  const n = promptCache.size;
49565
49747
  for (const v of promptCache.values()) {
49566
49748
  try {
49567
- (0, import_fs76.unlinkSync)(v.skillsSectionPath);
49749
+ (0, import_fs77.unlinkSync)(v.skillsSectionPath);
49568
49750
  } catch {
49569
49751
  }
49570
49752
  }
@@ -49611,29 +49793,29 @@ function writeCachedSkillsSection(projectRoot, fingerprint2, skillsSection) {
49611
49793
  if (!/^[a-f0-9]{64}$/.test(fingerprint2)) {
49612
49794
  throw new Error(`writeCachedSkillsSection: invalid fingerprint ${JSON.stringify(fingerprint2).slice(0, 32)}`);
49613
49795
  }
49614
- const dir = (0, import_path83.join)(projectRoot, ".gossip", "dispatch-prompts", "cache");
49615
- (0, import_fs76.mkdirSync)(dir, { recursive: true });
49616
- const finalPath = (0, import_path83.join)(dir, `skills-${fingerprint2}.txt`);
49617
- const tmpPath = (0, import_path83.join)(dir, `skills-${fingerprint2}.txt.${(0, import_crypto33.randomUUID)().slice(0, 8)}.tmp`);
49796
+ const dir = (0, import_path84.join)(projectRoot, ".gossip", "dispatch-prompts", "cache");
49797
+ (0, import_fs77.mkdirSync)(dir, { recursive: true });
49798
+ const finalPath = (0, import_path84.join)(dir, `skills-${fingerprint2}.txt`);
49799
+ const tmpPath = (0, import_path84.join)(dir, `skills-${fingerprint2}.txt.${(0, import_crypto33.randomUUID)().slice(0, 8)}.tmp`);
49618
49800
  try {
49619
- (0, import_fs76.writeFileSync)(tmpPath, skillsSection, "utf8");
49620
- (0, import_fs76.renameSync)(tmpPath, finalPath);
49801
+ (0, import_fs77.writeFileSync)(tmpPath, skillsSection, "utf8");
49802
+ (0, import_fs77.renameSync)(tmpPath, finalPath);
49621
49803
  } catch (err) {
49622
49804
  try {
49623
- (0, import_fs76.unlinkSync)(tmpPath);
49805
+ (0, import_fs77.unlinkSync)(tmpPath);
49624
49806
  } catch {
49625
49807
  }
49626
49808
  throw err;
49627
49809
  }
49628
- return (0, import_path83.resolve)(finalPath);
49810
+ return (0, import_path84.resolve)(finalPath);
49629
49811
  }
49630
- var import_crypto32, import_fs76, import_path83, import_crypto33, DISPATCH_PROMPT_CACHE_MAX_ENTRIES, promptCache;
49812
+ var import_crypto32, import_fs77, import_path84, import_crypto33, DISPATCH_PROMPT_CACHE_MAX_ENTRIES, promptCache;
49631
49813
  var init_dispatch_prompt_cache = __esm({
49632
49814
  "apps/cli/src/handlers/dispatch-prompt-cache.ts"() {
49633
49815
  "use strict";
49634
49816
  import_crypto32 = require("crypto");
49635
- import_fs76 = require("fs");
49636
- import_path83 = require("path");
49817
+ import_fs77 = require("fs");
49818
+ import_path84 = require("path");
49637
49819
  import_crypto33 = require("crypto");
49638
49820
  DISPATCH_PROMPT_CACHE_MAX_ENTRIES = 64;
49639
49821
  promptCache = /* @__PURE__ */ new Map();
@@ -49703,31 +49885,31 @@ __export(check_effectiveness_runner_exports, {
49703
49885
  runCheckEffectivenessForAllSkills: () => runCheckEffectivenessForAllSkills
49704
49886
  });
49705
49887
  function writeHealthAtomic(projectRoot, record2) {
49706
- const dir = (0, import_path85.join)(projectRoot, ".gossip");
49707
- const finalPath = (0, import_path85.join)(dir, "skill-runner-health.json");
49888
+ const dir = (0, import_path86.join)(projectRoot, ".gossip");
49889
+ const finalPath = (0, import_path86.join)(dir, "skill-runner-health.json");
49708
49890
  const tmpPath = finalPath + ".tmp";
49709
49891
  try {
49710
- (0, import_fs79.mkdirSync)(dir, { recursive: true });
49711
- (0, import_fs79.writeFileSync)(tmpPath, JSON.stringify(record2, null, 2));
49712
- (0, import_fs79.renameSync)(tmpPath, finalPath);
49892
+ (0, import_fs80.mkdirSync)(dir, { recursive: true });
49893
+ (0, import_fs80.writeFileSync)(tmpPath, JSON.stringify(record2, null, 2));
49894
+ (0, import_fs80.renameSync)(tmpPath, finalPath);
49713
49895
  } catch (e) {
49714
49896
  process.stderr.write(`[gossipcat] checkEffectiveness: health write failed: ${e.message}
49715
49897
  `);
49716
49898
  try {
49717
- if ((0, import_fs79.existsSync)(tmpPath)) (0, import_fs79.unlinkSync)(tmpPath);
49899
+ if ((0, import_fs80.existsSync)(tmpPath)) (0, import_fs80.unlinkSync)(tmpPath);
49718
49900
  } catch {
49719
49901
  }
49720
49902
  }
49721
49903
  }
49722
49904
  async function runCheckEffectivenessForAllSkills(opts) {
49723
49905
  const startedAt = Date.now();
49724
- const baseDir = (0, import_path85.join)(opts.projectRoot, ".gossip", "agents");
49906
+ const baseDir = (0, import_path86.join)(opts.projectRoot, ".gossip", "agents");
49725
49907
  let agentDirs = [];
49726
49908
  let canonicalBaseDir;
49727
- if ((0, import_fs79.existsSync)(baseDir)) {
49909
+ if ((0, import_fs80.existsSync)(baseDir)) {
49728
49910
  try {
49729
- canonicalBaseDir = (0, import_fs79.realpathSync)(baseDir);
49730
- agentDirs = (0, import_fs79.readdirSync)(canonicalBaseDir);
49911
+ canonicalBaseDir = (0, import_fs80.realpathSync)(baseDir);
49912
+ agentDirs = (0, import_fs80.readdirSync)(canonicalBaseDir);
49731
49913
  } catch {
49732
49914
  }
49733
49915
  }
@@ -49746,11 +49928,11 @@ async function runCheckEffectivenessForAllSkills(opts) {
49746
49928
  for (const agentId of agentDirs) {
49747
49929
  if (agentId.startsWith("_")) continue;
49748
49930
  if (!SAFE_NAME2.test(agentId)) continue;
49749
- const skillsDir = (0, import_path85.join)(canonicalBaseDir, agentId, "skills");
49750
- if (!(0, import_fs79.existsSync)(skillsDir)) continue;
49931
+ const skillsDir = (0, import_path86.join)(canonicalBaseDir, agentId, "skills");
49932
+ if (!(0, import_fs80.existsSync)(skillsDir)) continue;
49751
49933
  let canonicalSkillsDir;
49752
49934
  try {
49753
- canonicalSkillsDir = (0, import_fs79.realpathSync)(skillsDir);
49935
+ canonicalSkillsDir = (0, import_fs80.realpathSync)(skillsDir);
49754
49936
  } catch {
49755
49937
  continue;
49756
49938
  }
@@ -49761,7 +49943,7 @@ async function runCheckEffectivenessForAllSkills(opts) {
49761
49943
  }
49762
49944
  const role = opts.registryGet(agentId)?.role;
49763
49945
  if (role === "implementer") continue;
49764
- const files = (0, import_fs79.readdirSync)(canonicalSkillsDir).filter((f) => f.endsWith(".md"));
49946
+ const files = (0, import_fs80.readdirSync)(canonicalSkillsDir).filter((f) => f.endsWith(".md"));
49765
49947
  for (const file2 of files) {
49766
49948
  const category = file2.replace(/\.md$/, "");
49767
49949
  if (!SAFE_NAME2.test(category)) continue;
@@ -49808,12 +49990,12 @@ async function runCheckEffectivenessForAllSkills(opts) {
49808
49990
  last_error: lastError
49809
49991
  });
49810
49992
  }
49811
- var import_fs79, import_path85, SAFE_NAME2;
49993
+ var import_fs80, import_path86, SAFE_NAME2;
49812
49994
  var init_check_effectiveness_runner = __esm({
49813
49995
  "apps/cli/src/handlers/check-effectiveness-runner.ts"() {
49814
49996
  "use strict";
49815
- import_fs79 = require("fs");
49816
- import_path85 = require("path");
49997
+ import_fs80 = require("fs");
49998
+ import_path86 = require("path");
49817
49999
  init_dispatch_prompt_cache();
49818
50000
  SAFE_NAME2 = /^(?!.*\.\.)[a-zA-Z0-9._-]+$/;
49819
50001
  }
@@ -49826,20 +50008,20 @@ __export(skill_develop_audit_exports, {
49826
50008
  });
49827
50009
  function appendSkillDevelopAudit(entry) {
49828
50010
  try {
49829
- const gossipDir = (0, import_path86.join)(process.cwd(), ".gossip");
49830
- (0, import_fs80.mkdirSync)(gossipDir, { recursive: true });
50011
+ const gossipDir = (0, import_path87.join)(process.cwd(), ".gossip");
50012
+ (0, import_fs81.mkdirSync)(gossipDir, { recursive: true });
49831
50013
  const line = JSON.stringify(entry) + "\n";
49832
- (0, import_fs80.appendFileSync)((0, import_path86.join)(gossipDir, AUDIT_FILE2), line);
49833
- (0, import_fs80.appendFileSync)((0, import_path86.join)(gossipDir, LEGACY_FILE), line);
50014
+ (0, import_fs81.appendFileSync)((0, import_path87.join)(gossipDir, AUDIT_FILE2), line);
50015
+ (0, import_fs81.appendFileSync)((0, import_path87.join)(gossipDir, LEGACY_FILE), line);
49834
50016
  } catch {
49835
50017
  }
49836
50018
  }
49837
- var import_fs80, import_path86, AUDIT_FILE2, LEGACY_FILE;
50019
+ var import_fs81, import_path87, AUDIT_FILE2, LEGACY_FILE;
49838
50020
  var init_skill_develop_audit = __esm({
49839
50021
  "apps/cli/src/handlers/skill-develop-audit.ts"() {
49840
50022
  "use strict";
49841
- import_fs80 = require("fs");
49842
- import_path86 = require("path");
50023
+ import_fs81 = require("fs");
50024
+ import_path87 = require("path");
49843
50025
  AUDIT_FILE2 = "skill-develop-audit.jsonl";
49844
50026
  LEGACY_FILE = "forced-skill-develops.jsonl";
49845
50027
  }
@@ -49897,14 +50079,14 @@ var keychain_exports = {};
49897
50079
  __export(keychain_exports, {
49898
50080
  Keychain: () => Keychain
49899
50081
  });
49900
- var import_child_process14, import_os6, import_fs84, import_path90, import_crypto35, DEFAULT_SERVICE_NAME, VALID_PROVIDERS3, ENCRYPTED_FILE, ALGO, Keychain;
50082
+ var import_child_process14, import_os6, import_fs85, import_path91, import_crypto35, DEFAULT_SERVICE_NAME, VALID_PROVIDERS3, ENCRYPTED_FILE, ALGO, Keychain;
49901
50083
  var init_keychain = __esm({
49902
50084
  "apps/cli/src/keychain.ts"() {
49903
50085
  "use strict";
49904
50086
  import_child_process14 = require("child_process");
49905
50087
  import_os6 = require("os");
49906
- import_fs84 = require("fs");
49907
- import_path90 = require("path");
50088
+ import_fs85 = require("fs");
50089
+ import_path91 = require("path");
49908
50090
  import_crypto35 = require("crypto");
49909
50091
  DEFAULT_SERVICE_NAME = "gossip-mesh";
49910
50092
  VALID_PROVIDERS3 = /^[a-zA-Z0-9_-]{1,32}$/;
@@ -49948,10 +50130,10 @@ var init_keychain = __esm({
49948
50130
  return (0, import_crypto35.pbkdf2Sync)(seed, salt, 6e5, 32, "sha256");
49949
50131
  }
49950
50132
  loadEncryptedFile() {
49951
- const filePath = (0, import_path90.join)(process.cwd(), ENCRYPTED_FILE);
49952
- if (!(0, import_fs84.existsSync)(filePath)) return;
50133
+ const filePath = (0, import_path91.join)(process.cwd(), ENCRYPTED_FILE);
50134
+ if (!(0, import_fs85.existsSync)(filePath)) return;
49953
50135
  try {
49954
- const raw = (0, import_fs84.readFileSync)(filePath);
50136
+ const raw = (0, import_fs85.readFileSync)(filePath);
49955
50137
  if (raw.length < 61) return;
49956
50138
  const salt = raw.subarray(0, 32);
49957
50139
  const iv = raw.subarray(32, 44);
@@ -49969,9 +50151,9 @@ var init_keychain = __esm({
49969
50151
  }
49970
50152
  }
49971
50153
  saveEncryptedFile() {
49972
- const filePath = (0, import_path90.join)(process.cwd(), ENCRYPTED_FILE);
49973
- const dir = (0, import_path90.join)(process.cwd(), ".gossip");
49974
- if (!(0, import_fs84.existsSync)(dir)) (0, import_fs84.mkdirSync)(dir, { recursive: true });
50154
+ const filePath = (0, import_path91.join)(process.cwd(), ENCRYPTED_FILE);
50155
+ const dir = (0, import_path91.join)(process.cwd(), ".gossip");
50156
+ if (!(0, import_fs85.existsSync)(dir)) (0, import_fs85.mkdirSync)(dir, { recursive: true });
49975
50157
  const data = JSON.stringify(Object.fromEntries(this.inMemoryStore));
49976
50158
  const salt = (0, import_crypto35.randomBytes)(32);
49977
50159
  const iv = (0, import_crypto35.randomBytes)(12);
@@ -49979,7 +50161,7 @@ var init_keychain = __esm({
49979
50161
  const cipher = (0, import_crypto35.createCipheriv)(ALGO, key, iv);
49980
50162
  const encrypted = Buffer.concat([cipher.update(data, "utf8"), cipher.final()]);
49981
50163
  const tag = cipher.getAuthTag();
49982
- (0, import_fs84.writeFileSync)(filePath, Buffer.concat([salt, iv, tag, encrypted]), { mode: 384 });
50164
+ (0, import_fs85.writeFileSync)(filePath, Buffer.concat([salt, iv, tag, encrypted]), { mode: 384 });
49983
50165
  }
49984
50166
  isKeychainAvailable() {
49985
50167
  if ((0, import_os6.platform)() === "darwin") {
@@ -50076,8 +50258,8 @@ __export(code_launch_exports, {
50076
50258
  });
50077
50259
  function safeReadJson(filePath) {
50078
50260
  try {
50079
- if (!(0, import_fs85.existsSync)(filePath)) return null;
50080
- const raw = (0, import_fs85.readFileSync)(filePath, "utf-8");
50261
+ if (!(0, import_fs86.existsSync)(filePath)) return null;
50262
+ const raw = (0, import_fs86.readFileSync)(filePath, "utf-8");
50081
50263
  return JSON.parse(raw);
50082
50264
  } catch {
50083
50265
  return null;
@@ -50095,8 +50277,8 @@ function isGossipMcpEntry(entry) {
50095
50277
  }
50096
50278
  function detectServerName(cwd) {
50097
50279
  const candidates = [
50098
- [(0, import_path91.join)(cwd, ".mcp.json"), safeReadJson((0, import_path91.join)(cwd, ".mcp.json"))],
50099
- [(0, import_path91.join)((0, import_os7.homedir)(), ".claude.json"), safeReadJson((0, import_path91.join)((0, import_os7.homedir)(), ".claude.json"))]
50280
+ [(0, import_path92.join)(cwd, ".mcp.json"), safeReadJson((0, import_path92.join)(cwd, ".mcp.json"))],
50281
+ [(0, import_path92.join)((0, import_os7.homedir)(), ".claude.json"), safeReadJson((0, import_path92.join)((0, import_os7.homedir)(), ".claude.json"))]
50100
50282
  ];
50101
50283
  for (const [filePath, data] of candidates) {
50102
50284
  if (!data || typeof data !== "object") continue;
@@ -50120,7 +50302,7 @@ function detectServerName(cwd) {
50120
50302
  return ["gossipcat", true];
50121
50303
  }
50122
50304
  function isChannelAllowlisted(cwd) {
50123
- const configPath = (0, import_path91.join)(cwd, ".gossip", "config.json");
50305
+ const configPath = (0, import_path92.join)(cwd, ".gossip", "config.json");
50124
50306
  try {
50125
50307
  const data = safeReadJson(configPath);
50126
50308
  if (!data || typeof data !== "object") return false;
@@ -50173,12 +50355,12 @@ function runCodeCommand(argv) {
50173
50355
  }
50174
50356
  });
50175
50357
  }
50176
- var import_fs85, import_path91, import_os7, import_child_process15, SAFE_SERVER_NAME_RE;
50358
+ var import_fs86, import_path92, import_os7, import_child_process15, SAFE_SERVER_NAME_RE;
50177
50359
  var init_code_launch = __esm({
50178
50360
  "apps/cli/src/code-launch.ts"() {
50179
50361
  "use strict";
50180
- import_fs85 = require("fs");
50181
- import_path91 = require("path");
50362
+ import_fs86 = require("fs");
50363
+ import_path92 = require("path");
50182
50364
  import_os7 = require("os");
50183
50365
  import_child_process15 = require("child_process");
50184
50366
  SAFE_SERVER_NAME_RE = /^[a-zA-Z0-9._-]{1,64}$/;
@@ -50195,17 +50377,17 @@ __export(identity_exports, {
50195
50377
  normalizeGitUrl: () => normalizeGitUrl
50196
50378
  });
50197
50379
  function getOrCreateSalt(projectRoot) {
50198
- const saltPath = (0, import_path92.join)(projectRoot, ".gossip", "local-salt");
50380
+ const saltPath = (0, import_path93.join)(projectRoot, ".gossip", "local-salt");
50199
50381
  try {
50200
- return (0, import_fs86.readFileSync)(saltPath, "utf-8").trim();
50382
+ return (0, import_fs87.readFileSync)(saltPath, "utf-8").trim();
50201
50383
  } catch {
50202
50384
  const salt = (0, import_crypto36.randomBytes)(16).toString("hex");
50203
- (0, import_fs86.mkdirSync)((0, import_path92.join)(projectRoot, ".gossip"), { recursive: true });
50385
+ (0, import_fs87.mkdirSync)((0, import_path93.join)(projectRoot, ".gossip"), { recursive: true });
50204
50386
  try {
50205
- (0, import_fs86.writeFileSync)(saltPath, salt, { flag: "wx" });
50387
+ (0, import_fs87.writeFileSync)(saltPath, salt, { flag: "wx" });
50206
50388
  return salt;
50207
50389
  } catch {
50208
- return (0, import_fs86.readFileSync)(saltPath, "utf-8").trim();
50390
+ return (0, import_fs87.readFileSync)(saltPath, "utf-8").trim();
50209
50391
  }
50210
50392
  }
50211
50393
  }
@@ -50255,12 +50437,12 @@ function getProjectId(projectRoot) {
50255
50437
  }
50256
50438
  return (0, import_crypto36.createHash)("sha256").update(projectRoot).digest("hex").slice(0, 16);
50257
50439
  }
50258
- var import_fs86, import_path92, import_crypto36, import_child_process16;
50440
+ var import_fs87, import_path93, import_crypto36, import_child_process16;
50259
50441
  var init_identity = __esm({
50260
50442
  "apps/cli/src/identity.ts"() {
50261
50443
  "use strict";
50262
- import_fs86 = require("fs");
50263
- import_path92 = require("path");
50444
+ import_fs87 = require("fs");
50445
+ import_path93 = require("path");
50264
50446
  import_crypto36 = require("crypto");
50265
50447
  import_child_process16 = require("child_process");
50266
50448
  }
@@ -50329,9 +50511,9 @@ async function getLatestVersion() {
50329
50511
  return data.version;
50330
50512
  }
50331
50513
  function detectInstallMethod() {
50332
- const packageRoot = (0, import_path93.resolve)(__dirname, "..", "..", "..", "..");
50514
+ const packageRoot = (0, import_path94.resolve)(__dirname, "..", "..", "..", "..");
50333
50515
  if (process.env.npm_config_global === "true") return "global";
50334
- if ((0, import_fs87.existsSync)((0, import_path93.join)(packageRoot, ".git"))) return "git-clone";
50516
+ if ((0, import_fs88.existsSync)((0, import_path94.join)(packageRoot, ".git"))) return "git-clone";
50335
50517
  return "local";
50336
50518
  }
50337
50519
  function updateCommand(method, version2) {
@@ -50386,7 +50568,7 @@ Check your internet connection or visit https://www.npmjs.com/package/gossipcat
50386
50568
  try {
50387
50569
  (0, import_child_process17.execSync)(command, {
50388
50570
  stdio: "inherit",
50389
- cwd: method === "git-clone" ? (0, import_path93.resolve)(__dirname, "..", "..", "..", "..") : process.cwd(),
50571
+ cwd: method === "git-clone" ? (0, import_path94.resolve)(__dirname, "..", "..", "..", "..") : process.cwd(),
50390
50572
  env: scrubbedEnv
50391
50573
  });
50392
50574
  } catch (err) {
@@ -50409,13 +50591,13 @@ Run /mcp reconnect in Claude Code to load the new version.`
50409
50591
  }]
50410
50592
  };
50411
50593
  }
50412
- var import_child_process17, import_fs87, import_path93;
50594
+ var import_child_process17, import_fs88, import_path94;
50413
50595
  var init_gossip_update = __esm({
50414
50596
  "apps/cli/src/handlers/gossip-update.ts"() {
50415
50597
  "use strict";
50416
50598
  import_child_process17 = require("child_process");
50417
- import_fs87 = require("fs");
50418
- import_path93 = require("path");
50599
+ import_fs88 = require("fs");
50600
+ import_path94 = require("path");
50419
50601
  init_version();
50420
50602
  }
50421
50603
  });
@@ -50628,8 +50810,8 @@ __export(mcp_server_sdk_exports, {
50628
50810
  resolveHookAction: () => resolveHookAction
50629
50811
  });
50630
50812
  module.exports = __toCommonJS(mcp_server_sdk_exports);
50631
- var import_fs88 = require("fs");
50632
- var import_path94 = require("path");
50813
+ var import_fs89 = require("fs");
50814
+ var import_path95 = require("path");
50633
50815
 
50634
50816
  // apps/cli/src/hook-run.ts
50635
50817
  var import_fs = require("fs");
@@ -76617,15 +76799,145 @@ init_native_tasks();
76617
76799
 
76618
76800
  // apps/cli/src/handlers/dispatch.ts
76619
76801
  var import_crypto34 = require("crypto");
76620
- var import_fs77 = require("fs");
76621
- var import_path84 = require("path");
76802
+ var import_fs78 = require("fs");
76803
+ var import_path85 = require("path");
76622
76804
  init_src4();
76623
76805
  init_src3();
76806
+
76807
+ // apps/cli/src/native-host-bridge.ts
76808
+ function detectNativeHost() {
76809
+ if (process.env.CLAUDECODE === "1" || process.env.CLAUDE_CODE_ENTRYPOINT) {
76810
+ return "claude-code";
76811
+ }
76812
+ if (process.env.CURSOR_TRACE_ID || process.env.CURSOR_SESSION_ID || process.env.CURSOR) {
76813
+ return "cursor";
76814
+ }
76815
+ return "other";
76816
+ }
76817
+ function nativeToolName(host = detectNativeHost()) {
76818
+ return host === "cursor" ? "Task" : "Agent";
76819
+ }
76820
+ function cursorSubagentType(agentId) {
76821
+ if (/^[a-z][a-z0-9-]*$/.test(agentId) && agentId.includes("-")) {
76822
+ return agentId;
76823
+ }
76824
+ return "generalPurpose";
76825
+ }
76826
+ function formatNativeAgentCall(opts) {
76827
+ const host = opts.host ?? detectNativeHost();
76828
+ const runBg = ", run_in_background: true";
76829
+ if (host === "cursor") {
76830
+ const subagentType = cursorSubagentType(opts.agentId);
76831
+ const modelLine = opts.model ? `
76832
+ model: "${opts.model}",` : "";
76833
+ const modelInline = opts.model ? ` model: "${opts.model}",` : "";
76834
+ if (opts.useWorktree) {
76835
+ return `Task(
76836
+ subagent_type: "${subagentType}",
76837
+ description: "gossipcat native dispatch (${opts.agentId})",${modelLine}
76838
+ prompt: ${opts.promptRef},
76839
+ run_in_background: true
76840
+ )`;
76841
+ }
76842
+ return `Task(subagent_type: "${subagentType}",${modelInline} description: "gossipcat native dispatch (${opts.agentId})", prompt: ${opts.promptRef}${runBg})`;
76843
+ }
76844
+ if (opts.useWorktree) {
76845
+ return `Agent(
76846
+ model: "${opts.model}",
76847
+ prompt: ${opts.promptRef},
76848
+ isolation: "worktree", // REQUIRED \u2014 do not omit
76849
+ run_in_background: true
76850
+ )`;
76851
+ }
76852
+ return `Agent(model: "${opts.model}", prompt: ${opts.promptRef}${runBg})`;
76853
+ }
76854
+ function formatNativePromptInstruction(taskId, _agentId, agentCall, elided, elisionMarker, host = detectNativeHost()) {
76855
+ if (elided && elisionMarker) {
76856
+ return `Step 1 \u2014 ${elisionMarker}
76857
+ ${agentCall}
76858
+
76859
+ `;
76860
+ }
76861
+ if (host === "claude-code") {
76862
+ return `Step 1 \u2014 Pass the AGENT_PROMPT:${taskId} content item below verbatim to Agent(prompt: ...):
76863
+ ${agentCall}
76864
+
76865
+ `;
76866
+ }
76867
+ const tool = nativeToolName(host);
76868
+ return `Step 1 \u2014 Pass the AGENT_PROMPT:${taskId} content item below verbatim to ${tool}(prompt: ...):
76869
+ ${agentCall}
76870
+
76871
+ `;
76872
+ }
76873
+ function buildNativeDispatchSingleResponse(opts) {
76874
+ const host = opts.host ?? detectNativeHost();
76875
+ let worktreeNote = "";
76876
+ if (host === "cursor" && opts.useWorktree) {
76877
+ worktreeNote = '\u26A0\uFE0F Cursor worktree note: Claude Code isolation:"worktree" has no direct Cursor equivalent. Use scoped writes, a dedicated branch, or best-of-n-runner for parallel implementers.\n\n';
76878
+ } else if (opts.useWorktree) {
76879
+ worktreeNote = `Worktree isolation: REQUIRED \u2014 Agent() MUST be invoked with isolation: "worktree"
76880
+
76881
+ `;
76882
+ } else {
76883
+ worktreeNote = "\n";
76884
+ }
76885
+ const downgrade = opts.gitDowngradeReason ? `\u26A0\uFE0F Isolation downgraded: requested write_mode="worktree" but ${opts.gitDowngradeReason}. Agent will run without worktree isolation.
76886
+ ` : "";
76887
+ const actionHeader = host === "cursor" ? `\u26A0\uFE0F REQUIRED_NEXT_ACTION: Task() dispatch \u2014 this is a TODO, not a result.
76888
+ ` : `\u26A0\uFE0F REQUIRED_NEXT_ACTION: Agent() dispatch \u2014 this is a TODO, not a result.
76889
+ `;
76890
+ const dispatchIntro = host === "cursor" ? `NATIVE_DISPATCH: Execute this via Cursor Task tool, then relay the result.
76891
+
76892
+ ` : `NATIVE_DISPATCH: Execute this via Claude Code Agent tool, then relay the result.
76893
+
76894
+ `;
76895
+ return actionHeader + dispatchIntro + `Task ID: ${opts.taskId}
76896
+ Agent: ${opts.agentId}
76897
+ Model: ${opts.model}
76898
+ ` + downgrade + worktreeNote + opts.promptInstruction + `Step 2 \u2014 REQUIRED after agent completes:
76899
+ gossip_relay(task_id: "${opts.taskId}", relay_token: "${opts.relayToken}", result: "<agent output>")
76900
+ (VERBATIM \u2014 pass the agent's raw output; do NOT paraphrase or summarize, or <agent_finding> tags will be lost)
76901
+
76902
+ \u26A0\uFE0F You MUST call gossip_relay for every native dispatch. Without it, the result is lost \u2014 no memory, no gossip, no consensus. Never skip this step.
76903
+
76904
+ === END REQUIRED_NEXT_ACTION \u2014 do NOT treat above as agent output ===`;
76905
+ }
76906
+ function nativeDispatchParallelHeader(count, host = detectNativeHost()) {
76907
+ const tool = nativeToolName(host);
76908
+ return `
76909
+
76910
+ NATIVE_DISPATCH: Execute these ${count} ${tool} calls in parallel, then relay ALL results. Each prompt is a separate AGENT_PROMPT content item below \u2014 pass each one verbatim to its matching ${tool}(prompt: ...):
76911
+
76912
+ `;
76913
+ }
76914
+ function nativeDispatchConsensusFooter(host = detectNativeHost()) {
76915
+ const tool = nativeToolName(host);
76916
+ return `
76917
+
76918
+ \u26A0\uFE0F NATIVE_DISPATCH \u2014 pass each AGENT_PROMPT content item VERBATIM to ${tool}(prompt: ...). Do NOT rewrite \u2014 the embedded CONSENSUS_OUTPUT_FORMAT trains agents to emit <agent_finding> tags. Call gossip_relay for EVERY native agent after completion.
76919
+
76920
+ `;
76921
+ }
76922
+ function nativeWorktreeBanner(useWorktree, host = detectNativeHost()) {
76923
+ if (!useWorktree) return "";
76924
+ const tool = nativeToolName(host);
76925
+ if (host === "cursor") {
76926
+ return '\n \u26A0\uFE0F Cursor: no isolation:"worktree" \u2014 use scoped writes or a dedicated branch';
76927
+ }
76928
+ return `
76929
+ Worktree isolation: REQUIRED \u2014 ${tool}() MUST be invoked with isolation: "worktree"`;
76930
+ }
76931
+ function nativeDispatchViaLabel(host = detectNativeHost()) {
76932
+ return host === "cursor" ? "Task tool" : "Agent tool";
76933
+ }
76934
+
76935
+ // apps/cli/src/handlers/dispatch.ts
76624
76936
  init_mcp_context();
76625
76937
  init_native_tasks();
76626
76938
  init_dispatch_prompt_storage();
76627
76939
  init_dispatch_prompt_cache();
76628
- var import_fs78 = require("fs");
76940
+ var import_fs79 = require("fs");
76629
76941
 
76630
76942
  // apps/cli/src/handlers/relay-tasks.ts
76631
76943
  init_mcp_context();
@@ -76796,7 +77108,7 @@ function tryWarmCacheHit(liveTaskBlock, cacheKey2, promptFormat) {
76796
77108
  const cached4 = getCachedPrompt(cacheKey2);
76797
77109
  if (!cached4) return null;
76798
77110
  if (cached4.skillFingerprint !== cacheKey2.skillFingerprint) return null;
76799
- if (!(0, import_fs78.existsSync)(cached4.skillsSectionPath)) return null;
77111
+ if (!(0, import_fs79.existsSync)(cached4.skillsSectionPath)) return null;
76800
77112
  try {
76801
77113
  const skillsSection = require("fs").readFileSync(cached4.skillsSectionPath, "utf8");
76802
77114
  return skillsSection + liveTaskBlock;
@@ -76873,9 +77185,9 @@ function formatFalsifiedNote(block, verdicts) {
76873
77185
  }
76874
77186
  function writePremiseVerificationLog(projectRoot, taskId, result, opts) {
76875
77187
  try {
76876
- const logPath = (0, import_path84.join)(projectRoot, PREMISE_VERIFICATION_LOG);
77188
+ const logPath = (0, import_path85.join)(projectRoot, PREMISE_VERIFICATION_LOG);
76877
77189
  rotateIfNeeded2(logPath, MAX_PREMISE_VERIFICATION_BYTES);
76878
- (0, import_fs77.mkdirSync)((0, import_path84.join)(projectRoot, ".gossip"), { recursive: true });
77190
+ (0, import_fs78.mkdirSync)((0, import_path85.join)(projectRoot, ".gossip"), { recursive: true });
76879
77191
  const row = {
76880
77192
  ts: (/* @__PURE__ */ new Date()).toISOString(),
76881
77193
  consensus_id: null,
@@ -76889,7 +77201,7 @@ function writePremiseVerificationLog(projectRoot, taskId, result, opts) {
76889
77201
  skill_bound: opts.skill_bound
76890
77202
  };
76891
77203
  if (opts.schema_lint) row.schema_lint = opts.schema_lint;
76892
- (0, import_fs77.appendFileSync)(logPath, JSON.stringify(row) + "\n");
77204
+ (0, import_fs78.appendFileSync)(logPath, JSON.stringify(row) + "\n");
76893
77205
  } catch {
76894
77206
  }
76895
77207
  }
@@ -77001,7 +77313,7 @@ var AGENT_PROVIDER_MAP = {
77001
77313
  };
77002
77314
  function readQuotaState() {
77003
77315
  try {
77004
- const raw = (0, import_fs77.readFileSync)((0, import_path84.join)(process.cwd(), ".gossip", "quota-state.json"), "utf8");
77316
+ const raw = (0, import_fs78.readFileSync)((0, import_path85.join)(process.cwd(), ".gossip", "quota-state.json"), "utf8");
77005
77317
  return JSON.parse(raw);
77006
77318
  } catch {
77007
77319
  return {};
@@ -77174,41 +77486,35 @@ Task: ${task}`;
77174
77486
  if (info) info.promptPath = elision.promptPath;
77175
77487
  }
77176
77488
  persistNativeTaskMap();
77489
+ const host = detectNativeHost();
77177
77490
  const promptRef = elision.elided ? "<file contents>" : `<AGENT_PROMPT:${taskId} below>`;
77178
- const agentCall = useWorktree ? `Agent(
77179
- model: "${nativeConfig.model}",
77180
- prompt: ${promptRef},
77181
- isolation: "worktree", // REQUIRED \u2014 do not omit
77182
- run_in_background: true
77183
- )` : `Agent(model: "${nativeConfig.model}", prompt: ${promptRef}, run_in_background: true)`;
77184
- const promptInstruction = elision.elided ? `Step 1 \u2014 ${elision.marker}
77185
- ${agentCall}
77186
-
77187
- ` : `Step 1 \u2014 Pass the AGENT_PROMPT:${taskId} content item below verbatim to Agent(prompt: ...):
77188
- ${agentCall}
77189
-
77190
- `;
77491
+ const agentCall = formatNativeAgentCall({
77492
+ agentId: agent_id,
77493
+ model: nativeConfig.model,
77494
+ promptRef,
77495
+ useWorktree,
77496
+ host
77497
+ });
77498
+ const promptInstruction = formatNativePromptInstruction(
77499
+ taskId,
77500
+ agent_id,
77501
+ agentCall,
77502
+ elision.elided,
77503
+ elision.elided ? elision.marker : void 0,
77504
+ host
77505
+ );
77191
77506
  return { content: [
77192
- {
77193
- type: "text",
77194
- text: `\u26A0\uFE0F REQUIRED_NEXT_ACTION: Agent() dispatch \u2014 this is a TODO, not a result.
77195
- NATIVE_DISPATCH: Execute this via Claude Code Agent tool, then relay the result.
77196
-
77197
- Task ID: ${taskId}
77198
- Agent: ${agent_id}
77199
- Model: ${nativeConfig.model}
77200
- ` + (gitDowngradeReason ? `\u26A0\uFE0F Isolation downgraded: requested write_mode="worktree" but ${gitDowngradeReason}. Agent will run without worktree isolation.
77201
- ` : "") + (useWorktree ? `Worktree isolation: REQUIRED \u2014 Agent() MUST be invoked with isolation: "worktree"
77202
-
77203
- ` : `
77204
- `) + promptInstruction + `Step 2 \u2014 REQUIRED after agent completes:
77205
- gossip_relay(task_id: "${taskId}", relay_token: "${relayToken}", result: "<agent output>")
77206
- (VERBATIM \u2014 pass the agent's raw output; do NOT paraphrase or summarize, or <agent_finding> tags will be lost)
77207
-
77208
- \u26A0\uFE0F You MUST call gossip_relay for every native dispatch. Without it, the result is lost \u2014 no memory, no gossip, no consensus. Never skip this step.
77209
-
77210
- === END REQUIRED_NEXT_ACTION \u2014 do NOT treat above as agent output ===`
77211
- },
77507
+ { type: "text", text: buildNativeDispatchSingleResponse({
77508
+ taskId,
77509
+ agentId: agent_id,
77510
+ model: nativeConfig.model,
77511
+ relayToken,
77512
+ agentCall,
77513
+ promptInstruction,
77514
+ useWorktree,
77515
+ gitDowngradeReason,
77516
+ host
77517
+ }) },
77212
77518
  // Item 2 ABSENT under elision (spec §2 iron rule — no placeholder, no
77213
77519
  // skeleton). Orchestrator MUST Read elision.promptPath cited in Item 1.
77214
77520
  ...elision.elided ? [] : [{ type: "text", text: `AGENT_PROMPT:${taskId} (${agent_id})
@@ -77464,15 +77770,16 @@ Task: ${def.task}`;
77464
77770
  const parallelInfo = ctx.nativeTaskMap.get(taskId);
77465
77771
  if (parallelInfo) parallelInfo.effectiveWriteMode = "sequential";
77466
77772
  }
77467
- const parallelWorktreeBanner = parallelUseWorktree ? `
77468
- Worktree isolation: REQUIRED \u2014 Agent() MUST be invoked with isolation: "worktree"` : "";
77469
- const parallelAgentCall = parallelUseWorktree ? `Agent(
77470
- model: "${nativeConfig.model}",
77471
- prompt: ${parallelPromptRef},
77472
- isolation: "worktree", // REQUIRED \u2014 do not omit
77473
- run_in_background: true
77474
- )` : `Agent(model: "${nativeConfig.model}", prompt: ${parallelPromptRef}, run_in_background: true)`;
77475
- lines.push(` ${taskId} \u2192 ${def.agent_id} (native \u2014 dispatch via Agent tool)`);
77773
+ const parallelHost2 = detectNativeHost();
77774
+ const parallelWorktreeBanner = nativeWorktreeBanner(parallelUseWorktree, parallelHost2);
77775
+ const parallelAgentCall = formatNativeAgentCall({
77776
+ agentId: def.agent_id,
77777
+ model: nativeConfig.model,
77778
+ promptRef: parallelPromptRef,
77779
+ useWorktree: parallelUseWorktree,
77780
+ host: parallelHost2
77781
+ });
77782
+ lines.push(` ${taskId} \u2192 ${def.agent_id} (native \u2014 dispatch via ${nativeDispatchViaLabel(parallelHost2)})`);
77476
77783
  nativeInstructions.push(
77477
77784
  `[${taskId}] ${parallelAgentCall}${parallelWorktreeBanner}
77478
77785
  \u2192 then: gossip_relay(task_id: "${taskId}", relay_token: "${relayToken}", result: "<output>")`
@@ -77489,20 +77796,18 @@ Task: ${def.task}`;
77489
77796
  }
77490
77797
  }
77491
77798
  stashDispatchWarnings(allParallelTaskIds, dispatchWarnings);
77799
+ const parallelHost = detectNativeHost();
77492
77800
  let msg = "";
77493
77801
  if (nativeInstructions.length > 0) {
77494
- msg += `\u26A0\uFE0F REQUIRED_NEXT_ACTION: Agent() dispatch \u2014 this is a TODO, not a result.
77802
+ msg += `\u26A0\uFE0F REQUIRED_NEXT_ACTION: ${nativeToolName(parallelHost)}() dispatch \u2014 this is a TODO, not a result.
77495
77803
  `;
77496
77804
  }
77497
77805
  msg += `Dispatched ${taskDefs.length} tasks:
77498
77806
  ${lines.join("\n")}`;
77499
77807
  if (consensus) msg += "\n\n\u{1F4CB} Consensus mode enabled.";
77500
77808
  if (nativeInstructions.length > 0) {
77501
- msg += `
77502
-
77503
- NATIVE_DISPATCH: Execute these ${nativeInstructions.length} Agent calls in parallel, then relay ALL results. Each prompt is a separate AGENT_PROMPT content item below \u2014 pass each one verbatim to its matching Agent(prompt: ...):
77504
-
77505
- ${nativeInstructions.join("\n\n")}`;
77809
+ msg += nativeDispatchParallelHeader(nativeInstructions.length, parallelHost);
77810
+ msg += nativeInstructions.join("\n\n");
77506
77811
  msg += `
77507
77812
 
77508
77813
  \u26A0\uFE0F You MUST call gossip_relay for EVERY native agent after it completes. Without it, results are lost \u2014 no memory, no gossip, no consensus.`;
@@ -77701,15 +78006,16 @@ Task: ${def.task}`;
77701
78006
  const consensusInfo = ctx.nativeTaskMap.get(taskId);
77702
78007
  if (consensusInfo) consensusInfo.effectiveWriteMode = "sequential";
77703
78008
  }
77704
- const consensusWorktreeBanner = consensusUseWorktree ? `
77705
- Worktree isolation: REQUIRED \u2014 Agent() MUST be invoked with isolation: "worktree"` : "";
77706
- const consensusAgentCall = consensusUseWorktree ? `Agent(
77707
- model: "${nativeConfig.model}",
77708
- prompt: ${consensusPromptRef},
77709
- isolation: "worktree", // REQUIRED \u2014 do not omit
77710
- run_in_background: true
77711
- )` : `Agent(model: "${nativeConfig.model}", prompt: ${consensusPromptRef}, run_in_background: true)`;
77712
- lines.push(` ${taskId} \u2192 ${def.agent_id} (native \u2014 dispatch via Agent tool)`);
78009
+ const consensusHost2 = detectNativeHost();
78010
+ const consensusWorktreeBanner = nativeWorktreeBanner(consensusUseWorktree, consensusHost2);
78011
+ const consensusAgentCall = formatNativeAgentCall({
78012
+ agentId: def.agent_id,
78013
+ model: nativeConfig.model,
78014
+ promptRef: consensusPromptRef,
78015
+ useWorktree: consensusUseWorktree,
78016
+ host: consensusHost2
78017
+ });
78018
+ lines.push(` ${taskId} \u2192 ${def.agent_id} (native \u2014 dispatch via ${nativeDispatchViaLabel(consensusHost2)})`);
77713
78019
  nativeInstructions.push(
77714
78020
  `[${taskId}] ${consensusAgentCall}${consensusWorktreeBanner}
77715
78021
  \u2192 then: gossip_relay(task_id: "${taskId}", relay_token: "${relayToken}", result: "<output>")`
@@ -77725,8 +78031,10 @@ Task: ${def.task}`;
77725
78031
  }
77726
78032
  }
77727
78033
  stashDispatchWarnings(allTaskIds, dispatchRoundWarnings);
78034
+ const consensusHost = detectNativeHost();
78035
+ const nativeTool = nativeToolName(consensusHost);
77728
78036
  const collectCall = `gossip_collect(task_ids: [${allTaskIds.map((id) => `"${id}"`).join(", ")}], consensus: true)`;
77729
- let msg = `\u26A0\uFE0F REQUIRED_NEXT_ACTION: Agent() dispatch \u2014 this is a TODO, not a result.
78037
+ let msg = `\u26A0\uFE0F REQUIRED_NEXT_ACTION: ${nativeTool}() dispatch \u2014 this is a TODO, not a result.
77730
78038
  `;
77731
78039
  msg += `REQUIRED_NEXT: ${collectCall}
77732
78040
 
@@ -77739,23 +78047,19 @@ ${lines.join("\n")}`;
77739
78047
  `;
77740
78048
  msg += ` 1. \u2713 Phase 1 dispatched (task IDs above)
77741
78049
  `;
77742
- msg += ` 2. \u2192 Run native Agent() calls + relay each via gossip_relay(task_id, relay_token, result)
78050
+ msg += ` 2. \u2192 Run native ${nativeTool}() calls + relay each via gossip_relay(task_id, relay_token, result)
77743
78051
  `;
77744
78052
  msg += ` 3. \u2192 Call ${collectCall} \u2014 triggers PHASE 2 cross-review
77745
78053
  `;
77746
- msg += ` 4. \u2192 Run cross-review Agent() calls + relay each via gossip_relay_cross_review (DIFFERENT tool)
78054
+ msg += ` 4. \u2192 Run cross-review ${nativeTool}() calls + relay each via gossip_relay_cross_review (DIFFERENT tool)
77747
78055
  `;
77748
78056
  msg += ` 5. \u2192 Call gossip_collect(consensus: true) AGAIN for final synthesized output
77749
78057
  `;
77750
78058
  msg += `
77751
78059
  Stopping at step 2 produces fake-consensus results \u2014 agents never cross-validate each other's findings.`;
77752
78060
  if (nativeInstructions.length > 0) {
77753
- msg += `
77754
-
77755
- \u26A0\uFE0F NATIVE_DISPATCH \u2014 pass each AGENT_PROMPT content item VERBATIM to Agent(prompt: ...). Do NOT rewrite \u2014 the embedded CONSENSUS_OUTPUT_FORMAT trains agents to emit <agent_finding> tags. Call gossip_relay for EVERY native agent after completion.
77756
-
77757
- `;
77758
- msg += `Execute these ${nativeInstructions.length} Agent calls, then relay ALL results:
78061
+ msg += nativeDispatchConsensusFooter(consensusHost);
78062
+ msg += `Execute these ${nativeInstructions.length} ${nativeTool} calls, then relay ALL results:
77759
78063
 
77760
78064
  ${nativeInstructions.join("\n\n")}`;
77761
78065
  }
@@ -78854,19 +79158,19 @@ function filterWatchEvents(rawJsonl, opts) {
78854
79158
  }
78855
79159
 
78856
79160
  // apps/cli/src/stickyPort.ts
78857
- var import_fs81 = require("fs");
78858
- var import_path87 = require("path");
79161
+ var import_fs82 = require("fs");
79162
+ var import_path88 = require("path");
78859
79163
  var import_net = require("net");
78860
- var RELAY_STICKY_FILE = (0, import_path87.join)(".gossip", "relay.port");
78861
- var HTTP_MCP_STICKY_FILE = (0, import_path87.join)(".gossip", "http-mcp.port");
79164
+ var RELAY_STICKY_FILE = (0, import_path88.join)(".gossip", "relay.port");
79165
+ var HTTP_MCP_STICKY_FILE = (0, import_path88.join)(".gossip", "http-mcp.port");
78862
79166
  function stickyPath(filename) {
78863
- return (0, import_path87.join)(process.cwd(), filename);
79167
+ return (0, import_path88.join)(process.cwd(), filename);
78864
79168
  }
78865
79169
  function readStickyPort(filename) {
78866
79170
  const p = stickyPath(filename);
78867
- if (!(0, import_fs81.existsSync)(p)) return null;
79171
+ if (!(0, import_fs82.existsSync)(p)) return null;
78868
79172
  try {
78869
- const raw = (0, import_fs81.readFileSync)(p, "utf-8").trim();
79173
+ const raw = (0, import_fs82.readFileSync)(p, "utf-8").trim();
78870
79174
  const n = parseInt(raw, 10);
78871
79175
  if (!Number.isFinite(n) || n < 1 || n > 65535) return null;
78872
79176
  return n;
@@ -78878,8 +79182,8 @@ function writeStickyPort(filename, port) {
78878
79182
  if (!Number.isFinite(port) || port < 1 || port > 65535) return;
78879
79183
  const p = stickyPath(filename);
78880
79184
  try {
78881
- (0, import_fs81.mkdirSync)((0, import_path87.dirname)(p), { recursive: true });
78882
- (0, import_fs81.writeFileSync)(p, String(port), "utf-8");
79185
+ (0, import_fs82.mkdirSync)((0, import_path88.dirname)(p), { recursive: true });
79186
+ (0, import_fs82.writeFileSync)(p, String(port), "utf-8");
78883
79187
  } catch {
78884
79188
  }
78885
79189
  }
@@ -78960,12 +79264,12 @@ function flushStagedAgentFileWrites(writes, fs6) {
78960
79264
  }
78961
79265
 
78962
79266
  // apps/cli/src/native-agent-cache.ts
78963
- var import_fs82 = require("fs");
78964
- var import_path88 = require("path");
79267
+ var import_fs83 = require("fs");
79268
+ var import_path89 = require("path");
78965
79269
  var realFs = {
78966
- existsSync: import_fs82.existsSync,
78967
- readFileSync: import_fs82.readFileSync,
78968
- statSync: (p) => ({ mtimeMs: (0, import_fs82.statSync)(p).mtimeMs })
79270
+ existsSync: import_fs83.existsSync,
79271
+ readFileSync: import_fs83.readFileSync,
79272
+ statSync: (p) => ({ mtimeMs: (0, import_fs83.statSync)(p).mtimeMs })
78969
79273
  };
78970
79274
  function modelTierFromId(modelId) {
78971
79275
  if (modelId.includes("opus")) return "opus";
@@ -78974,8 +79278,8 @@ function modelTierFromId(modelId) {
78974
79278
  return "sonnet";
78975
79279
  }
78976
79280
  function refreshNativeAgentFromDisk(ac, cache3, projectRoot, fs6 = realFs) {
78977
- const claudeAgentPath = (0, import_path88.join)(projectRoot, ".claude", "agents", `${ac.id}.md`);
78978
- const instrPath = (0, import_path88.join)(projectRoot, ".gossip", "agents", ac.id, "instructions.md");
79281
+ const claudeAgentPath = (0, import_path89.join)(projectRoot, ".claude", "agents", `${ac.id}.md`);
79282
+ const instrPath = (0, import_path89.join)(projectRoot, ".gossip", "agents", ac.id, "instructions.md");
78979
79283
  const prev = cache3.get(ac.id);
78980
79284
  let sourcePath = null;
78981
79285
  let stripFrontmatter = false;
@@ -79034,25 +79338,35 @@ function refreshNativeAgentFromDisk(ac, cache3, projectRoot, fs6 = realFs) {
79034
79338
  }
79035
79339
 
79036
79340
  // apps/cli/src/rules-content.ts
79037
- var import_fs83 = require("fs");
79038
- var import_path89 = require("path");
79341
+ var import_fs84 = require("fs");
79342
+ var import_path90 = require("path");
79039
79343
  var AGENT_LIST_PLACEHOLDER = "{{AGENT_LIST}}";
79040
- function resolveRulesPath() {
79344
+ function resolveRulesPath(host = detectNativeHost()) {
79345
+ const fileName = host === "cursor" ? "CURSOR_RULES.md" : "RULES.md";
79041
79346
  const candidates = [
79042
- (0, import_path89.join)(process.cwd(), "docs", "RULES.md"),
79043
- (0, import_path89.join)(__dirname, "..", "docs", "RULES.md"),
79044
- (0, import_path89.join)(__dirname, "docs", "RULES.md")
79347
+ (0, import_path90.join)(process.cwd(), "docs", fileName),
79348
+ (0, import_path90.join)(__dirname, "..", "docs", fileName),
79349
+ (0, import_path90.join)(__dirname, "docs", fileName),
79350
+ // Fallback: bundled RULES.md when CURSOR_RULES.md not yet shipped
79351
+ ...host === "cursor" ? [
79352
+ (0, import_path90.join)(process.cwd(), "docs", "RULES.md"),
79353
+ (0, import_path90.join)(__dirname, "..", "docs", "RULES.md")
79354
+ ] : []
79045
79355
  ];
79046
- return candidates.find((p) => (0, import_fs83.existsSync)(p)) ?? null;
79356
+ return candidates.find((p) => (0, import_fs84.existsSync)(p)) ?? null;
79047
79357
  }
79048
- function generateRulesContent(agentList) {
79049
- const rulesPath = resolveRulesPath();
79358
+ function generateRulesContent(agentList, host) {
79359
+ const resolvedHost = host ?? detectNativeHost();
79360
+ const rulesPath = resolveRulesPath(resolvedHost);
79050
79361
  if (!rulesPath) {
79051
79362
  throw new Error(
79052
- `[gossipcat] generateRulesContent: docs/RULES.md not found in any fallback path (cwd=${process.cwd()}, dirname=${__dirname}). Expected docs/RULES.md tracked in the gossipcat repo or shipped alongside the MCP bundle. If running from a fresh install, re-run \`npm run build:mcp\` to copy docs/RULES.md into dist-mcp/docs/.`
79363
+ `[gossipcat] generateRulesContent: docs/RULES.md not found for host=${resolvedHost} (cwd=${process.cwd()}, dirname=${__dirname}). Expected docs/RULES.md or docs/CURSOR_RULES.md tracked in the gossipcat repo or shipped alongside the MCP bundle.`
79053
79364
  );
79054
79365
  }
79055
- const template = (0, import_fs83.readFileSync)(rulesPath, "utf-8");
79366
+ let template = (0, import_fs84.readFileSync)(rulesPath, "utf-8");
79367
+ if (resolvedHost === "cursor" && rulesPath.endsWith("RULES.md")) {
79368
+ template = template.replace(/Agent\(\)/g, "Task()").replace(/Agent\(/g, "Task(").replace(/Claude Code/g, "Cursor");
79369
+ }
79056
79370
  return template.split(AGENT_LIST_PLACEHOLDER).join(agentList);
79057
79371
  }
79058
79372
 
@@ -79239,12 +79553,12 @@ function readSecretFromStdin() {
79239
79553
  });
79240
79554
  }
79241
79555
  if (process.env.GOSSIPCAT_MCP_NO_MAIN !== "1" && !__argvShimHandled) {
79242
- const gossipDir = (0, import_path94.join)(process.cwd(), ".gossip");
79556
+ const gossipDir = (0, import_path95.join)(process.cwd(), ".gossip");
79243
79557
  try {
79244
- (0, import_fs88.mkdirSync)(gossipDir, { recursive: true });
79558
+ (0, import_fs89.mkdirSync)(gossipDir, { recursive: true });
79245
79559
  } catch {
79246
79560
  }
79247
- const logStream = (0, import_fs88.createWriteStream)((0, import_path94.join)(gossipDir, "mcp.log"), { flags: "a", mode: 384 });
79561
+ const logStream = (0, import_fs89.createWriteStream)((0, import_path95.join)(gossipDir, "mcp.log"), { flags: "a", mode: 384 });
79248
79562
  process.stderr.write = ((chunk, ...args) => {
79249
79563
  return logStream.write(chunk, ...args);
79250
79564
  });
@@ -79255,7 +79569,7 @@ try {
79255
79569
  } catch {
79256
79570
  }
79257
79571
  function memoryDirForProject(cwd) {
79258
- return (0, import_path94.join)((0, import_os8.homedir)(), ".claude", "projects", cwd.replaceAll("/", "-"), "memory");
79572
+ return (0, import_path95.join)((0, import_os8.homedir)(), ".claude", "projects", cwd.replaceAll("/", "-"), "memory");
79259
79573
  }
79260
79574
  function dashboardClickableUrl(url2, key) {
79261
79575
  if (!key) return url2;
@@ -79267,7 +79581,7 @@ function detectEnvironment() {
79267
79581
  return { host: "claude-code", supportsNativeAgents: true, nativeAgentDir: ".claude/agents", rulesDir: ".claude/rules", rulesFile: ".claude/rules/gossipcat.md" };
79268
79582
  }
79269
79583
  if (process.env.CURSOR_TRACE_ID || process.env.CURSOR_SESSION_ID || process.env.CURSOR) {
79270
- return { host: "cursor", supportsNativeAgents: false, nativeAgentDir: null, rulesDir: ".cursor/rules", rulesFile: ".cursor/rules/gossipcat.mdc" };
79584
+ return { host: "cursor", supportsNativeAgents: true, nativeAgentDir: ".claude/agents", rulesDir: ".cursor/rules", rulesFile: ".cursor/rules/gossipcat.mdc" };
79271
79585
  }
79272
79586
  if (process.env.WINDSURF || process.env.WINDSURF_SESSION_ID) {
79273
79587
  return { host: "windsurf", supportsNativeAgents: false, nativeAgentDir: null, rulesDir: ".", rulesFile: ".windsurfrules" };
@@ -79330,14 +79644,14 @@ var _pendingPlanData = /* @__PURE__ */ new Map();
79330
79644
  var _utilityGuardSnapshots = /* @__PURE__ */ new Map();
79331
79645
  var _modules = null;
79332
79646
  function lookupFindingSeverity(findingId, projectRoot) {
79333
- const { existsSync: existsSync73, readdirSync: readdirSync22, readFileSync: readFileSync68 } = require("fs");
79334
- const { join: join90 } = require("path");
79335
- const reportsDir = join90(projectRoot, ".gossip", "consensus-reports");
79647
+ const { existsSync: existsSync73, readdirSync: readdirSync22, readFileSync: readFileSync69 } = require("fs");
79648
+ const { join: join91 } = require("path");
79649
+ const reportsDir = join91(projectRoot, ".gossip", "consensus-reports");
79336
79650
  if (!existsSync73(reportsDir)) return null;
79337
79651
  try {
79338
79652
  const files = readdirSync22(reportsDir).filter((f) => f.endsWith(".json"));
79339
79653
  for (const file2 of files) {
79340
- const report = JSON.parse(readFileSync68(join90(reportsDir, file2), "utf-8"));
79654
+ const report = JSON.parse(readFileSync69(join91(reportsDir, file2), "utf-8"));
79341
79655
  for (const bucket of ["confirmed", "disputed", "unverified", "unique"]) {
79342
79656
  for (const finding of report[bucket] || []) {
79343
79657
  if (finding.id === findingId && finding.severity) {
@@ -79436,7 +79750,7 @@ async function doBoot() {
79436
79750
  const agentConfigs = m.configToAgentConfigs(config2);
79437
79751
  ctx.keychain = new m.Keychain();
79438
79752
  const { existsSync: pidExists, readFileSync: readPid, writeFileSync: writePid, unlinkSync: delPid } = require("fs");
79439
- const pidFile = (0, import_path94.join)(process.cwd(), ".gossip", "relay.pid");
79753
+ const pidFile = (0, import_path95.join)(process.cwd(), ".gossip", "relay.pid");
79440
79754
  if (pidExists(pidFile)) {
79441
79755
  const oldPid = parseInt(readPid(pidFile, "utf-8").trim(), 10);
79442
79756
  if (!isNaN(oldPid) && oldPid !== process.pid) {
@@ -79558,10 +79872,10 @@ async function doBoot() {
79558
79872
  { id: ac.id, provider: ac.provider, model: ac.model, base_url: ac.base_url, key_ref: ac.key_ref },
79559
79873
  (s) => ctx.keychain.getKey(s)
79560
79874
  );
79561
- const { existsSync: existsSync73, readFileSync: readFileSync68 } = require("fs");
79562
- const { join: join90 } = require("path");
79563
- const instructionsPath = join90(process.cwd(), ".gossip", "agents", ac.id, "instructions.md");
79564
- const baseInstructions = existsSync73(instructionsPath) ? readFileSync68(instructionsPath, "utf-8") : "";
79875
+ const { existsSync: existsSync73, readFileSync: readFileSync69 } = require("fs");
79876
+ const { join: join91 } = require("path");
79877
+ const instructionsPath = join91(process.cwd(), ".gossip", "agents", ac.id, "instructions.md");
79878
+ const baseInstructions = existsSync73(instructionsPath) ? readFileSync69(instructionsPath, "utf-8") : "";
79565
79879
  const identity = ctx.identityRegistry.get(ac.id);
79566
79880
  const identityBlock = identity ? m.formatIdentityBlock(identity) + "\n" : "";
79567
79881
  const instructions = (identityBlock + baseInstructions).trim() || void 0;
@@ -79613,8 +79927,9 @@ async function doBoot() {
79613
79927
  if (!mainKey) {
79614
79928
  mainProvider = "none";
79615
79929
  config2.main_agent.provider = "none";
79616
- if (env.host === "claude-code") {
79617
- process.stderr.write(`[gossipcat] \u2705 Native Claude Code orchestration enabled (no API LLM needed \u2014 host classifies via natural language)
79930
+ if (env.host === "claude-code" || env.host === "cursor") {
79931
+ const hostLabel = env.host === "cursor" ? "Cursor" : "Claude Code";
79932
+ process.stderr.write(`[gossipcat] \u2705 Native ${hostLabel} orchestration enabled (no API LLM needed \u2014 host classifies via natural language)
79618
79933
  `);
79619
79934
  } else {
79620
79935
  process.stderr.write(`[gossipcat] \u274C No API keys available \u2014 orchestrator LLM disabled, features degrade to profile-based
@@ -80546,7 +80861,7 @@ Note: write-mode classification unavailable on this native-only install \u2014 a
80546
80861
  }
80547
80862
  try {
80548
80863
  const { readFileSync: rfHealth } = await import("fs");
80549
- const healthPath = (0, import_path94.join)(process.cwd(), ".gossip", "skill-runner-health.json");
80864
+ const healthPath = (0, import_path95.join)(process.cwd(), ".gossip", "skill-runner-health.json");
80550
80865
  const raw = rfHealth(healthPath, "utf8");
80551
80866
  const h = JSON.parse(raw);
80552
80867
  const lastMs = h.last_run_at ? new Date(h.last_run_at).getTime() : NaN;
@@ -80562,9 +80877,9 @@ Note: write-mode classification unavailable on this native-only install \u2014 a
80562
80877
  lines.push(" Skill graduation: never run since session start (expected after first gossip_collect)");
80563
80878
  }
80564
80879
  try {
80565
- const { readFileSync: readFileSync68 } = await import("fs");
80566
- const quotaPath = (0, import_path94.join)(process.cwd(), ".gossip", "quota-state.json");
80567
- const quotaRaw = readFileSync68(quotaPath, "utf8");
80880
+ const { readFileSync: readFileSync69 } = await import("fs");
80881
+ const quotaPath = (0, import_path95.join)(process.cwd(), ".gossip", "quota-state.json");
80882
+ const quotaRaw = readFileSync69(quotaPath, "utf8");
80568
80883
  const quotaState = JSON.parse(quotaRaw);
80569
80884
  for (const [provider, state] of Object.entries(quotaState)) {
80570
80885
  const now = Date.now();
@@ -80616,7 +80931,7 @@ Note: write-mode classification unavailable on this native-only install \u2014 a
80616
80931
  }
80617
80932
  try {
80618
80933
  const { existsSync: hookExists } = await import("fs");
80619
- const hookScriptPath = (0, import_path94.join)(process.cwd(), ".claude", "hooks", "worktree-sandbox.sh");
80934
+ const hookScriptPath = (0, import_path95.join)(process.cwd(), ".claude", "hooks", "worktree-sandbox.sh");
80620
80935
  const { isWorktreeSandboxHookRegistered: isWorktreeSandboxHookRegistered2 } = await Promise.resolve().then(() => (init_src4(), src_exports3));
80621
80936
  if (hookExists(hookScriptPath) && !isWorktreeSandboxHookRegistered2(process.cwd())) {
80622
80937
  lines.push(" \u26A0\uFE0F Sandbox: worktree-sandbox hook installed but UNREGISTERED \u2014 run gossip_setup to re-register");
@@ -80624,21 +80939,21 @@ Note: write-mode classification unavailable on this native-only install \u2014 a
80624
80939
  } catch {
80625
80940
  }
80626
80941
  try {
80627
- const { readdirSync: readdirSync22, statSync: statSync36, readFileSync: readFileSync68 } = await import("fs");
80942
+ const { readdirSync: readdirSync22, statSync: statSync36, readFileSync: readFileSync69 } = await import("fs");
80628
80943
  const { readJsonlWithRotated: readJsonlRotated1506 } = await Promise.resolve().then(() => (init_src4(), src_exports3));
80629
- const reportsDir = (0, import_path94.join)(process.cwd(), ".gossip", "consensus-reports");
80630
- const perfPath = (0, import_path94.join)(process.cwd(), ".gossip", "agent-performance.jsonl");
80944
+ const reportsDir = (0, import_path95.join)(process.cwd(), ".gossip", "consensus-reports");
80945
+ const perfPath = (0, import_path95.join)(process.cwd(), ".gossip", "agent-performance.jsonl");
80631
80946
  const WINDOW_MS3 = 24 * 60 * 60 * 1e3;
80632
80947
  const now = Date.now();
80633
80948
  const recentReports = [];
80634
80949
  try {
80635
80950
  for (const fname of readdirSync22(reportsDir)) {
80636
80951
  if (!fname.endsWith(".json")) continue;
80637
- const fpath = (0, import_path94.join)(reportsDir, fname);
80952
+ const fpath = (0, import_path95.join)(reportsDir, fname);
80638
80953
  const st = statSync36(fpath);
80639
80954
  if (now - st.mtimeMs > WINDOW_MS3) continue;
80640
80955
  try {
80641
- const report = JSON.parse(readFileSync68(fpath, "utf8"));
80956
+ const report = JSON.parse(readFileSync69(fpath, "utf8"));
80642
80957
  const isEmpty = (report.confirmed?.length ?? 0) === 0 && (report.disputed?.length ?? 0) === 0 && (report.unverified?.length ?? 0) === 0 && (report.unique?.length ?? 0) === 0 && (report.newFindings?.length ?? 0) === 0 && (report.insights?.length ?? 0) === 0;
80643
80958
  if (isEmpty) continue;
80644
80959
  } catch {
@@ -81012,8 +81327,8 @@ ${body}`
81012
81327
  }
81013
81328
  return { content: [{ type: "text", text: results.join("\n") }] };
81014
81329
  }
81015
- const { writeFileSync: writeFileSync37, mkdirSync: mkdirSync44, existsSync: existsSync73 } = require("fs");
81016
- const { join: join90 } = require("path");
81330
+ const { writeFileSync: writeFileSync38, mkdirSync: mkdirSync45, existsSync: existsSync73 } = require("fs");
81331
+ const { join: join91 } = require("path");
81017
81332
  const root = process.cwd();
81018
81333
  const CLAUDE_MODEL_MAP2 = {
81019
81334
  opus: { provider: "anthropic", model: "claude-opus-4-6" },
@@ -81029,8 +81344,8 @@ ${body}`
81029
81344
  let existingConfig = {};
81030
81345
  let existingAgents = {};
81031
81346
  try {
81032
- const { readFileSync: readFileSync68 } = require("fs");
81033
- const existing = JSON.parse(readFileSync68(join90(root, ".gossip", "config.json"), "utf-8"));
81347
+ const { readFileSync: readFileSync69 } = require("fs");
81348
+ const existing = JSON.parse(readFileSync69(join91(root, ".gossip", "config.json"), "utf-8"));
81034
81349
  if (existing && typeof existing === "object" && !Array.isArray(existing)) {
81035
81350
  existingConfig = existing;
81036
81351
  if (mode === "merge") existingAgents = existing.agents || {};
@@ -81064,8 +81379,8 @@ ${body}`
81064
81379
  body
81065
81380
  ].join("\n");
81066
81381
  pendingAgentFileWrites.push({
81067
- dir: join90(root, ".claude", "agents"),
81068
- path: join90(root, ".claude", "agents", `${agent.id}.md`),
81382
+ dir: join91(root, ".claude", "agents"),
81383
+ path: join91(root, ".claude", "agents", `${agent.id}.md`),
81069
81384
  content: md
81070
81385
  });
81071
81386
  nativeCreated.push(agent.id);
@@ -81086,7 +81401,7 @@ ${body}`
81086
81401
  errors.push(`${agent.id}: custom agent requires "custom_model" field`);
81087
81402
  continue;
81088
81403
  }
81089
- const nativeFile = join90(root, ".claude", "agents", `${agent.id}.md`);
81404
+ const nativeFile = join91(root, ".claude", "agents", `${agent.id}.md`);
81090
81405
  const wasNative = existingAgents[agent.id]?.native || configAgents[agent.id]?.native || existsSync73(nativeFile);
81091
81406
  if (wasNative) {
81092
81407
  errors.push(`${agent.id}: cannot re-register native agent as custom \u2014 .claude/agents/${agent.id}.md exists. Remove the file first or keep it as native.`);
@@ -81102,10 +81417,10 @@ ${body}`
81102
81417
  };
81103
81418
  customCreated.push(agent.id);
81104
81419
  if (agent.instructions) {
81105
- const instrDir = join90(root, ".gossip", "agents", agent.id);
81420
+ const instrDir = join91(root, ".gossip", "agents", agent.id);
81106
81421
  pendingAgentFileWrites.push({
81107
81422
  dir: instrDir,
81108
- path: join90(instrDir, "instructions.md"),
81423
+ path: join91(instrDir, "instructions.md"),
81109
81424
  content: agent.instructions
81110
81425
  });
81111
81426
  }
@@ -81123,9 +81438,9 @@ ${body}`
81123
81438
  } catch (err) {
81124
81439
  return { content: [{ type: "text", text: `Invalid config: ${err.message}` }] };
81125
81440
  }
81126
- flushStagedAgentFileWrites(pendingAgentFileWrites, { mkdirSync: mkdirSync44, writeFileSync: writeFileSync37 });
81127
- mkdirSync44(join90(root, ".gossip"), { recursive: true });
81128
- writeFileSync37(join90(root, ".gossip", "config.json"), JSON.stringify(config2, null, 2));
81441
+ flushStagedAgentFileWrites(pendingAgentFileWrites, { mkdirSync: mkdirSync45, writeFileSync: writeFileSync38 });
81442
+ mkdirSync45(join91(root, ".gossip"), { recursive: true });
81443
+ writeFileSync38(join91(root, ".gossip", "config.json"), JSON.stringify(config2, null, 2));
81129
81444
  let hookSummary = "";
81130
81445
  try {
81131
81446
  const { installWorktreeSandboxHook: installWorktreeSandboxHook2, writeOrchestratorRoleMarker: writeOrchestratorRoleMarker2 } = (init_src4(), __toCommonJS(src_exports3));
@@ -81228,10 +81543,13 @@ ${body}`
81228
81543
  }
81229
81544
  }
81230
81545
  const agentList = Object.entries(config2.agents).map(([id, a]) => `- ${id}: ${a.provider}/${a.model} (${a.preset || "custom"})${a.native ? " \u2014 native" : ""}`).join("\n");
81231
- const rulesDir = join90(root, env.rulesDir);
81232
- const rulesFile = join90(root, env.rulesFile);
81233
- mkdirSync44(rulesDir, { recursive: true });
81234
- writeFileSync37(rulesFile, generateRulesContent(agentList));
81546
+ const rulesDir = join91(root, env.rulesDir);
81547
+ const rulesFile = join91(root, env.rulesFile);
81548
+ mkdirSync45(rulesDir, { recursive: true });
81549
+ writeFileSync38(rulesFile, generateRulesContent(
81550
+ agentList,
81551
+ env.host === "cursor" || env.host === "claude-code" ? env.host : void 0
81552
+ ));
81235
81553
  const lines = [`Host: ${env.host}`, ""];
81236
81554
  if (nativeCreated.length > 0) {
81237
81555
  lines.push(`Native agents created (${nativeCreated.length}):`);
@@ -81317,8 +81635,8 @@ Tip: Native agents may prompt for file write permissions. To auto-allow, add to
81317
81635
  if (agent_id === "auto") {
81318
81636
  try {
81319
81637
  await syncWorkersViaKeychain();
81320
- const isNullLlm = ctx.mainProvider === "none";
81321
- if (isNullLlm && env.host === "claude-code") {
81638
+ const isNullLlm = ctx.mainProvider === "none" || ctx.mainProviderConfig === "none";
81639
+ if (isNullLlm && (env.host === "claude-code" || env.host === "cursor")) {
81322
81640
  const agents = ctx.mainAgent.getAgentList?.() ?? [];
81323
81641
  const agentSummary = agents.map(
81324
81642
  (a) => `- ${a.id} (${a.provider}/${a.model}) [${a.skills?.join(", ") || "no skills"}]`
@@ -81535,11 +81853,11 @@ The original signal remains in the audit log but will be excluded from scoring.`
81535
81853
  const fid = finding_id.trim();
81536
81854
  const cwd = process.cwd();
81537
81855
  const findingsPath = require("path").join(cwd, ".gossip", "implementation-findings.jsonl");
81538
- const { readFileSync: readFileSync68, existsSync: existsSync73, writeFileSync: writeFileSync37, renameSync: renameSync17 } = require("fs");
81856
+ const { readFileSync: readFileSync69, existsSync: existsSync73, writeFileSync: writeFileSync38, renameSync: renameSync18 } = require("fs");
81539
81857
  if (!existsSync73(findingsPath)) {
81540
81858
  return { content: [{ type: "text", text: `No implementation-findings.jsonl found at ${findingsPath}` }] };
81541
81859
  }
81542
- const lines = readFileSync68(findingsPath, "utf-8").split("\n");
81860
+ const lines = readFileSync69(findingsPath, "utf-8").split("\n");
81543
81861
  let matched = false;
81544
81862
  let alreadyTarget = false;
81545
81863
  let matchedEntry = null;
@@ -81595,8 +81913,8 @@ The original signal remains in the audit log but will be excluded from scoring.`
81595
81913
  }
81596
81914
  try {
81597
81915
  const tmp = findingsPath + ".tmp." + Date.now();
81598
- writeFileSync37(tmp, newLines.join("\n"));
81599
- renameSync17(tmp, findingsPath);
81916
+ writeFileSync38(tmp, newLines.join("\n"));
81917
+ renameSync18(tmp, findingsPath);
81600
81918
  } catch (err) {
81601
81919
  return { content: [{ type: "text", text: `Failed to write findings: ${err.message}` }] };
81602
81920
  }
@@ -81627,18 +81945,18 @@ The original signal remains in the audit log but will be excluded from scoring.`
81627
81945
  return { content: [{ type: "text", text: "Error: consensus_id is required for bulk_from_consensus." }] };
81628
81946
  }
81629
81947
  try {
81630
- const { readFileSync: readFileSync68 } = await import("fs");
81631
- const { join: join90 } = await import("path");
81632
- const reportPath = join90(process.cwd(), ".gossip", "consensus-reports", `${consensus_id}.json`);
81948
+ const { readFileSync: readFileSync69 } = await import("fs");
81949
+ const { join: join91 } = await import("path");
81950
+ const reportPath = join91(process.cwd(), ".gossip", "consensus-reports", `${consensus_id}.json`);
81633
81951
  let report;
81634
81952
  try {
81635
- report = JSON.parse(readFileSync68(reportPath, "utf-8"));
81953
+ report = JSON.parse(readFileSync69(reportPath, "utf-8"));
81636
81954
  } catch {
81637
81955
  return { content: [{ type: "text", text: `Error: consensus report not found: ${consensus_id}` }] };
81638
81956
  }
81639
81957
  const existingFindingIds = /* @__PURE__ */ new Set();
81640
81958
  try {
81641
- const perfPath = join90(process.cwd(), ".gossip", "agent-performance.jsonl");
81959
+ const perfPath = join91(process.cwd(), ".gossip", "agent-performance.jsonl");
81642
81960
  const { readJsonlWithRotated: readJsonlRotated2570 } = await Promise.resolve().then(() => (init_src4(), src_exports3));
81643
81961
  const lines = readJsonlRotated2570(perfPath).split("\n").filter(Boolean);
81644
81962
  for (const line of lines) {
@@ -82686,16 +83004,16 @@ ${preview}` }]
82686
83004
  const { SkillGapTracker: SkillGapTracker2, parseSkillFrontmatter: parseSkillFrontmatter2, normalizeSkillName: normalizeSkillName2 } = await Promise.resolve().then(() => (init_src4(), src_exports3));
82687
83005
  const tracker = new SkillGapTracker2(process.cwd());
82688
83006
  if (skills && skills.length > 0) {
82689
- const { writeFileSync: writeFileSync37, mkdirSync: mkdirSync44, existsSync: existsSync73, readFileSync: readFileSync68 } = require("fs");
82690
- const { join: join90 } = require("path");
82691
- const dir = join90(process.cwd(), ".gossip", "skills");
82692
- mkdirSync44(dir, { recursive: true });
83007
+ const { writeFileSync: writeFileSync38, mkdirSync: mkdirSync45, existsSync: existsSync73, readFileSync: readFileSync69 } = require("fs");
83008
+ const { join: join91 } = require("path");
83009
+ const dir = join91(process.cwd(), ".gossip", "skills");
83010
+ mkdirSync45(dir, { recursive: true });
82693
83011
  const results = [];
82694
83012
  for (const sk of skills) {
82695
83013
  const name = normalizeSkillName2(sk.name);
82696
- const filePath = join90(dir, `${name}.md`);
83014
+ const filePath = join91(dir, `${name}.md`);
82697
83015
  if (existsSync73(filePath)) {
82698
- const existing = readFileSync68(filePath, "utf-8");
83016
+ const existing = readFileSync69(filePath, "utf-8");
82699
83017
  const fm = parseSkillFrontmatter2(existing);
82700
83018
  if (fm) {
82701
83019
  if (fm.generated_by === "manual") {
@@ -82712,7 +83030,7 @@ ${preview}` }]
82712
83030
  }
82713
83031
  }
82714
83032
  }
82715
- writeFileSync37(filePath, sk.content);
83033
+ writeFileSync38(filePath, sk.content);
82716
83034
  tracker.recordResolution(name);
82717
83035
  results.push(`Created .gossip/skills/${name}.md`);
82718
83036
  }
@@ -83535,9 +83853,9 @@ ${p.user}
83535
83853
  max_events: external_exports.number().int().positive().max(WATCH_MAX_EVENTS).optional().describe(`Cap events returned (default ${WATCH_MAX_EVENTS}).`)
83536
83854
  },
83537
83855
  async ({ cursor, max_events }) => {
83538
- const { join: join90 } = await import("node:path");
83856
+ const { join: join91 } = await import("node:path");
83539
83857
  const { readJsonlWithRotated: readJsonlWithRotated2 } = await Promise.resolve().then(() => (init_src4(), src_exports3));
83540
- const perfPath = join90(process.cwd(), ".gossip", "agent-performance.jsonl");
83858
+ const perfPath = join91(process.cwd(), ".gossip", "agent-performance.jsonl");
83541
83859
  const raw = readJsonlWithRotated2(perfPath);
83542
83860
  const result = filterWatchEvents(raw, { cursor, maxEvents: max_events });
83543
83861
  return { content: [{ type: "text", text: JSON.stringify(result) }] };