gossipcat 0.4.22 → 0.4.23

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.
@@ -3405,9 +3405,18 @@ var init_src2 = __esm({
3405
3405
  });
3406
3406
 
3407
3407
  // packages/orchestrator/src/task-stream.ts
3408
+ var TaskStreamEventType;
3408
3409
  var init_task_stream = __esm({
3409
3410
  "packages/orchestrator/src/task-stream.ts"() {
3410
3411
  "use strict";
3412
+ TaskStreamEventType = /* @__PURE__ */ ((TaskStreamEventType2) => {
3413
+ TaskStreamEventType2["LOG"] = "log";
3414
+ TaskStreamEventType2["PROGRESS"] = "progress";
3415
+ TaskStreamEventType2["PARTIAL_RESULT"] = "partial_result";
3416
+ TaskStreamEventType2["FINAL_RESULT"] = "final_result";
3417
+ TaskStreamEventType2["ERROR"] = "error";
3418
+ return TaskStreamEventType2;
3419
+ })(TaskStreamEventType || {});
3411
3420
  }
3412
3421
  });
3413
3422
 
@@ -8776,7 +8785,15 @@ var init_tool_server = __esm({
8776
8785
  }
8777
8786
  assignRoot(agentId, root) {
8778
8787
  const abs = (0, import_path8.resolve)(root);
8779
- this.agentRoots.set(agentId, canonicalizeForBoundary(abs));
8788
+ const canonical = canonicalizeForBoundary(abs);
8789
+ const existing = this.agentRoots.get(agentId);
8790
+ if (existing && existing !== canonical) {
8791
+ process.stderr.write(
8792
+ `[gossipcat] assignRoot collision: agent=${agentId} replacing root ${existing} \u2192 ${canonical} (Option B last-write-wins)
8793
+ `
8794
+ );
8795
+ }
8796
+ this.agentRoots.set(agentId, canonical);
8780
8797
  this.writeAgents.add(agentId);
8781
8798
  }
8782
8799
  releaseAgent(agentId) {
@@ -9447,118 +9464,6 @@ var init_types2 = __esm({
9447
9464
  }
9448
9465
  });
9449
9466
 
9450
- // packages/orchestrator/src/skill-parser.ts
9451
- function parseSkillFrontmatter(content) {
9452
- const match = content.match(/^---\n([\s\S]*?)\n---/);
9453
- if (!match) return null;
9454
- const lines = match[1].split("\n");
9455
- const fields = {};
9456
- for (const line of lines) {
9457
- const colonIdx = line.indexOf(":");
9458
- if (colonIdx === -1) continue;
9459
- const key = line.slice(0, colonIdx).trim();
9460
- let value = line.slice(colonIdx + 1).trim();
9461
- if (value.length >= 2 && (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'"))) {
9462
- value = value.slice(1, -1);
9463
- }
9464
- fields[key] = value;
9465
- }
9466
- if (!fields.name || !fields.description || !fields.status) return null;
9467
- let keywords = [];
9468
- if (fields.keywords) {
9469
- const raw = fields.keywords;
9470
- if (raw.startsWith("[") && raw.endsWith("]")) {
9471
- keywords = raw.slice(1, -1).split(",").map((k) => k.trim().replace(/^['"]|['"]$/g, "").slice(0, 100)).filter(Boolean);
9472
- } else {
9473
- keywords = raw.split(",").map((k) => k.trim().slice(0, 100)).filter(Boolean);
9474
- }
9475
- }
9476
- const rawTaskType = fields.task_type;
9477
- const task_type = rawTaskType === "review" || rawTaskType === "implement" || rawTaskType === "research" || rawTaskType === "any" ? rawTaskType : "any";
9478
- let scope;
9479
- if (fields.scope) {
9480
- const raw = fields.scope.trim();
9481
- let tokens;
9482
- if (raw.startsWith("[") && raw.endsWith("]")) {
9483
- tokens = raw.slice(1, -1).split(",").map((t) => t.trim().replace(/^['"]|['"]$/g, ""));
9484
- } else {
9485
- tokens = [raw.replace(/^['"]|['"]$/g, "")];
9486
- }
9487
- const valid = tokens.filter(
9488
- (t) => t === "review" || t === "implement" || t === "research"
9489
- );
9490
- if (valid.length > 0) scope = valid;
9491
- }
9492
- return {
9493
- name: normalizeSkillName(fields.name),
9494
- description: fields.description,
9495
- keywords,
9496
- category: fields.category || void 0,
9497
- mode: fields.mode === "contextual" ? "contextual" : fields.mode === "permanent" ? "permanent" : void 0,
9498
- generated_by: fields.generated_by,
9499
- sources: fields.sources,
9500
- status: fields.status,
9501
- task_type,
9502
- scope
9503
- };
9504
- }
9505
- var init_skill_parser = __esm({
9506
- "packages/orchestrator/src/skill-parser.ts"() {
9507
- "use strict";
9508
- init_skill_name();
9509
- }
9510
- });
9511
-
9512
- // packages/orchestrator/src/memory-config.ts
9513
- function loadMemoryConfig(projectRoot) {
9514
- const configPath = (0, import_path9.resolve)(projectRoot, ".gossip", "memory-config.json");
9515
- if (!(0, import_fs8.existsSync)(configPath)) {
9516
- return { ...DEFAULTS, bundledMemories: { ...DEFAULTS.bundledMemories } };
9517
- }
9518
- let raw;
9519
- try {
9520
- raw = (0, import_fs8.readFileSync)(configPath, "utf-8");
9521
- } catch (err) {
9522
- log("memory-config", `WARNING: failed to read ${configPath}: ${err?.message ?? err} \u2014 using defaults`);
9523
- return { ...DEFAULTS, bundledMemories: { ...DEFAULTS.bundledMemories } };
9524
- }
9525
- let parsed;
9526
- try {
9527
- parsed = JSON.parse(raw);
9528
- } catch (err) {
9529
- log("memory-config", `WARNING: malformed JSON in ${configPath}: ${err?.message ?? err} \u2014 using defaults`);
9530
- return { ...DEFAULTS, bundledMemories: { ...DEFAULTS.bundledMemories } };
9531
- }
9532
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
9533
- log("memory-config", `WARNING: ${configPath} must be a JSON object \u2014 using defaults`);
9534
- return { ...DEFAULTS, bundledMemories: { ...DEFAULTS.bundledMemories } };
9535
- }
9536
- const obj = parsed;
9537
- const bm = obj.bundledMemories;
9538
- if (typeof bm !== "object" || bm === null || Array.isArray(bm)) {
9539
- return { ...DEFAULTS, bundledMemories: { ...DEFAULTS.bundledMemories } };
9540
- }
9541
- const bmObj = bm;
9542
- const enabled = typeof bmObj.enabled === "boolean" ? bmObj.enabled : DEFAULTS.bundledMemories.enabled;
9543
- const exclude = Array.isArray(bmObj.exclude) ? bmObj.exclude.filter((x) => typeof x === "string") : DEFAULTS.bundledMemories.exclude;
9544
- return { bundledMemories: { enabled, exclude } };
9545
- }
9546
- var import_fs8, import_path9, DEFAULTS;
9547
- var init_memory_config = __esm({
9548
- "packages/orchestrator/src/memory-config.ts"() {
9549
- "use strict";
9550
- import_fs8 = require("fs");
9551
- import_path9 = require("path");
9552
- init_log();
9553
- DEFAULTS = {
9554
- bundledMemories: {
9555
- enabled: true,
9556
- exclude: []
9557
- }
9558
- };
9559
- }
9560
- });
9561
-
9562
9467
  // packages/orchestrator/src/consensus-types.ts
9563
9468
  function classifySignal(signalName) {
9564
9469
  if (PERFORMANCE_SIGNAL_NAMES.has(signalName)) return "performance";
@@ -9593,7 +9498,8 @@ var init_consensus_types = __esm({
9593
9498
  "citation_fabricated",
9594
9499
  "finding_dropped_format",
9595
9500
  "consensus_round_retracted",
9596
- "unverified"
9501
+ "unverified",
9502
+ "transport_failure"
9597
9503
  ]);
9598
9504
  }
9599
9505
  });
@@ -9688,7 +9594,28 @@ function bump(projectRoot, consensusId) {
9688
9594
  const next = (inMemoryFallback.get(fbk) ?? 0) + 1;
9689
9595
  inMemoryFallback.set(fbk, next);
9690
9596
  } else {
9691
- inMemoryFallback.delete(fbk);
9597
+ const priorCount = inMemoryFallback.get(fbk) ?? 0;
9598
+ if (priorCount > 0) {
9599
+ const toFlush = Math.min(priorCount, MAX_BACKFILL_PER_BUMP);
9600
+ let remaining = priorCount;
9601
+ for (let i = 0; i < toFlush; i++) {
9602
+ const backfillRecord = {
9603
+ type: "_meta",
9604
+ signal: "round_counter_bumped",
9605
+ consensusId,
9606
+ bumpedAt: (/* @__PURE__ */ new Date()).toISOString(),
9607
+ _emission_path: "round-counter-bump-backfill"
9608
+ };
9609
+ if (!appendMetaRecord(projectRoot, backfillRecord)) break;
9610
+ remaining -= 1;
9611
+ inMemoryFallback.set(fbk, remaining);
9612
+ }
9613
+ if (remaining === 0) {
9614
+ inMemoryFallback.delete(fbk);
9615
+ }
9616
+ } else {
9617
+ inMemoryFallback.delete(fbk);
9618
+ }
9692
9619
  }
9693
9620
  }
9694
9621
  function get(projectRoot, consensusId) {
@@ -9730,7 +9657,7 @@ function makeBumpRecord(consensusId, emissionPath) {
9730
9657
  _emission_path: emissionPath
9731
9658
  };
9732
9659
  }
9733
- var fs, path, JSONL_FILENAME, inMemoryFallback, fallbackKey, scanCache;
9660
+ var fs, path, JSONL_FILENAME, inMemoryFallback, MAX_BACKFILL_PER_BUMP, fallbackKey, scanCache;
9734
9661
  var init_round_counter = __esm({
9735
9662
  "packages/orchestrator/src/round-counter.ts"() {
9736
9663
  "use strict";
@@ -9738,6 +9665,7 @@ var init_round_counter = __esm({
9738
9665
  path = __toESM(require("path"));
9739
9666
  JSONL_FILENAME = "agent-performance.jsonl";
9740
9667
  inMemoryFallback = /* @__PURE__ */ new Map();
9668
+ MAX_BACKFILL_PER_BUMP = 100;
9741
9669
  fallbackKey = (projectRoot, consensusId) => `${projectRoot} ${consensusId}`;
9742
9670
  scanCache = /* @__PURE__ */ new Map();
9743
9671
  }
@@ -9810,8 +9738,8 @@ function escapeControlChars(s) {
9810
9738
  function readTail(filePath, n) {
9811
9739
  const lines = [];
9812
9740
  try {
9813
- if ((0, import_fs9.existsSync)(filePath)) {
9814
- const raw = (0, import_fs9.readFileSync)(filePath, "utf8");
9741
+ if ((0, import_fs8.existsSync)(filePath)) {
9742
+ const raw = (0, import_fs8.readFileSync)(filePath, "utf8");
9815
9743
  if (raw.length > 0) {
9816
9744
  const split = raw.split("\n").filter((l) => l.length > 0);
9817
9745
  lines.push(...split);
@@ -9822,8 +9750,8 @@ function readTail(filePath, n) {
9822
9750
  if (lines.length < n) {
9823
9751
  try {
9824
9752
  const rotated = filePath + ".1";
9825
- if ((0, import_fs9.existsSync)(rotated)) {
9826
- const raw = (0, import_fs9.readFileSync)(rotated, "utf8");
9753
+ if ((0, import_fs8.existsSync)(rotated)) {
9754
+ const raw = (0, import_fs8.readFileSync)(rotated, "utf8");
9827
9755
  const split = raw.split("\n").filter((l) => l.length > 0);
9828
9756
  lines.unshift(...split);
9829
9757
  }
@@ -9832,16 +9760,16 @@ function readTail(filePath, n) {
9832
9760
  }
9833
9761
  return lines.slice(-n);
9834
9762
  }
9835
- var import_fs9, import_path10, import_crypto7, DEFAULTS2, ALLOWLIST_SET, PipelineDriftDetector;
9763
+ var import_fs8, import_path9, import_crypto7, DEFAULTS, ALLOWLIST_SET, PipelineDriftDetector;
9836
9764
  var init_pipeline_drift_detector = __esm({
9837
9765
  "packages/orchestrator/src/pipeline-drift-detector.ts"() {
9838
9766
  "use strict";
9839
- import_fs9 = require("fs");
9840
- import_path10 = require("path");
9767
+ import_fs8 = require("fs");
9768
+ import_path9 = require("path");
9841
9769
  import_crypto7 = require("crypto");
9842
9770
  init_completion_signals_allowlist();
9843
9771
  init_performance_writer();
9844
- DEFAULTS2 = {
9772
+ DEFAULTS = {
9845
9773
  windowSize: 500,
9846
9774
  bypassThreshold: 1,
9847
9775
  unknownThresholdRate: 0.01,
@@ -9856,23 +9784,23 @@ var init_pipeline_drift_detector = __esm({
9856
9784
  dir;
9857
9785
  opts;
9858
9786
  constructor(projectRoot, options = {}) {
9859
- this.dir = (0, import_path10.join)(projectRoot, ".gossip");
9860
- this.perfPath = (0, import_path10.join)(this.dir, "agent-performance.jsonl");
9861
- this.driftPath = (0, import_path10.join)(this.dir, "pipeline-drift.jsonl");
9862
- this.statePath = (0, import_path10.join)(this.dir, "pipeline-drift.state");
9787
+ this.dir = (0, import_path9.join)(projectRoot, ".gossip");
9788
+ this.perfPath = (0, import_path9.join)(this.dir, "agent-performance.jsonl");
9789
+ this.driftPath = (0, import_path9.join)(this.dir, "pipeline-drift.jsonl");
9790
+ this.statePath = (0, import_path9.join)(this.dir, "pipeline-drift.state");
9863
9791
  this.opts = {
9864
- windowSize: options.windowSize ?? DEFAULTS2.windowSize,
9865
- bypassThreshold: options.bypassThreshold ?? DEFAULTS2.bypassThreshold,
9866
- unknownThresholdRate: options.unknownThresholdRate ?? DEFAULTS2.unknownThresholdRate,
9867
- minDenominator: options.minDenominator ?? DEFAULTS2.minDenominator,
9868
- enabled: options.enabled ?? DEFAULTS2.enabled
9792
+ windowSize: options.windowSize ?? DEFAULTS.windowSize,
9793
+ bypassThreshold: options.bypassThreshold ?? DEFAULTS.bypassThreshold,
9794
+ unknownThresholdRate: options.unknownThresholdRate ?? DEFAULTS.unknownThresholdRate,
9795
+ minDenominator: options.minDenominator ?? DEFAULTS.minDenominator,
9796
+ enabled: options.enabled ?? DEFAULTS.enabled
9869
9797
  };
9870
9798
  }
9871
9799
  /** Read the last detection row from `pipeline-drift.jsonl`, or null. */
9872
9800
  readLastReport() {
9873
9801
  try {
9874
- if (!(0, import_fs9.existsSync)(this.driftPath)) return null;
9875
- const raw = (0, import_fs9.readFileSync)(this.driftPath, "utf8");
9802
+ if (!(0, import_fs8.existsSync)(this.driftPath)) return null;
9803
+ const raw = (0, import_fs8.readFileSync)(this.driftPath, "utf8");
9876
9804
  const lines = raw.split("\n").filter((l) => l.length > 0);
9877
9805
  if (lines.length === 0) return null;
9878
9806
  const last = lines[lines.length - 1];
@@ -9962,9 +9890,9 @@ var init_pipeline_drift_detector = __esm({
9962
9890
  const detectedAt = (/* @__PURE__ */ new Date()).toISOString();
9963
9891
  const row = { ...result, detectedAt };
9964
9892
  try {
9965
- if (!(0, import_fs9.existsSync)(this.dir)) (0, import_fs9.mkdirSync)(this.dir, { recursive: true });
9893
+ if (!(0, import_fs8.existsSync)(this.dir)) (0, import_fs8.mkdirSync)(this.dir, { recursive: true });
9966
9894
  rotateJsonlIfNeeded2(this.driftPath);
9967
- (0, import_fs9.appendFileSync)(this.driftPath, JSON.stringify(row) + "\n");
9895
+ (0, import_fs8.appendFileSync)(this.driftPath, JSON.stringify(row) + "\n");
9968
9896
  } catch (err) {
9969
9897
  try {
9970
9898
  process.stderr.write(`[gossipcat] drift jsonl write failed: ${err.message}
@@ -9977,8 +9905,8 @@ var init_pipeline_drift_detector = __esm({
9977
9905
  const safeSignal = first ? escapeControlChars(first.signal) : "";
9978
9906
  const safePath = first ? escapeControlChars(first.emissionPath) : "";
9979
9907
  const safeTask = first ? escapeControlChars(first.taskId) : "";
9980
- const mcpLog = (0, import_path10.join)(this.dir, "mcp.log");
9981
- (0, import_fs9.appendFileSync)(
9908
+ const mcpLog = (0, import_path9.join)(this.dir, "mcp.log");
9909
+ (0, import_fs8.appendFileSync)(
9982
9910
  mcpLog,
9983
9911
  `[drift] bypass=${bypassCount}/${rows.length} unknown=${unknownCount}/${postEpochCount} first_offender={path=${safePath} signal=${safeSignal} task=${safeTask}}
9984
9912
  `
@@ -9990,8 +9918,8 @@ var init_pipeline_drift_detector = __esm({
9990
9918
  }
9991
9919
  readState() {
9992
9920
  try {
9993
- if (!(0, import_fs9.existsSync)(this.statePath)) return {};
9994
- const raw = (0, import_fs9.readFileSync)(this.statePath, "utf8");
9921
+ if (!(0, import_fs8.existsSync)(this.statePath)) return {};
9922
+ const raw = (0, import_fs8.readFileSync)(this.statePath, "utf8");
9995
9923
  const parsed = JSON.parse(raw);
9996
9924
  return parsed && typeof parsed === "object" ? parsed : {};
9997
9925
  } catch {
@@ -10000,8 +9928,8 @@ var init_pipeline_drift_detector = __esm({
10000
9928
  }
10001
9929
  writeState(next) {
10002
9930
  try {
10003
- if (!(0, import_fs9.existsSync)(this.dir)) (0, import_fs9.mkdirSync)(this.dir, { recursive: true });
10004
- (0, import_fs9.writeFileSync)(this.statePath, JSON.stringify(next));
9931
+ if (!(0, import_fs8.existsSync)(this.dir)) (0, import_fs8.mkdirSync)(this.dir, { recursive: true });
9932
+ (0, import_fs8.writeFileSync)(this.statePath, JSON.stringify(next));
10005
9933
  } catch {
10006
9934
  }
10007
9935
  }
@@ -10012,9 +9940,9 @@ var init_pipeline_drift_detector = __esm({
10012
9940
  // packages/orchestrator/src/performance-writer.ts
10013
9941
  function rotateJsonlIfNeeded2(filePath, maxBytes = MAX_TELEMETRY_BYTES) {
10014
9942
  try {
10015
- const st = (0, import_fs10.statSync)(filePath);
9943
+ const st = (0, import_fs9.statSync)(filePath);
10016
9944
  if (st.size < maxBytes) return;
10017
- (0, import_fs10.renameSync)(filePath, filePath + ".1");
9945
+ (0, import_fs9.renameSync)(filePath, filePath + ".1");
10018
9946
  } catch {
10019
9947
  }
10020
9948
  }
@@ -10077,12 +10005,12 @@ function bumpSampleCounter(projectRoot, delta) {
10077
10005
  }
10078
10006
  }
10079
10007
  }
10080
- var import_fs10, import_path11, loggedCounterErrors, MAX_TELEMETRY_BYTES, VALID_CONSENSUS_SIGNALS, VALID_IMPL_SIGNALS, VALID_META_SIGNALS, VALID_PIPELINE_SIGNALS, SYSTEM_SENTINEL_AGENT_ID, rowsWrittenSinceCheck, DRIFT_SAMPLE_INTERVAL, INTERNAL, PerformanceWriter;
10008
+ var import_fs9, import_path10, loggedCounterErrors, MAX_TELEMETRY_BYTES, VALID_CONSENSUS_SIGNALS, VALID_IMPL_SIGNALS, VALID_META_SIGNALS, VALID_PIPELINE_SIGNALS, SYSTEM_SENTINEL_AGENT_ID, rowsWrittenSinceCheck, DRIFT_SAMPLE_INTERVAL, INTERNAL, PerformanceWriter;
10081
10009
  var init_performance_writer = __esm({
10082
10010
  "packages/orchestrator/src/performance-writer.ts"() {
10083
10011
  "use strict";
10084
- import_fs10 = require("fs");
10085
- import_path11 = require("path");
10012
+ import_fs9 = require("fs");
10013
+ import_path10 = require("path");
10086
10014
  init_consensus_types();
10087
10015
  init_round_counter();
10088
10016
  loggedCounterErrors = /* @__PURE__ */ new Set();
@@ -10109,7 +10037,12 @@ var init_performance_writer = __esm({
10109
10037
  "consensus_coverage_degraded",
10110
10038
  // Sandbox policy violation — recorded for observability, zero weight in scoring.
10111
10039
  // Consensus round bb03845d-64264402 (7/7 confirmed).
10112
- "boundary_escape"
10040
+ "boundary_escape",
10041
+ // Transport-layer failure (relay-worker resolutionRoots gap, missing/deleted
10042
+ // worktree, cwd misrouting). Excluded from accuracy/uniqueness arithmetic
10043
+ // per performance-reader.ts:950 + L75. Pre-PR #329: rejected by validateSignal
10044
+ // and silently dropped, masking the fail-closed signal emit added in PR #328.
10045
+ "transport_failure"
10113
10046
  ]);
10114
10047
  VALID_IMPL_SIGNALS = /* @__PURE__ */ new Set([
10115
10048
  "impl_test_pass",
@@ -10149,9 +10082,9 @@ var init_performance_writer = __esm({
10149
10082
  filePath;
10150
10083
  projectRoot;
10151
10084
  constructor(projectRoot) {
10152
- const dir = (0, import_path11.join)(projectRoot, ".gossip");
10153
- if (!(0, import_fs10.existsSync)(dir)) (0, import_fs10.mkdirSync)(dir, { recursive: true });
10154
- this.filePath = (0, import_path11.join)(dir, "agent-performance.jsonl");
10085
+ const dir = (0, import_path10.join)(projectRoot, ".gossip");
10086
+ if (!(0, import_fs9.existsSync)(dir)) (0, import_fs9.mkdirSync)(dir, { recursive: true });
10087
+ this.filePath = (0, import_path10.join)(dir, "agent-performance.jsonl");
10155
10088
  this.projectRoot = projectRoot;
10156
10089
  }
10157
10090
  /**
@@ -10205,7 +10138,7 @@ var init_performance_writer = __esm({
10205
10138
  }
10206
10139
  }
10207
10140
  try {
10208
- (0, import_fs10.appendFileSync)(this.filePath, payload);
10141
+ (0, import_fs9.appendFileSync)(this.filePath, payload);
10209
10142
  } catch (writeErr) {
10210
10143
  const cid = deriveConsensusId(signal);
10211
10144
  if (cid) {
@@ -10253,7 +10186,7 @@ var init_performance_writer = __esm({
10253
10186
  const data = parts.join("\n") + "\n";
10254
10187
  rotateJsonlIfNeeded2(this.filePath);
10255
10188
  try {
10256
- (0, import_fs10.appendFileSync)(this.filePath, data);
10189
+ (0, import_fs9.appendFileSync)(this.filePath, data);
10257
10190
  } catch (writeErr) {
10258
10191
  for (const s of signals) {
10259
10192
  const cid = deriveConsensusId(s);
@@ -10313,7 +10246,7 @@ var init_performance_writer = __esm({
10313
10246
  validateSignal(classStamped);
10314
10247
  const stamped = { ...classStamped, _emission_path: "mcp-server-signals" };
10315
10248
  try {
10316
- (0, import_fs10.appendFileSync)(this.filePath, JSON.stringify(stamped) + "\n");
10249
+ (0, import_fs9.appendFileSync)(this.filePath, JSON.stringify(stamped) + "\n");
10317
10250
  } finally {
10318
10251
  try {
10319
10252
  reset(this.projectRoot, consensusId);
@@ -10405,6 +10338,118 @@ var init_signal_helpers = __esm({
10405
10338
  }
10406
10339
  });
10407
10340
 
10341
+ // packages/orchestrator/src/skill-parser.ts
10342
+ function parseSkillFrontmatter(content) {
10343
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
10344
+ if (!match) return null;
10345
+ const lines = match[1].split("\n");
10346
+ const fields = {};
10347
+ for (const line of lines) {
10348
+ const colonIdx = line.indexOf(":");
10349
+ if (colonIdx === -1) continue;
10350
+ const key = line.slice(0, colonIdx).trim();
10351
+ let value = line.slice(colonIdx + 1).trim();
10352
+ if (value.length >= 2 && (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'"))) {
10353
+ value = value.slice(1, -1);
10354
+ }
10355
+ fields[key] = value;
10356
+ }
10357
+ if (!fields.name || !fields.description || !fields.status) return null;
10358
+ let keywords = [];
10359
+ if (fields.keywords) {
10360
+ const raw = fields.keywords;
10361
+ if (raw.startsWith("[") && raw.endsWith("]")) {
10362
+ keywords = raw.slice(1, -1).split(",").map((k) => k.trim().replace(/^['"]|['"]$/g, "").slice(0, 100)).filter(Boolean);
10363
+ } else {
10364
+ keywords = raw.split(",").map((k) => k.trim().slice(0, 100)).filter(Boolean);
10365
+ }
10366
+ }
10367
+ const rawTaskType = fields.task_type;
10368
+ const task_type = rawTaskType === "review" || rawTaskType === "implement" || rawTaskType === "research" || rawTaskType === "any" ? rawTaskType : "any";
10369
+ let scope;
10370
+ if (fields.scope) {
10371
+ const raw = fields.scope.trim();
10372
+ let tokens;
10373
+ if (raw.startsWith("[") && raw.endsWith("]")) {
10374
+ tokens = raw.slice(1, -1).split(",").map((t) => t.trim().replace(/^['"]|['"]$/g, ""));
10375
+ } else {
10376
+ tokens = [raw.replace(/^['"]|['"]$/g, "")];
10377
+ }
10378
+ const valid = tokens.filter(
10379
+ (t) => t === "review" || t === "implement" || t === "research"
10380
+ );
10381
+ if (valid.length > 0) scope = valid;
10382
+ }
10383
+ return {
10384
+ name: normalizeSkillName(fields.name),
10385
+ description: fields.description,
10386
+ keywords,
10387
+ category: fields.category || void 0,
10388
+ mode: fields.mode === "contextual" ? "contextual" : fields.mode === "permanent" ? "permanent" : void 0,
10389
+ generated_by: fields.generated_by,
10390
+ sources: fields.sources,
10391
+ status: fields.status,
10392
+ task_type,
10393
+ scope
10394
+ };
10395
+ }
10396
+ var init_skill_parser = __esm({
10397
+ "packages/orchestrator/src/skill-parser.ts"() {
10398
+ "use strict";
10399
+ init_skill_name();
10400
+ }
10401
+ });
10402
+
10403
+ // packages/orchestrator/src/memory-config.ts
10404
+ function loadMemoryConfig(projectRoot) {
10405
+ const configPath = (0, import_path11.resolve)(projectRoot, ".gossip", "memory-config.json");
10406
+ if (!(0, import_fs10.existsSync)(configPath)) {
10407
+ return { ...DEFAULTS2, bundledMemories: { ...DEFAULTS2.bundledMemories } };
10408
+ }
10409
+ let raw;
10410
+ try {
10411
+ raw = (0, import_fs10.readFileSync)(configPath, "utf-8");
10412
+ } catch (err) {
10413
+ log("memory-config", `WARNING: failed to read ${configPath}: ${err?.message ?? err} \u2014 using defaults`);
10414
+ return { ...DEFAULTS2, bundledMemories: { ...DEFAULTS2.bundledMemories } };
10415
+ }
10416
+ let parsed;
10417
+ try {
10418
+ parsed = JSON.parse(raw);
10419
+ } catch (err) {
10420
+ log("memory-config", `WARNING: malformed JSON in ${configPath}: ${err?.message ?? err} \u2014 using defaults`);
10421
+ return { ...DEFAULTS2, bundledMemories: { ...DEFAULTS2.bundledMemories } };
10422
+ }
10423
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
10424
+ log("memory-config", `WARNING: ${configPath} must be a JSON object \u2014 using defaults`);
10425
+ return { ...DEFAULTS2, bundledMemories: { ...DEFAULTS2.bundledMemories } };
10426
+ }
10427
+ const obj = parsed;
10428
+ const bm = obj.bundledMemories;
10429
+ if (typeof bm !== "object" || bm === null || Array.isArray(bm)) {
10430
+ return { ...DEFAULTS2, bundledMemories: { ...DEFAULTS2.bundledMemories } };
10431
+ }
10432
+ const bmObj = bm;
10433
+ const enabled = typeof bmObj.enabled === "boolean" ? bmObj.enabled : DEFAULTS2.bundledMemories.enabled;
10434
+ const exclude = Array.isArray(bmObj.exclude) ? bmObj.exclude.filter((x) => typeof x === "string") : DEFAULTS2.bundledMemories.exclude;
10435
+ return { bundledMemories: { enabled, exclude } };
10436
+ }
10437
+ var import_fs10, import_path11, DEFAULTS2;
10438
+ var init_memory_config = __esm({
10439
+ "packages/orchestrator/src/memory-config.ts"() {
10440
+ "use strict";
10441
+ import_fs10 = require("fs");
10442
+ import_path11 = require("path");
10443
+ init_log();
10444
+ DEFAULTS2 = {
10445
+ bundledMemories: {
10446
+ enabled: true,
10447
+ exclude: []
10448
+ }
10449
+ };
10450
+ }
10451
+ });
10452
+
10408
10453
  // packages/orchestrator/src/skill-loader.ts
10409
10454
  function categoryBoost(skillCategory, categories) {
10410
10455
  if (!skillCategory || categories.length === 0) return 0;
@@ -11039,6 +11084,7 @@ ${content}
11039
11084
  }
11040
11085
  const confirmed = entry.confirmedBy;
11041
11086
  if (!Array.isArray(confirmed) || confirmed.length === 0) continue;
11087
+ if (entry.type === "insight") continue;
11042
11088
  const ts2 = entry.timestamp;
11043
11089
  if (ts2) {
11044
11090
  const ms = typeof ts2 === "number" ? ts2 : new Date(ts2).getTime();
@@ -14471,7 +14517,13 @@ var init_performance_reader = __esm({
14471
14517
  boundary_escape: true,
14472
14518
  task_timeout: true,
14473
14519
  task_empty: true,
14474
- consensus_coverage_degraded: true
14520
+ consensus_coverage_degraded: true,
14521
+ // Operational rewrite from hallucination_caught when the relay worker ran
14522
+ // without the dispatched resolutionRoots (Path 2, spec
14523
+ // docs/specs/2026-04-29-relay-worker-resolution-roots.md). Listed here so the
14524
+ // scoring switch's exhaustiveness check holds; it's a no-op for accuracy /
14525
+ // uniqueness / circuit breaker — only `transport_failure_count` is bumped.
14526
+ transport_failure: true
14475
14527
  };
14476
14528
  SEVERITY_MULTIPLIER = {
14477
14529
  critical: 4,
@@ -14609,9 +14661,9 @@ var init_performance_reader = __esm({
14609
14661
  /** Get a reliability multiplier for dispatch weighting (0.3 to 2.0) */
14610
14662
  getDispatchWeight(agentId) {
14611
14663
  const score = this.getAgentScore(agentId);
14612
- if (!score || score.totalSignals < 3) return 1;
14664
+ if (!score || score.scoringSignals < 3) return 1;
14613
14665
  if (score.circuitOpen) return 0.3;
14614
- const confidence = 1 - Math.exp(-score.totalSignals / 10);
14666
+ const confidence = 1 - Math.exp(-score.scoringSignals / 10);
14615
14667
  const consensusAdjusted = 0.5 + (score.reliability - 0.5) * confidence;
14616
14668
  return clamp(0.3 + consensusAdjusted * 1.7, 0.3, 2);
14617
14669
  }
@@ -14696,8 +14748,8 @@ var init_performance_reader = __esm({
14696
14748
  isBenched(agentId, categories, allAgentIds) {
14697
14749
  const score = this.getAgentScore(agentId);
14698
14750
  if (!score) return { benched: false };
14699
- const ruleA = score.accuracy < 0.3 && score.totalSignals >= 200;
14700
- const hallRate = score.totalSignals > 0 ? score.weightedHallucinations / score.totalSignals : 0;
14751
+ const ruleA = score.accuracy < 0.3 && score.scoringSignals >= 200;
14752
+ const hallRate = score.scoringSignals > 0 ? score.weightedHallucinations / score.scoringSignals : 0;
14701
14753
  const ruleB = score.weightedHallucinations >= 5 && hallRate > 0.4;
14702
14754
  if (!ruleA && !ruleB) return { benched: false };
14703
14755
  const reason = ruleA ? "chronic-low-accuracy" : "burst-hallucination";
@@ -14910,10 +14962,12 @@ var init_performance_reader = __esm({
14910
14962
  unverifiedsEmitted: 0,
14911
14963
  unverifiedsReceived: 0,
14912
14964
  totalSignals: 0,
14965
+ scoringSignals: 0,
14913
14966
  lastSignalMs: 0,
14914
14967
  categoryStrengths: {},
14915
14968
  categoryCorrect: {},
14916
- categoryHallucinated: {}
14969
+ categoryHallucinated: {},
14970
+ transportFailures: 0
14917
14971
  });
14918
14972
  return acc.get(id);
14919
14973
  };
@@ -15017,6 +15071,7 @@ var init_performance_reader = __esm({
15017
15071
  case "category_confirmed":
15018
15072
  case "consensus_verified": {
15019
15073
  const diversityMul = signal.signal === "agreement" ? peerDiversity.get(signal.agentId) ?? 1 : 1;
15074
+ a.scoringSignals++;
15020
15075
  a.weightedCorrect += sevMul * decay * diversityMul;
15021
15076
  a.weightedTotal += sevMul * decay * diversityMul;
15022
15077
  a.agreements++;
@@ -15030,8 +15085,9 @@ var init_performance_reader = __esm({
15030
15085
  }
15031
15086
  case "disagreement": {
15032
15087
  if (!signal.category) {
15033
- continue;
15088
+ break;
15034
15089
  }
15090
+ a.scoringSignals++;
15035
15091
  a.weightedTotal += sevMul * decay;
15036
15092
  a.disagreements++;
15037
15093
  if (signal.counterpartId && signal.counterpartId.length > 0) {
@@ -15047,6 +15103,7 @@ var init_performance_reader = __esm({
15047
15103
  break;
15048
15104
  }
15049
15105
  case "unverified": {
15106
+ a.scoringSignals++;
15050
15107
  a.weightedTotal += decay * 0.02;
15051
15108
  a.unverifiedsEmitted++;
15052
15109
  if (signal.counterpartId && signal.counterpartId.length > 0) {
@@ -15056,6 +15113,7 @@ var init_performance_reader = __esm({
15056
15113
  break;
15057
15114
  }
15058
15115
  case "unique_confirmed": {
15116
+ a.scoringSignals++;
15059
15117
  a.weightedCorrect += sevMul * decay;
15060
15118
  a.weightedTotal += sevMul * decay;
15061
15119
  a.weightedUnique += 0.2 * sevMul * decay;
@@ -15068,11 +15126,13 @@ var init_performance_reader = __esm({
15068
15126
  break;
15069
15127
  }
15070
15128
  case "unique_unconfirmed": {
15129
+ a.scoringSignals++;
15071
15130
  a.weightedUnique += 0.05 * decay;
15072
15131
  a.uniqueFindings++;
15073
15132
  break;
15074
15133
  }
15075
15134
  case "new_finding": {
15135
+ a.scoringSignals++;
15076
15136
  a.weightedUnique += 0.15 * decay;
15077
15137
  a.uniqueFindings++;
15078
15138
  break;
@@ -15099,6 +15159,7 @@ var init_performance_reader = __esm({
15099
15159
  }
15100
15160
  }
15101
15161
  }
15162
+ a.scoringSignals++;
15102
15163
  a.weightedHallucinations += severity * hallucDecay * gatedMultiplier;
15103
15164
  a.weightedTotal += decay;
15104
15165
  a.hallucinations++;
@@ -15110,6 +15171,9 @@ var init_performance_reader = __esm({
15110
15171
  case "task_timeout":
15111
15172
  case "task_empty":
15112
15173
  break;
15174
+ case "transport_failure":
15175
+ a.transportFailures++;
15176
+ break;
15113
15177
  case "boundary_escape":
15114
15178
  break;
15115
15179
  }
@@ -15118,6 +15182,7 @@ var init_performance_reader = __esm({
15118
15182
  const signalsByAgent = /* @__PURE__ */ new Map();
15119
15183
  for (const signal of consensusSignals) {
15120
15184
  if (!KNOWN_SIGNALS[signal.signal]) continue;
15185
+ if (signal.signal === "transport_failure") continue;
15121
15186
  const list = signalsByAgent.get(signal.agentId) || [];
15122
15187
  list.push(signal);
15123
15188
  signalsByAgent.set(signal.agentId, list);
@@ -15180,6 +15245,7 @@ var init_performance_reader = __esm({
15180
15245
  reliability,
15181
15246
  impactScore,
15182
15247
  totalSignals: a.totalSignals,
15248
+ scoringSignals: a.scoringSignals,
15183
15249
  agreements: a.agreements,
15184
15250
  disagreements: a.disagreements,
15185
15251
  uniqueFindings: a.uniqueFindings,
@@ -15192,7 +15258,8 @@ var init_performance_reader = __esm({
15192
15258
  categoryStrengths: a.categoryStrengths,
15193
15259
  categoryCorrect: { ...a.categoryCorrect },
15194
15260
  categoryHallucinated: { ...a.categoryHallucinated },
15195
- categoryAccuracy
15261
+ categoryAccuracy,
15262
+ transport_failure_count: a.transportFailures
15196
15263
  });
15197
15264
  }
15198
15265
  for (const [agentId, consec] of consecutiveFailures) {
@@ -15204,6 +15271,7 @@ var init_performance_reader = __esm({
15204
15271
  reliability: 0.5,
15205
15272
  impactScore: 0.5,
15206
15273
  totalSignals: 0,
15274
+ scoringSignals: 0,
15207
15275
  agreements: 0,
15208
15276
  disagreements: 0,
15209
15277
  uniqueFindings: 0,
@@ -15216,7 +15284,8 @@ var init_performance_reader = __esm({
15216
15284
  categoryStrengths: {},
15217
15285
  categoryCorrect: {},
15218
15286
  categoryHallucinated: {},
15219
- categoryAccuracy: {}
15287
+ categoryAccuracy: {},
15288
+ transport_failure_count: 0
15220
15289
  });
15221
15290
  }
15222
15291
  }
@@ -18263,6 +18332,8 @@ var init_dispatch_pipeline = __esm({
18263
18332
  import_fs27 = require("fs");
18264
18333
  import_path29 = require("path");
18265
18334
  init_types2();
18335
+ init_worker_agent();
18336
+ init_signal_helpers();
18266
18337
  init_skill_loader();
18267
18338
  init_prompt_assembler();
18268
18339
  init_agent_memory();
@@ -18493,10 +18564,14 @@ var init_dispatch_pipeline = __esm({
18493
18564
  entry.scope = options?.scope;
18494
18565
  entry.planId = options?.planId;
18495
18566
  entry.planStep = options?.step;
18567
+ if (options?.resolutionRoots && options.resolutionRoots.length > 0) {
18568
+ entry.resolutionRoots = options.resolutionRoots;
18569
+ }
18496
18570
  if (options?.writeMode === "scoped" && options.scope) {
18497
18571
  this.scopeTracker.register(options.scope, taskId);
18498
18572
  }
18499
18573
  const prevSequential = options?.writeMode === "sequential" ? this.sequentialQueues.get(agentId) : void 0;
18574
+ let resolutionRootAssigned = false;
18500
18575
  const runTask = async () => {
18501
18576
  if (prevSequential) await prevSequential.catch(() => {
18502
18577
  });
@@ -18505,6 +18580,37 @@ var init_dispatch_pipeline = __esm({
18505
18580
  entry.worktreeInfo = wtInfo;
18506
18581
  this.toolServer?.assignRoot(agentId, wtInfo.path);
18507
18582
  }
18583
+ const rrCandidate = options?.resolutionRoots?.[0];
18584
+ if (rrCandidate && worker instanceof WorkerAgent) {
18585
+ let dirOk = false;
18586
+ try {
18587
+ dirOk = (0, import_fs27.existsSync)(rrCandidate) && (0, import_fs27.statSync)(rrCandidate).isDirectory();
18588
+ } catch {
18589
+ dirOk = false;
18590
+ }
18591
+ if (!dirOk) {
18592
+ const errMsg = `resolutionRoots[0] does not exist or is not a directory: ${rrCandidate}`;
18593
+ try {
18594
+ emitConsensusSignals(this.projectRoot, [{
18595
+ type: "consensus",
18596
+ signal: "transport_failure",
18597
+ agentId,
18598
+ taskId,
18599
+ evidence: `dispatch failed: ${errMsg}`,
18600
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
18601
+ source: "auto"
18602
+ }]);
18603
+ } catch {
18604
+ }
18605
+ throw new Error(errMsg);
18606
+ }
18607
+ if (this.toolServer) {
18608
+ this.toolServer.assignRoot(agentId, rrCandidate);
18609
+ resolutionRootAssigned = true;
18610
+ entry.resolutionRootAssigned = true;
18611
+ gossipLog(`\u2192 resolutionRoots: assignRoot(${agentId}, ${rrCandidate}) [taskId=${taskId}]`);
18612
+ }
18613
+ }
18508
18614
  const stream = worker.executeTask(task, options?.lens, promptContent, taskId);
18509
18615
  entry.stream = stream;
18510
18616
  for await (const event of stream) {
@@ -18523,6 +18629,10 @@ var init_dispatch_pipeline = __esm({
18523
18629
  entry.completedAt = Date.now();
18524
18630
  entry.memoryQueryCalled = event.payload.memoryQueryCalled ?? false;
18525
18631
  if (entry.writeMode === "scoped") this.scopeTracker.release(entry.id);
18632
+ if (resolutionRootAssigned) {
18633
+ this.toolServer?.releaseAgent(agentId);
18634
+ resolutionRootAssigned = false;
18635
+ }
18526
18636
  try {
18527
18637
  const elapsedMs = (entry.completedAt ?? Date.now()) - entry.startedAt;
18528
18638
  gossipLog(`\u2705 relay \u2190 ${entry.agentId} [${entry.id}] OK (${(elapsedMs / 1e3).toFixed(1)}s, ${(event.payload.result || "").length} chars)`);
@@ -18557,6 +18667,10 @@ var init_dispatch_pipeline = __esm({
18557
18667
  this.worktreeManager.cleanup(entry.id, entry.worktreeInfo.path).catch(() => {
18558
18668
  });
18559
18669
  }
18670
+ if (resolutionRootAssigned) {
18671
+ this.toolServer?.releaseAgent(agentId);
18672
+ resolutionRootAssigned = false;
18673
+ }
18560
18674
  try {
18561
18675
  const elapsedMs = (entry.completedAt ?? Date.now()) - entry.startedAt;
18562
18676
  gossipLog(`\u274C relay \u2190 ${entry.agentId} [${entry.id}] FAILED (${(elapsedMs / 1e3).toFixed(1)}s) \u2014 ${event.payload.error}`);
@@ -18603,7 +18717,8 @@ var init_dispatch_pipeline = __esm({
18603
18717
  agentId: t.agentId,
18604
18718
  task: t.task,
18605
18719
  startedAt: t.startedAt,
18606
- timeoutMs: 3e5
18720
+ timeoutMs: 3e5,
18721
+ ...t.resolutionRoots && t.resolutionRoots.length > 0 ? { resolutionRoots: t.resolutionRoots } : {}
18607
18722
  }));
18608
18723
  }
18609
18724
  /** Get a health summary of all active tasks — for diagnostics when user asks "is it working?" */
@@ -18655,6 +18770,10 @@ var init_dispatch_pipeline = __esm({
18655
18770
  });
18656
18771
  this.toolServer?.releaseAgent(task.agentId);
18657
18772
  }
18773
+ if (task.resolutionRootAssigned) {
18774
+ this.toolServer?.releaseAgent(task.agentId);
18775
+ task.resolutionRootAssigned = false;
18776
+ }
18658
18777
  cancelled++;
18659
18778
  }
18660
18779
  }
@@ -18826,6 +18945,10 @@ Worktree merge: CONFLICT
18826
18945
  if (t.writeMode === "worktree") {
18827
18946
  this.toolServer?.releaseAgent(t.agentId);
18828
18947
  }
18948
+ if (t.resolutionRootAssigned) {
18949
+ this.toolServer?.releaseAgent(t.agentId);
18950
+ t.resolutionRootAssigned = false;
18951
+ }
18829
18952
  }
18830
18953
  }
18831
18954
  const results = [
@@ -20143,8 +20266,8 @@ Keep it SHORT \u2014 under 30 lines. This is a working document, not a design do
20143
20266
  ]);
20144
20267
  const specContent = response.text || "";
20145
20268
  try {
20146
- const { mkdirSync: mkdirSync30, writeFileSync: writeFS } = require("fs");
20147
- mkdirSync30((0, import_path30.join)(this.projectRoot, ".gossip"), { recursive: true });
20269
+ const { mkdirSync: mkdirSync33, writeFileSync: writeFS } = require("fs");
20270
+ mkdirSync33((0, import_path30.join)(this.projectRoot, ".gossip"), { recursive: true });
20148
20271
  writeFS(specPath, specContent, "utf-8");
20149
20272
  } catch (err) {
20150
20273
  return { text: `Spec generated but failed to save: ${err.message}
@@ -21088,8 +21211,8 @@ message: Your question?
21088
21211
  }
21089
21212
  /** Start all worker agents (connect to relay) */
21090
21213
  async start() {
21091
- const { existsSync: existsSync61, readFileSync: readFileSync59 } = await import("fs");
21092
- const { join: join70 } = await import("path");
21214
+ const { existsSync: existsSync64, readFileSync: readFileSync61 } = await import("fs");
21215
+ const { join: join73 } = await import("path");
21093
21216
  for (const config2 of this.registry.getAll()) {
21094
21217
  if (config2.native) continue;
21095
21218
  if (this.workers.has(config2.id)) continue;
@@ -21098,8 +21221,8 @@ message: Your question?
21098
21221
  apiKey = await this.keyProviderFn(config2.provider) ?? void 0;
21099
21222
  }
21100
21223
  const llm = createProvider(config2.provider, config2.model, apiKey);
21101
- const instructionsPath = join70(this.projectRoot, ".gossip", "agents", config2.id, "instructions.md");
21102
- const instructions = existsSync61(instructionsPath) ? readFileSync59(instructionsPath, "utf-8") : void 0;
21224
+ const instructionsPath = join73(this.projectRoot, ".gossip", "agents", config2.id, "instructions.md");
21225
+ const instructions = existsSync64(instructionsPath) ? readFileSync61(instructionsPath, "utf-8") : void 0;
21103
21226
  const enableWebSearch = config2.preset === "researcher" || config2.skills.includes("research");
21104
21227
  const worker = new WorkerAgent(config2.id, llm, this.relayUrl, ALL_TOOLS, instructions, enableWebSearch, this.relayApiKey);
21105
21228
  await worker.start();
@@ -21292,8 +21415,8 @@ message: Your question?
21292
21415
  this.registry.register(config2);
21293
21416
  }
21294
21417
  async syncWorkers(keyProvider) {
21295
- const { existsSync: existsSync61, readFileSync: readFileSync59 } = await import("fs");
21296
- const { join: join70 } = await import("path");
21418
+ const { existsSync: existsSync64, readFileSync: readFileSync61 } = await import("fs");
21419
+ const { join: join73 } = await import("path");
21297
21420
  let added = 0;
21298
21421
  for (const ac of this.registry.getAll()) {
21299
21422
  if (ac.native) continue;
@@ -21310,8 +21433,8 @@ message: Your question?
21310
21433
  this.workers.delete(ac.id);
21311
21434
  }
21312
21435
  const llm = createProvider(ac.provider, ac.model, key ?? void 0, void 0, ac.base_url);
21313
- const instructionsPath = join70(this.projectRoot, ".gossip", "agents", ac.id, "instructions.md");
21314
- const instructions = existsSync61(instructionsPath) ? readFileSync59(instructionsPath, "utf-8") : void 0;
21436
+ const instructionsPath = join73(this.projectRoot, ".gossip", "agents", ac.id, "instructions.md");
21437
+ const instructions = existsSync64(instructionsPath) ? readFileSync61(instructionsPath, "utf-8") : void 0;
21315
21438
  const enableWebSearch = ac.preset === "researcher" || ac.skills.includes("research");
21316
21439
  const worker = new WorkerAgent(ac.id, llm, this.relayUrl, ALL_TOOLS, instructions, enableWebSearch, this.relayApiKey);
21317
21440
  await worker.start();
@@ -24249,6 +24372,86 @@ var init_memory_searcher = __esm({
24249
24372
  }
24250
24373
  });
24251
24374
 
24375
+ // packages/orchestrator/src/transport-failure-detector.ts
24376
+ function shouldRewriteToTransportFailure(signalName, ctx2) {
24377
+ if (signalName !== "hallucination_caught") return false;
24378
+ if (ctx2.isNativeAgent) return false;
24379
+ if (!ctx2.hadResolutionRoots) return false;
24380
+ if (!ctx2.findingText) return false;
24381
+ if (!TRANSPORT_FAILURE_PATTERN.test(ctx2.findingText)) return false;
24382
+ if (CITE_ANCHOR_PATTERN.test(ctx2.findingText)) return false;
24383
+ return true;
24384
+ }
24385
+ function appendTransportRewrite(projectRoot, audit) {
24386
+ try {
24387
+ const filePath = (0, import_path45.join)(projectRoot, ".gossip", "transport-rewrites.jsonl");
24388
+ (0, import_fs42.mkdirSync)((0, import_path45.dirname)(filePath), { recursive: true });
24389
+ (0, import_fs42.appendFileSync)(filePath, JSON.stringify(audit) + "\n", "utf-8");
24390
+ } catch (err) {
24391
+ process.stderr.write(
24392
+ `[gossipcat] appendTransportRewrite failed: ${err.message}
24393
+ `
24394
+ );
24395
+ }
24396
+ }
24397
+ function lookupRoundResolutionRoots(projectRoot, consensusId) {
24398
+ try {
24399
+ const reportPath = (0, import_path45.join)(
24400
+ projectRoot,
24401
+ ".gossip",
24402
+ "consensus-reports",
24403
+ `${consensusId}.json`
24404
+ );
24405
+ if (!(0, import_fs42.existsSync)(reportPath)) return [];
24406
+ const raw = (0, import_fs42.readFileSync)(reportPath, "utf-8");
24407
+ const parsed = JSON.parse(raw);
24408
+ return Array.isArray(parsed.resolutionRoots) ? parsed.resolutionRoots : [];
24409
+ } catch {
24410
+ return [];
24411
+ }
24412
+ }
24413
+ function extractConsensusId(findingId) {
24414
+ if (!findingId) return void 0;
24415
+ const match = findingId.match(/^([0-9a-f]{8}-[0-9a-f]{8})(?::|$)/);
24416
+ return match ? match[1] : void 0;
24417
+ }
24418
+ function maybeRewriteHallucinationToTransportFailure(projectRoot, signal, isNativeAgent) {
24419
+ if (signal.signal !== "hallucination_caught") return signal;
24420
+ const consensusId = extractConsensusId(signal.findingId) ?? signal.consensusId ?? void 0;
24421
+ if (!consensusId) return signal;
24422
+ const roots = lookupRoundResolutionRoots(projectRoot, consensusId);
24423
+ const findingText = `${signal.evidence ?? ""} ${signal.finding ?? ""}`.trim();
24424
+ const ctx2 = {
24425
+ isNativeAgent: isNativeAgent(signal.agentId),
24426
+ hadResolutionRoots: roots.length > 0,
24427
+ findingText
24428
+ };
24429
+ if (!shouldRewriteToTransportFailure(signal.signal, ctx2)) return signal;
24430
+ appendTransportRewrite(projectRoot, {
24431
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
24432
+ consensus_id: consensusId,
24433
+ finding_id: signal.findingId,
24434
+ agent_id: signal.agentId,
24435
+ original_signal: "hallucination_caught",
24436
+ rewritten_to: "transport_failure",
24437
+ finding_excerpt: (signal.evidence ?? "").slice(0, 200)
24438
+ });
24439
+ return {
24440
+ ...signal,
24441
+ signal: "transport_failure"
24442
+ };
24443
+ }
24444
+ var import_fs42, import_path45, TRANSPORT_FAILURE_PATTERN, CITE_ANCHOR_PATTERN;
24445
+ var init_transport_failure_detector = __esm({
24446
+ "packages/orchestrator/src/transport-failure-detector.ts"() {
24447
+ "use strict";
24448
+ import_fs42 = require("fs");
24449
+ import_path45 = require("path");
24450
+ TRANSPORT_FAILURE_PATTERN = /files? (?:are )?(?:not present|missing)|empty diff|empty workspace|cannot be (?:read|located)|not (?:found|present) (?:on|in)(?: the)? (?:disk|filesystem|worktree)/i;
24451
+ CITE_ANCHOR_PATTERN = /<cite\s+tag=["']file["']\s*>[^<]+<\/cite>/i;
24452
+ }
24453
+ });
24454
+
24252
24455
  // packages/orchestrator/src/_sanitize.ts
24253
24456
  function sanitizeForLog(raw, maxChars = 200) {
24254
24457
  let s = raw.replace(CONTROL_CHAR_RE, "\uFFFD");
@@ -24521,28 +24724,28 @@ var init_claim_types = __esm({
24521
24724
  function containWithinProject(projectRoot, input) {
24522
24725
  let resolved;
24523
24726
  try {
24524
- resolved = (0, import_path45.resolve)(projectRoot, input);
24727
+ resolved = (0, import_path46.resolve)(projectRoot, input);
24525
24728
  } catch {
24526
24729
  return null;
24527
24730
  }
24528
24731
  let finalPath = resolved;
24529
24732
  try {
24530
- if ((0, import_fs42.existsSync)(resolved)) {
24531
- finalPath = (0, import_fs42.realpathSync)(resolved);
24733
+ if ((0, import_fs43.existsSync)(resolved)) {
24734
+ finalPath = (0, import_fs43.realpathSync)(resolved);
24532
24735
  }
24533
24736
  } catch {
24534
24737
  return null;
24535
24738
  }
24536
24739
  let rootReal = projectRoot;
24537
24740
  try {
24538
- if ((0, import_fs42.existsSync)(projectRoot)) {
24539
- rootReal = (0, import_fs42.realpathSync)(projectRoot);
24741
+ if ((0, import_fs43.existsSync)(projectRoot)) {
24742
+ rootReal = (0, import_fs43.realpathSync)(projectRoot);
24540
24743
  }
24541
24744
  } catch {
24542
24745
  return null;
24543
24746
  }
24544
24747
  if (finalPath === rootReal) return finalPath;
24545
- if (finalPath.startsWith(rootReal + import_path45.sep)) return finalPath;
24748
+ if (finalPath.startsWith(rootReal + import_path46.sep)) return finalPath;
24546
24749
  return null;
24547
24750
  }
24548
24751
  function getModality(claim) {
@@ -24555,7 +24758,7 @@ function runRg(symbol2, scope, projectRoot, remainingMs) {
24555
24758
  return;
24556
24759
  }
24557
24760
  const scopePath = containWithinProject(projectRoot, scope);
24558
- if (scopePath === null || !(0, import_fs42.existsSync)(scopePath)) {
24761
+ if (scopePath === null || !(0, import_fs43.existsSync)(scopePath)) {
24559
24762
  resolvePromise({ total: 0, error: "missing_path" });
24560
24763
  return;
24561
24764
  }
@@ -24656,12 +24859,12 @@ async function verifyCallsiteCount(claim, idx, projectRoot, remainingMs) {
24656
24859
  async function verifyFileLine(claim, idx, projectRoot) {
24657
24860
  const modality = getModality(claim);
24658
24861
  const filePath = containWithinProject(projectRoot, claim.path);
24659
- if (filePath === null || !(0, import_fs42.existsSync)(filePath)) {
24862
+ if (filePath === null || !(0, import_fs43.existsSync)(filePath)) {
24660
24863
  return { claim_index: idx, status: "unverifiable_by_grep", reason: "file_not_found" };
24661
24864
  }
24662
24865
  let st;
24663
24866
  try {
24664
- st = (0, import_fs42.statSync)(filePath);
24867
+ st = (0, import_fs43.statSync)(filePath);
24665
24868
  } catch {
24666
24869
  return { claim_index: idx, status: "unverifiable_by_grep", reason: "stat_failed" };
24667
24870
  }
@@ -24670,7 +24873,7 @@ async function verifyFileLine(claim, idx, projectRoot) {
24670
24873
  }
24671
24874
  let text;
24672
24875
  try {
24673
- text = (0, import_fs42.readFileSync)(filePath, "utf-8");
24876
+ text = (0, import_fs43.readFileSync)(filePath, "utf-8");
24674
24877
  } catch {
24675
24878
  return { claim_index: idx, status: "unverifiable_by_grep", reason: "read_failed" };
24676
24879
  }
@@ -24821,13 +25024,13 @@ async function verifyClaims(block, projectRoot) {
24821
25024
  }
24822
25025
  return verdicts;
24823
25026
  }
24824
- var import_child_process6, import_fs42, import_path45, MAX_CLAIMS_PER_BLOCK, PER_BLOCK_DEADLINE_MS;
25027
+ var import_child_process6, import_fs43, import_path46, MAX_CLAIMS_PER_BLOCK, PER_BLOCK_DEADLINE_MS;
24825
25028
  var init_claim_verifier = __esm({
24826
25029
  "packages/orchestrator/src/claim-verifier.ts"() {
24827
25030
  "use strict";
24828
25031
  import_child_process6 = require("child_process");
24829
- import_fs42 = require("fs");
24830
- import_path45 = require("path");
25032
+ import_fs43 = require("fs");
25033
+ import_path46 = require("path");
24831
25034
  init_sanitize();
24832
25035
  MAX_CLAIMS_PER_BLOCK = 16;
24833
25036
  PER_BLOCK_DEADLINE_MS = 500;
@@ -25182,42 +25385,81 @@ function runUnderLock(projectRoot, opts) {
25182
25385
  if (inferred) symbolsToCheck.add(inferred);
25183
25386
  }
25184
25387
  if (symbolsToCheck.size === 0) continue;
25185
- let allClear = true;
25388
+ let sawAbsentEverywhere = false;
25389
+ let sawPresentElsewhere = false;
25390
+ let sawPresentAtAnchor = false;
25391
+ let sawCiteWithoutLine = false;
25392
+ let readFailure = false;
25186
25393
  for (const fc of safeFileCites) {
25187
25394
  let body;
25188
25395
  try {
25189
25396
  body = fs4.readFileSync(fc.absPath, "utf8");
25190
25397
  } catch {
25191
- allClear = false;
25398
+ readFailure = true;
25192
25399
  break;
25193
25400
  }
25194
25401
  const stripped = stripJsTsComments(body);
25195
25402
  for (const sym of symbolsToCheck) {
25196
- if (containsToken(stripped, sym)) {
25197
- allClear = false;
25198
- break;
25403
+ const presence = classifyPresence(
25404
+ body,
25405
+ stripped,
25406
+ sym,
25407
+ fc.line,
25408
+ LINE_ANCHORED_WINDOW
25409
+ );
25410
+ if (presence.kind === "absent_everywhere") {
25411
+ sawAbsentEverywhere = true;
25412
+ } else if (presence.kind === "present_at_anchor") {
25413
+ sawPresentAtAnchor = true;
25414
+ } else {
25415
+ sawPresentElsewhere = true;
25416
+ if (fc.line === void 0) sawCiteWithoutLine = true;
25199
25417
  }
25200
25418
  }
25201
- if (!allClear) break;
25202
25419
  }
25203
- if (!allClear) continue;
25420
+ if (readFailure) continue;
25421
+ const allAbsentEverywhere = sawAbsentEverywhere && !sawPresentElsewhere && !sawPresentAtAnchor;
25422
+ const allPresentElsewhere = sawPresentElsewhere && !sawAbsentEverywhere && !sawPresentAtAnchor && !sawCiteWithoutLine;
25423
+ if (!allAbsentEverywhere && !(opts.lineAnchored && allPresentElsewhere)) {
25424
+ continue;
25425
+ }
25204
25426
  const findingId = String(entry.taskId ?? entry.findingId ?? "");
25205
25427
  const beforeQuote = Array.from(symbolsToCheck).slice(0, 3).join(", ");
25206
- const resolvedBy = headSha ? `commit:${headSha}` : "manual";
25428
+ const useStaleAnchor = !allAbsentEverywhere && allPresentElsewhere;
25429
+ const resolvedBy = useStaleAnchor ? "stale_anchor" : headSha ? `commit:${headSha}` : "manual";
25207
25430
  try {
25208
25431
  entry.status = "resolved";
25209
25432
  entry.resolvedAt = (/* @__PURE__ */ new Date()).toISOString();
25210
25433
  entry.resolvedBy = resolvedBy;
25211
25434
  row.raw = JSON.stringify(entry);
25212
- appendChainedEntry(projectRoot, {
25213
- ts: entry.resolvedAt,
25214
- finding_id: findingId,
25215
- action: "resolve",
25216
- resolved_by: resolvedBy,
25217
- before_quote: beforeQuote,
25218
- after_check: "absent",
25219
- operator: "auto"
25220
- });
25435
+ if (useStaleAnchor) {
25436
+ const firstCite = safeFileCites[0];
25437
+ if (firstCite.line === void 0) {
25438
+ throw new Error("finding-resolver invariant: stale_anchor path reached with undefined cited line");
25439
+ }
25440
+ const citedLine = firstCite.line;
25441
+ appendChainedEntry(projectRoot, {
25442
+ ts: entry.resolvedAt,
25443
+ finding_id: findingId,
25444
+ action: "resolve",
25445
+ resolved_by: "stale_anchor",
25446
+ before_quote: beforeQuote,
25447
+ after_check: "present_elsewhere_only",
25448
+ operator: "auto",
25449
+ cited_line: citedLine,
25450
+ window: LINE_ANCHORED_WINDOW
25451
+ });
25452
+ } else {
25453
+ appendChainedEntry(projectRoot, {
25454
+ ts: entry.resolvedAt,
25455
+ finding_id: findingId,
25456
+ action: "resolve",
25457
+ resolved_by: resolvedBy,
25458
+ before_quote: beforeQuote,
25459
+ after_check: "absent",
25460
+ operator: "auto"
25461
+ });
25462
+ }
25221
25463
  resolvedCount++;
25222
25464
  resolvedFindingIds.push(findingId);
25223
25465
  } catch (err) {
@@ -25374,6 +25616,20 @@ function containsToken(haystack, token) {
25374
25616
  const re = new RegExp(`(^|[^A-Za-z0-9_$.])${escaped}(?![A-Za-z0-9_$])`);
25375
25617
  return re.test(haystack);
25376
25618
  }
25619
+ function classifyPresence(body, stripped, symbol2, citedLine, window) {
25620
+ const inFile = containsToken(stripped, symbol2);
25621
+ if (!inFile) return { kind: "absent_everywhere" };
25622
+ if (citedLine === void 0) {
25623
+ return { kind: "present_elsewhere" };
25624
+ }
25625
+ const lines = body.split("\n");
25626
+ if (lines.length === 0) return { kind: "present_elsewhere" };
25627
+ const lo = Math.max(0, citedLine - 1 - window);
25628
+ const hi = Math.min(lines.length - 1, citedLine - 1 + window);
25629
+ const windowText = lines.slice(lo, hi + 1).join("\n");
25630
+ const inWindow = containsToken(windowText, symbol2);
25631
+ return inWindow ? { kind: "present_at_anchor" } : { kind: "present_elsewhere" };
25632
+ }
25377
25633
  function loadConsensusReport(projectRoot, consensusId) {
25378
25634
  if (!/^[A-Za-z0-9_-]+$/.test(consensusId)) return null;
25379
25635
  const reportPath = path5.join(projectRoot, ".gossip", "consensus-reports", `${consensusId}.json`);
@@ -25500,7 +25756,7 @@ function computeTouchedSet(projectRoot, opts, gitRoot) {
25500
25756
  }
25501
25757
  return set2;
25502
25758
  }
25503
- var fs4, os, path5, import_child_process7, FINDINGS_FILENAME, WATERMARK_FILENAME, COLD_START_SINCE, PATH_REJECT_REASONS, FILE_CITE_RE, FN_CITE_RE, PLAIN_FILE_CITE_RE, INFER_SKIP_LIST, SHA_RE, FINDING_RESOLVER_INTERNALS;
25759
+ var fs4, os, path5, import_child_process7, FINDINGS_FILENAME, WATERMARK_FILENAME, COLD_START_SINCE, LINE_ANCHORED_WINDOW, PATH_REJECT_REASONS, FILE_CITE_RE, FN_CITE_RE, PLAIN_FILE_CITE_RE, INFER_SKIP_LIST, SHA_RE, FINDING_RESOLVER_INTERNALS;
25504
25760
  var init_finding_resolver = __esm({
25505
25761
  "packages/orchestrator/src/finding-resolver.ts"() {
25506
25762
  "use strict";
@@ -25513,6 +25769,7 @@ var init_finding_resolver = __esm({
25513
25769
  FINDINGS_FILENAME = "implementation-findings.jsonl";
25514
25770
  WATERMARK_FILENAME = "last-resolve-scan.sha";
25515
25771
  COLD_START_SINCE = "90.days";
25772
+ LINE_ANCHORED_WINDOW = 5;
25516
25773
  PATH_REJECT_REASONS = {
25517
25774
  TRAVERSAL: 'path contains ".." traversal',
25518
25775
  NUL: "path contains NUL byte",
@@ -25595,11 +25852,13 @@ __export(src_exports3, {
25595
25852
  MemoryCompactor: () => MemoryCompactor,
25596
25853
  MemorySearcher: () => MemorySearcher,
25597
25854
  MemoryWriter: () => MemoryWriter,
25855
+ OPERATIONAL_SIGNAL_NAMES: () => OPERATIONAL_SIGNAL_NAMES,
25598
25856
  OllamaProvider: () => OllamaProvider,
25599
25857
  OpenAIProvider: () => OpenAIProvider,
25600
25858
  OverlapDetector: () => OverlapDetector,
25601
25859
  PARSE_FINDINGS_LIMITS: () => PARSE_FINDINGS_LIMITS,
25602
25860
  PENDING_PLAN_CHOICES: () => PENDING_PLAN_CHOICES,
25861
+ PERFORMANCE_SIGNAL_NAMES: () => PERFORMANCE_SIGNAL_NAMES,
25603
25862
  PER_BLOCK_DEADLINE_MS: () => PER_BLOCK_DEADLINE_MS,
25604
25863
  PLAN_CHOICES: () => PLAN_CHOICES,
25605
25864
  PerformanceReader: () => PerformanceReader,
@@ -25619,9 +25878,11 @@ __export(src_exports3, {
25619
25878
  TIMEOUT_DAYS: () => TIMEOUT_DAYS,
25620
25879
  TIMEOUT_MS: () => TIMEOUT_MS,
25621
25880
  TOOL_SCHEMAS: () => TOOL_SCHEMAS2,
25881
+ TRANSPORT_FAILURE_PATTERN: () => TRANSPORT_FAILURE_PATTERN,
25622
25882
  TaskDispatcher: () => TaskDispatcher,
25623
25883
  TaskGraph: () => TaskGraph,
25624
25884
  TaskGraphSync: () => TaskGraphSync,
25885
+ TaskStreamEventType: () => TaskStreamEventType,
25625
25886
  TeamManager: () => TeamManager,
25626
25887
  ToolExecutor: () => ToolExecutor,
25627
25888
  ToolRouter: () => ToolRouter,
@@ -25631,6 +25892,7 @@ __export(src_exports3, {
25631
25892
  ZERO_HASH: () => ZERO_HASH,
25632
25893
  Z_CRITICAL: () => Z_CRITICAL,
25633
25894
  appendChainedEntry: () => appendChainedEntry,
25895
+ appendTransportRewrite: () => appendTransportRewrite,
25634
25896
  applyStatusTags: () => applyStatusTags,
25635
25897
  assemblePrompt: () => assemblePrompt,
25636
25898
  assembleUtilityPrompt: () => assembleUtilityPrompt,
@@ -25638,6 +25900,7 @@ __export(src_exports3, {
25638
25900
  buildToolSystemPrompt: () => buildToolSystemPrompt,
25639
25901
  buildUtilityAgentPrompt: () => buildUtilityAgentPrompt,
25640
25902
  bumpRoundCounter: () => bump,
25903
+ classifyPresence: () => classifyPresence,
25641
25904
  classifySignal: () => classifySignal,
25642
25905
  computeCooldown: () => computeCooldown,
25643
25906
  computeDedupeKey: () => computeDedupeKey,
@@ -25657,6 +25920,7 @@ __export(src_exports3, {
25657
25920
  emitScoringAdjustmentSignals: () => emitScoringAdjustmentSignals,
25658
25921
  ensureRulesFile: () => ensureRulesFile,
25659
25922
  extractCategories: () => extractCategories,
25923
+ extractConsensusId: () => extractConsensusId,
25660
25924
  extractSpecReferences: () => extractSpecReferences,
25661
25925
  findBundledHook: () => findBundledHook,
25662
25926
  findBundledRules: () => findBundledRules,
@@ -25672,6 +25936,8 @@ __export(src_exports3, {
25672
25936
  loadMemoryConfig: () => loadMemoryConfig,
25673
25937
  loadSkills: () => loadSkills,
25674
25938
  logUncategorizedFinding: () => logUncategorizedFinding,
25939
+ lookupRoundResolutionRoots: () => lookupRoundResolutionRoots,
25940
+ maybeRewriteHallucinationToTransportFailure: () => maybeRewriteHallucinationToTransportFailure,
25675
25941
  normalizeSkillName: () => normalizeSkillName,
25676
25942
  oneSidedZTest: () => oneSidedZTest,
25677
25943
  parseAgentFindingsStrict: () => parseAgentFindingsStrict,
@@ -25691,6 +25957,7 @@ __export(src_exports3, {
25691
25957
  sanitizeForLog: () => sanitizeForLog,
25692
25958
  seedMemoryHygiene: () => seedMemoryHygiene,
25693
25959
  selectCrossReviewers: () => selectCrossReviewers,
25960
+ shouldRewriteToTransportFailure: () => shouldRewriteToTransportFailure,
25694
25961
  shouldSkipConsensus: () => shouldSkipConsensus,
25695
25962
  stableStringify: () => stableStringify,
25696
25963
  stripJsTsComments: () => stripJsTsComments,
@@ -25761,6 +26028,7 @@ var init_src4 = __esm({
25761
26028
  init_check_effectiveness();
25762
26029
  init_completion_signals();
25763
26030
  init_signal_helpers();
26031
+ init_transport_failure_detector();
25764
26032
  init_completion_signals_allowlist();
25765
26033
  init_pipeline_drift_detector();
25766
26034
  init_memory_config();
@@ -25771,6 +26039,7 @@ var init_src4 = __esm({
25771
26039
  init_file_lock();
25772
26040
  init_audit_log_chain();
25773
26041
  init_finding_resolver();
26042
+ init_task_stream();
25774
26043
  }
25775
26044
  });
25776
26045
 
@@ -26535,7 +26804,7 @@ var init_utility_agents = __esm({
26535
26804
  // packages/relay/src/dashboard/api-overview.ts
26536
26805
  function readSkillStatus(path6) {
26537
26806
  try {
26538
- const content = (0, import_fs43.readFileSync)(path6, "utf-8");
26807
+ const content = (0, import_fs44.readFileSync)(path6, "utf-8");
26539
26808
  const m = content.match(/^---\n([\s\S]*?)\n---/);
26540
26809
  if (!m) return null;
26541
26810
  const block = m[1];
@@ -26549,24 +26818,24 @@ function readSkillStatus(path6) {
26549
26818
  }
26550
26819
  }
26551
26820
  function computeSkillVerdictSummary(projectRoot) {
26552
- const agentsDir = (0, import_path46.join)(projectRoot, ".gossip", "agents");
26553
- if (!(0, import_fs43.existsSync)(agentsDir)) return void 0;
26821
+ const agentsDir = (0, import_path47.join)(projectRoot, ".gossip", "agents");
26822
+ if (!(0, import_fs44.existsSync)(agentsDir)) return void 0;
26554
26823
  const summary = { pending: 0, passed: 0, failed: 0, silent_skill: 0, insufficient_evidence: 0, inconclusive: 0 };
26555
26824
  let seen = false;
26556
26825
  try {
26557
- const agentIds = (0, import_fs43.readdirSync)(agentsDir);
26826
+ const agentIds = (0, import_fs44.readdirSync)(agentsDir);
26558
26827
  for (const agentId of agentIds) {
26559
- const skillsDir = (0, import_path46.join)(agentsDir, agentId, "skills");
26560
- if (!(0, import_fs43.existsSync)(skillsDir)) continue;
26828
+ const skillsDir = (0, import_path47.join)(agentsDir, agentId, "skills");
26829
+ if (!(0, import_fs44.existsSync)(skillsDir)) continue;
26561
26830
  let entries;
26562
26831
  try {
26563
- entries = (0, import_fs43.readdirSync)(skillsDir);
26832
+ entries = (0, import_fs44.readdirSync)(skillsDir);
26564
26833
  } catch {
26565
26834
  continue;
26566
26835
  }
26567
26836
  for (const entry of entries) {
26568
26837
  if (!entry.endsWith(".md")) continue;
26569
- const status = readSkillStatus((0, import_path46.join)(skillsDir, entry));
26838
+ const status = readSkillStatus((0, import_path47.join)(skillsDir, entry));
26570
26839
  if (!status) continue;
26571
26840
  seen = true;
26572
26841
  if (status === "pending") summary.pending++;
@@ -26583,13 +26852,13 @@ function computeSkillVerdictSummary(projectRoot) {
26583
26852
  return seen ? summary : summary;
26584
26853
  }
26585
26854
  function computeDroppedFindingTypeCounts(projectRoot) {
26586
- const dir = (0, import_path46.join)(projectRoot, ".gossip", "consensus-reports");
26587
- if (!(0, import_fs43.existsSync)(dir)) return void 0;
26855
+ const dir = (0, import_path47.join)(projectRoot, ".gossip", "consensus-reports");
26856
+ if (!(0, import_fs44.existsSync)(dir)) return void 0;
26588
26857
  try {
26589
- const files = (0, import_fs43.readdirSync)(dir).filter((f) => f.endsWith(".json")).map((f) => {
26590
- const full = (0, import_path46.join)(dir, f);
26858
+ const files = (0, import_fs44.readdirSync)(dir).filter((f) => f.endsWith(".json")).map((f) => {
26859
+ const full = (0, import_path47.join)(dir, f);
26591
26860
  try {
26592
- return { full, mtime: (0, import_fs43.statSync)(full).mtimeMs };
26861
+ return { full, mtime: (0, import_fs44.statSync)(full).mtimeMs };
26593
26862
  } catch {
26594
26863
  return null;
26595
26864
  }
@@ -26598,7 +26867,7 @@ function computeDroppedFindingTypeCounts(projectRoot) {
26598
26867
  let seen = false;
26599
26868
  for (const { full } of files) {
26600
26869
  try {
26601
- const report = JSON.parse((0, import_fs43.readFileSync)(full, "utf-8"));
26870
+ const report = JSON.parse((0, import_fs44.readFileSync)(full, "utf-8"));
26602
26871
  const dropped = report?.droppedFindingsByType;
26603
26872
  if (!dropped || typeof dropped !== "object") continue;
26604
26873
  for (const [type, count] of Object.entries(dropped)) {
@@ -26627,12 +26896,12 @@ async function overviewHandler(projectRoot, ctx2) {
26627
26896
  const hourlyActivity = new Array(12).fill(0);
26628
26897
  const now = Date.now();
26629
26898
  const hourMs = 60 * 60 * 1e3;
26630
- const graphPath = (0, import_path46.join)(projectRoot, ".gossip", "task-graph.jsonl");
26631
- if ((0, import_fs43.existsSync)(graphPath)) {
26899
+ const graphPath = (0, import_path47.join)(projectRoot, ".gossip", "task-graph.jsonl");
26900
+ if ((0, import_fs44.existsSync)(graphPath)) {
26632
26901
  try {
26633
26902
  const created = /* @__PURE__ */ new Map();
26634
26903
  const finished = /* @__PURE__ */ new Set();
26635
- const lines = (0, import_fs43.readFileSync)(graphPath, "utf-8").trim().split("\n").filter(Boolean);
26904
+ const lines = (0, import_fs44.readFileSync)(graphPath, "utf-8").trim().split("\n").filter(Boolean);
26636
26905
  for (const line of lines) {
26637
26906
  try {
26638
26907
  const ev = JSON.parse(line);
@@ -26685,10 +26954,10 @@ async function overviewHandler(projectRoot, ctx2) {
26685
26954
  let lastConsensusTimestamp = "";
26686
26955
  let actionableFindings = 0;
26687
26956
  const runBuckets = /* @__PURE__ */ new Map();
26688
- const perfPath = (0, import_path46.join)(projectRoot, ".gossip", "agent-performance.jsonl");
26689
- if ((0, import_fs43.existsSync)(perfPath)) {
26957
+ const perfPath = (0, import_path47.join)(projectRoot, ".gossip", "agent-performance.jsonl");
26958
+ if ((0, import_fs44.existsSync)(perfPath)) {
26690
26959
  try {
26691
- const lines = (0, import_fs43.readFileSync)(perfPath, "utf-8").trim().split("\n").filter(Boolean);
26960
+ const lines = (0, import_fs44.readFileSync)(perfPath, "utf-8").trim().split("\n").filter(Boolean);
26692
26961
  for (const line of lines) {
26693
26962
  try {
26694
26963
  const entry = JSON.parse(line);
@@ -26738,12 +27007,12 @@ async function overviewHandler(projectRoot, ctx2) {
26738
27007
  const droppedFindingTypeCounts = computeDroppedFindingTypeCounts(projectRoot);
26739
27008
  return { agentsOnline, relayCount, relayConnected, nativeCount, consensusRuns, totalFindings, confirmedFindings, totalSignals, tasksCompleted, tasksFailed, avgDurationMs, lastConsensusTimestamp, actionableFindings, hourlyActivity, skillVerdictSummary, droppedFindingTypeCounts };
26740
27009
  }
26741
- var import_fs43, import_path46;
27010
+ var import_fs44, import_path47;
26742
27011
  var init_api_overview = __esm({
26743
27012
  "packages/relay/src/dashboard/api-overview.ts"() {
26744
27013
  "use strict";
26745
- import_fs43 = require("fs");
26746
- import_path46 = require("path");
27014
+ import_fs44 = require("fs");
27015
+ import_path47 = require("path");
26747
27016
  init_utility_agents();
26748
27017
  }
26749
27018
  });
@@ -26769,16 +27038,16 @@ function cacheSet(key, value) {
26769
27038
  async function fleetTrendHandler(projectRoot, query) {
26770
27039
  const rawDays = parseInt(query?.get("days") ?? "", 10);
26771
27040
  const days = isNaN(rawDays) || rawDays < 1 ? DEFAULT_DAYS : Math.min(rawDays, MAX_DAYS);
26772
- const perfPath = (0, import_path47.join)(projectRoot, ".gossip", "agent-performance.jsonl");
26773
- if (!(0, import_fs44.existsSync)(perfPath)) return { days, points: [] };
26774
- const mtime = (0, import_fs44.statSync)(perfPath).mtimeMs;
27041
+ const perfPath = (0, import_path48.join)(projectRoot, ".gossip", "agent-performance.jsonl");
27042
+ if (!(0, import_fs45.existsSync)(perfPath)) return { days, points: [] };
27043
+ const mtime = (0, import_fs45.statSync)(perfPath).mtimeMs;
26775
27044
  const cacheKey = `${projectRoot}::${days}`;
26776
27045
  const cached3 = cacheGet(cacheKey);
26777
27046
  if (cached3 && cached3.mtime === mtime) return cached3.payload;
26778
27047
  const cutoff = Date.now() - days * DAY_MS2;
26779
27048
  const buckets = /* @__PURE__ */ new Map();
26780
27049
  try {
26781
- const lines = (0, import_fs44.readFileSync)(perfPath, "utf-8").trim().split("\n").filter(Boolean);
27050
+ const lines = (0, import_fs45.readFileSync)(perfPath, "utf-8").trim().split("\n").filter(Boolean);
26782
27051
  for (const line of lines) {
26783
27052
  try {
26784
27053
  const rec = JSON.parse(line);
@@ -26815,18 +27084,18 @@ async function fleetTrendHandler(projectRoot, query) {
26815
27084
  const payload = { days, points };
26816
27085
  let postMtime = null;
26817
27086
  try {
26818
- postMtime = (0, import_fs44.statSync)(perfPath).mtimeMs;
27087
+ postMtime = (0, import_fs45.statSync)(perfPath).mtimeMs;
26819
27088
  } catch {
26820
27089
  }
26821
27090
  if (postMtime === mtime) cacheSet(cacheKey, { mtime, payload });
26822
27091
  return payload;
26823
27092
  }
26824
- var import_fs44, import_path47, DEFAULT_DAYS, MAX_DAYS, DAY_MS2, CACHE_MAX, cache;
27093
+ var import_fs45, import_path48, DEFAULT_DAYS, MAX_DAYS, DAY_MS2, CACHE_MAX, cache;
26825
27094
  var init_api_fleet_trend = __esm({
26826
27095
  "packages/relay/src/dashboard/api-fleet-trend.ts"() {
26827
27096
  "use strict";
26828
- import_fs44 = require("fs");
26829
- import_path47 = require("path");
27097
+ import_fs45 = require("fs");
27098
+ import_path48 = require("path");
26830
27099
  DEFAULT_DAYS = 30;
26831
27100
  MAX_DAYS = 90;
26832
27101
  DAY_MS2 = 24 * 60 * 60 * 1e3;
@@ -26838,9 +27107,9 @@ var init_api_fleet_trend = __esm({
26838
27107
  // packages/relay/src/dashboard/api-agents.ts
26839
27108
  function readSkillFrontmatter(projectRoot, agentId, skillName) {
26840
27109
  try {
26841
- const path6 = (0, import_path48.join)(projectRoot, ".gossip", "agents", agentId, "skills", `${skillName}.md`);
26842
- if (!(0, import_fs45.existsSync)(path6)) return null;
26843
- const raw = (0, import_fs45.readFileSync)(path6, "utf-8");
27110
+ const path6 = (0, import_path49.join)(projectRoot, ".gossip", "agents", agentId, "skills", `${skillName}.md`);
27111
+ if (!(0, import_fs46.existsSync)(path6)) return null;
27112
+ const raw = (0, import_fs46.readFileSync)(path6, "utf-8");
26844
27113
  if (!raw.startsWith("---")) return null;
26845
27114
  const end = raw.indexOf("\n---", 3);
26846
27115
  if (end === -1) return null;
@@ -26876,9 +27145,9 @@ function normalizeCategory(s) {
26876
27145
  }
26877
27146
  function readForcedDevelops(projectRoot, agentId, category) {
26878
27147
  try {
26879
- const path6 = (0, import_path48.join)(projectRoot, ".gossip", "forced-skill-develops.jsonl");
26880
- if (!(0, import_fs45.existsSync)(path6)) return [];
26881
- const raw = (0, import_fs45.readFileSync)(path6, "utf-8");
27148
+ const path6 = (0, import_path49.join)(projectRoot, ".gossip", "forced-skill-develops.jsonl");
27149
+ if (!(0, import_fs46.existsSync)(path6)) return [];
27150
+ const raw = (0, import_fs46.readFileSync)(path6, "utf-8");
26882
27151
  const target = normalizeCategory(category);
26883
27152
  const out = [];
26884
27153
  for (const line of raw.split("\n")) {
@@ -26901,17 +27170,18 @@ function readForcedDevelops(projectRoot, agentId, category) {
26901
27170
  }
26902
27171
  }
26903
27172
  function readTaskGraphByAgent(projectRoot) {
26904
- const taskGraphPath = (0, import_path48.join)(projectRoot, ".gossip", "task-graph.jsonl");
27173
+ const taskGraphPath = (0, import_path49.join)(projectRoot, ".gossip", "task-graph.jsonl");
26905
27174
  const result = /* @__PURE__ */ new Map();
26906
- if (!(0, import_fs45.existsSync)(taskGraphPath)) return result;
27175
+ if (!(0, import_fs46.existsSync)(taskGraphPath)) return result;
26907
27176
  let lines;
26908
27177
  try {
26909
- lines = (0, import_fs45.readFileSync)(taskGraphPath, "utf-8").trim().split("\n").filter(Boolean);
27178
+ lines = (0, import_fs46.readFileSync)(taskGraphPath, "utf-8").trim().split("\n").filter(Boolean);
26910
27179
  } catch {
26911
27180
  return result;
26912
27181
  }
26913
27182
  const created = /* @__PURE__ */ new Map();
26914
27183
  const completed = /* @__PURE__ */ new Map();
27184
+ const failed = /* @__PURE__ */ new Set();
26915
27185
  for (const line of lines) {
26916
27186
  let entry;
26917
27187
  try {
@@ -26927,18 +27197,23 @@ function readTaskGraphByAgent(projectRoot) {
26927
27197
  outputTokens: entry.outputTokens ?? 0,
26928
27198
  timestamp: entry.timestamp ?? ""
26929
27199
  });
27200
+ } else if (entry.type === "task.failed" && entry.taskId) {
27201
+ failed.add(entry.taskId);
26930
27202
  }
26931
27203
  }
26932
27204
  for (const [taskId, createdData] of created) {
26933
27205
  const { agentId, task, timestamp } = createdData;
26934
27206
  if (isUtilityAgent(agentId)) continue;
26935
27207
  if (!result.has(agentId)) {
26936
- result.set(agentId, { totalTokens: 0, lastTask: null });
27208
+ result.set(agentId, { totalTokens: 0, lastTask: null, tasksCompleted: 0, tasksFailed: 0 });
26937
27209
  }
26938
27210
  const agentData = result.get(agentId);
26939
27211
  const comp = completed.get(taskId);
26940
27212
  if (comp) {
26941
27213
  agentData.totalTokens += comp.inputTokens + comp.outputTokens;
27214
+ agentData.tasksCompleted++;
27215
+ } else if (failed.has(taskId)) {
27216
+ agentData.tasksFailed++;
26942
27217
  }
26943
27218
  if (!agentData.lastTask || timestamp > agentData.lastTask.timestamp) {
26944
27219
  agentData.lastTask = { task, timestamp };
@@ -26963,7 +27238,7 @@ async function agentsHandler(projectRoot, configs, onlineAgents = []) {
26963
27238
  const allIds = configs.map((c) => c.id);
26964
27239
  return configs.map((config2) => {
26965
27240
  const score = scores.get(config2.id) ?? { ...DEFAULT_SCORE, agentId: config2.id };
26966
- const agentTask = taskDataByAgent.get(config2.id) ?? { totalTokens: 0, lastTask: null };
27241
+ const agentTask = taskDataByAgent.get(config2.id) ?? { totalTokens: 0, lastTask: null, tasksCompleted: 0, tasksFailed: 0 };
26967
27242
  const categories = Object.keys({ ...score.categoryCorrect, ...score.categoryHallucinated });
26968
27243
  const benchResult = reader.isBenched(config2.id, categories, allIds);
26969
27244
  const benchState = benchResult.benched ? "benched" : benchResult.safeguardBlocked ? "kept-for-coverage" : "none";
@@ -26996,6 +27271,9 @@ async function agentsHandler(projectRoot, configs, onlineAgents = []) {
26996
27271
  }
26997
27272
  } catch {
26998
27273
  }
27274
+ const { tasksCompleted, tasksFailed } = agentTask;
27275
+ const completionDenominator = tasksCompleted + tasksFailed;
27276
+ const taskCompletionRate = completionDenominator > 0 ? tasksCompleted / completionDenominator : null;
26999
27277
  return {
27000
27278
  id: config2.id,
27001
27279
  provider: config2.provider,
@@ -27011,6 +27289,7 @@ async function agentsHandler(projectRoot, configs, onlineAgents = []) {
27011
27289
  accuracy: score.accuracy,
27012
27290
  uniqueness: score.uniqueness,
27013
27291
  reliability: score.reliability,
27292
+ taskCompletionRate,
27014
27293
  impactScore: score.impactScore ?? 0.5,
27015
27294
  dispatchWeight: reader.getDispatchWeight(config2.id),
27016
27295
  signals: score.totalSignals,
@@ -27020,6 +27299,7 @@ async function agentsHandler(projectRoot, configs, onlineAgents = []) {
27020
27299
  uniqueFindings: score.uniqueFindings ?? 0,
27021
27300
  unverifiedsEmitted: score.unverifiedsEmitted ?? 0,
27022
27301
  unverifiedsReceived: score.unverifiedsReceived ?? 0,
27302
+ transportFailureCount: score.transport_failure_count ?? 0,
27023
27303
  consecutiveFailures: score.consecutiveFailures ?? 0,
27024
27304
  circuitOpen: score.circuitOpen ?? false,
27025
27305
  bench,
@@ -27041,14 +27321,14 @@ async function agentsHandler(projectRoot, configs, onlineAgents = []) {
27041
27321
  };
27042
27322
  });
27043
27323
  }
27044
- var import_fs45, import_path48, DEFAULT_SCORE;
27324
+ var import_fs46, import_path49, DEFAULT_SCORE;
27045
27325
  var init_api_agents = __esm({
27046
27326
  "packages/relay/src/dashboard/api-agents.ts"() {
27047
27327
  "use strict";
27048
27328
  init_performance_reader();
27049
27329
  init_skill_index();
27050
- import_fs45 = require("fs");
27051
- import_path48 = require("path");
27330
+ import_fs46 = require("fs");
27331
+ import_path49 = require("path");
27052
27332
  init_utility_agents();
27053
27333
  DEFAULT_SCORE = {
27054
27334
  agentId: "",
@@ -27057,6 +27337,7 @@ var init_api_agents = __esm({
27057
27337
  reliability: 0.5,
27058
27338
  impactScore: 0.5,
27059
27339
  totalSignals: 0,
27340
+ scoringSignals: 0,
27060
27341
  agreements: 0,
27061
27342
  disagreements: 0,
27062
27343
  uniqueFindings: 0,
@@ -27069,14 +27350,15 @@ var init_api_agents = __esm({
27069
27350
  categoryStrengths: {},
27070
27351
  categoryCorrect: {},
27071
27352
  categoryHallucinated: {},
27072
- categoryAccuracy: {}
27353
+ categoryAccuracy: {},
27354
+ transport_failure_count: 0
27073
27355
  };
27074
27356
  }
27075
27357
  });
27076
27358
 
27077
27359
  // packages/relay/src/dashboard/api-skills.ts
27078
27360
  function isCorrupt(projectRoot, index) {
27079
- return (0, import_fs46.existsSync)((0, import_path49.join)(projectRoot, ".gossip", "skill-index.json")) && !index.exists();
27361
+ return (0, import_fs47.existsSync)((0, import_path50.join)(projectRoot, ".gossip", "skill-index.json")) && !index.exists();
27080
27362
  }
27081
27363
  async function skillsGetHandler(projectRoot) {
27082
27364
  try {
@@ -27105,13 +27387,13 @@ async function skillsBindHandler(projectRoot, body) {
27105
27387
  return { success: false, error: err instanceof Error ? err.message : "Unknown error" };
27106
27388
  }
27107
27389
  }
27108
- var import_fs46, import_path49, AGENT_ID_RE2;
27390
+ var import_fs47, import_path50, AGENT_ID_RE2;
27109
27391
  var init_api_skills = __esm({
27110
27392
  "packages/relay/src/dashboard/api-skills.ts"() {
27111
27393
  "use strict";
27112
27394
  init_skill_index();
27113
- import_fs46 = require("fs");
27114
- import_path49 = require("path");
27395
+ import_fs47 = require("fs");
27396
+ import_path50 = require("path");
27115
27397
  AGENT_ID_RE2 = /^[a-zA-Z0-9_-]{1,64}$/;
27116
27398
  }
27117
27399
  });
@@ -27119,25 +27401,25 @@ var init_api_skills = __esm({
27119
27401
  // packages/relay/src/dashboard/api-memory.ts
27120
27402
  async function memoryHandler(projectRoot, agentId) {
27121
27403
  if (!agentId || !AGENT_ID_RE3.test(agentId) || DANGEROUS_IDS.has(agentId)) throw new Error("Invalid agent ID");
27122
- const memDir = (0, import_path50.join)(projectRoot, ".gossip", "agents", agentId, "memory");
27404
+ const memDir = (0, import_path51.join)(projectRoot, ".gossip", "agents", agentId, "memory");
27123
27405
  let index = "";
27124
- const indexPath = (0, import_path50.join)(memDir, "MEMORY.md");
27125
- if ((0, import_fs47.existsSync)(indexPath)) {
27406
+ const indexPath = (0, import_path51.join)(memDir, "MEMORY.md");
27407
+ if ((0, import_fs48.existsSync)(indexPath)) {
27126
27408
  try {
27127
- index = (0, import_fs47.readFileSync)(indexPath, "utf-8");
27409
+ index = (0, import_fs48.readFileSync)(indexPath, "utf-8");
27128
27410
  } catch {
27129
27411
  }
27130
27412
  }
27131
27413
  const knowledge = [];
27132
- const knowledgeDir = (0, import_path50.join)(memDir, "knowledge");
27414
+ const knowledgeDir = (0, import_path51.join)(memDir, "knowledge");
27133
27415
  const knowledgeDirs = [knowledgeDir, memDir];
27134
27416
  for (const dir of knowledgeDirs) {
27135
- if (!(0, import_fs47.existsSync)(dir)) continue;
27417
+ if (!(0, import_fs48.existsSync)(dir)) continue;
27136
27418
  try {
27137
- const files = (0, import_fs47.readdirSync)(dir).filter((f) => f.endsWith(".md") && f !== "MEMORY.md");
27419
+ const files = (0, import_fs48.readdirSync)(dir).filter((f) => f.endsWith(".md") && f !== "MEMORY.md");
27138
27420
  for (const filename of files) {
27139
27421
  try {
27140
- const raw = (0, import_fs47.readFileSync)((0, import_path50.join)(dir, filename), "utf-8");
27422
+ const raw = (0, import_fs48.readFileSync)((0, import_path51.join)(dir, filename), "utf-8");
27141
27423
  const { frontmatter, content } = parseFrontmatter(raw);
27142
27424
  knowledge.push({ filename, frontmatter, content });
27143
27425
  } catch {
@@ -27147,10 +27429,10 @@ async function memoryHandler(projectRoot, agentId) {
27147
27429
  }
27148
27430
  }
27149
27431
  const tasks = [];
27150
- const tasksPath = (0, import_path50.join)(memDir, "tasks.jsonl");
27151
- if ((0, import_fs47.existsSync)(tasksPath)) {
27432
+ const tasksPath = (0, import_path51.join)(memDir, "tasks.jsonl");
27433
+ if ((0, import_fs48.existsSync)(tasksPath)) {
27152
27434
  try {
27153
- const lines = (0, import_fs47.readFileSync)(tasksPath, "utf-8").trim().split("\n").filter(Boolean).slice(-200);
27435
+ const lines = (0, import_fs48.readFileSync)(tasksPath, "utf-8").trim().split("\n").filter(Boolean).slice(-200);
27154
27436
  for (const line of lines) {
27155
27437
  try {
27156
27438
  tasks.push(JSON.parse(line));
@@ -27178,12 +27460,12 @@ function parseFrontmatter(raw) {
27178
27460
  }
27179
27461
  return { frontmatter: fm, content: raw.slice(end + 3).trim() };
27180
27462
  }
27181
- var import_fs47, import_path50, AGENT_ID_RE3, DANGEROUS_IDS;
27463
+ var import_fs48, import_path51, AGENT_ID_RE3, DANGEROUS_IDS;
27182
27464
  var init_api_memory = __esm({
27183
27465
  "packages/relay/src/dashboard/api-memory.ts"() {
27184
27466
  "use strict";
27185
- import_fs47 = require("fs");
27186
- import_path50 = require("path");
27467
+ import_fs48 = require("fs");
27468
+ import_path51 = require("path");
27187
27469
  AGENT_ID_RE3 = /^[a-zA-Z0-9_-]{1,64}$/;
27188
27470
  DANGEROUS_IDS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
27189
27471
  }
@@ -27192,25 +27474,25 @@ var init_api_memory = __esm({
27192
27474
  // packages/relay/src/dashboard/api-native-memory.ts
27193
27475
  function autoMemoryDir(projectRoot, home) {
27194
27476
  const h = home ?? (0, import_os2.homedir)();
27195
- return (0, import_path51.join)(h, ".claude", "projects", projectRoot.replaceAll("/", "-"), "memory");
27477
+ return (0, import_path52.join)(h, ".claude", "projects", projectRoot.replaceAll("/", "-"), "memory");
27196
27478
  }
27197
27479
  async function autoMemoryHandler(projectRoot, home) {
27198
27480
  const dir = autoMemoryDir(projectRoot, home);
27199
- if (!(0, import_fs48.existsSync)(dir)) return { knowledge: [] };
27481
+ if (!(0, import_fs49.existsSync)(dir)) return { knowledge: [] };
27200
27482
  let entries;
27201
27483
  try {
27202
- entries = (0, import_fs48.readdirSync)(dir);
27484
+ entries = (0, import_fs49.readdirSync)(dir);
27203
27485
  } catch {
27204
27486
  return { knowledge: [] };
27205
27487
  }
27206
27488
  const knowledge = [];
27207
27489
  for (const filename of entries) {
27208
27490
  if (!FILENAME_RE.test(filename)) continue;
27209
- const full = (0, import_path51.join)(dir, filename);
27491
+ const full = (0, import_path52.join)(dir, filename);
27210
27492
  try {
27211
- const st = (0, import_fs48.statSync)(full);
27493
+ const st = (0, import_fs49.statSync)(full);
27212
27494
  if (!st.isFile()) continue;
27213
- const raw = (0, import_fs48.readFileSync)(full, "utf-8");
27495
+ const raw = (0, import_fs49.readFileSync)(full, "utf-8");
27214
27496
  const { frontmatter, content } = parseFrontmatter2(raw);
27215
27497
  knowledge.push({ filename, frontmatter, content, agentId: "_auto" });
27216
27498
  } catch {
@@ -27232,12 +27514,12 @@ function parseFrontmatter2(raw) {
27232
27514
  if (rest.startsWith("\n")) rest = rest.slice(1);
27233
27515
  return { frontmatter: fm, content: rest.trim() };
27234
27516
  }
27235
- var import_fs48, import_path51, import_os2, FILENAME_RE;
27517
+ var import_fs49, import_path52, import_os2, FILENAME_RE;
27236
27518
  var init_api_native_memory = __esm({
27237
27519
  "packages/relay/src/dashboard/api-native-memory.ts"() {
27238
27520
  "use strict";
27239
- import_fs48 = require("fs");
27240
- import_path51 = require("path");
27521
+ import_fs49 = require("fs");
27522
+ import_path52 = require("path");
27241
27523
  import_os2 = require("os");
27242
27524
  FILENAME_RE = /^[A-Za-z0-9_.-]+\.md$/;
27243
27525
  }
@@ -27245,25 +27527,25 @@ var init_api_native_memory = __esm({
27245
27527
 
27246
27528
  // packages/relay/src/dashboard/api-gossip-memory.ts
27247
27529
  function gossipMemoryDir(projectRoot) {
27248
- return (0, import_path52.join)(projectRoot, ".gossip", "memory");
27530
+ return (0, import_path53.join)(projectRoot, ".gossip", "memory");
27249
27531
  }
27250
27532
  async function gossipMemoryHandler(projectRoot) {
27251
27533
  const dir = gossipMemoryDir(projectRoot);
27252
- if (!(0, import_fs49.existsSync)(dir)) return { knowledge: [] };
27534
+ if (!(0, import_fs50.existsSync)(dir)) return { knowledge: [] };
27253
27535
  let entries;
27254
27536
  try {
27255
- entries = (0, import_fs49.readdirSync)(dir);
27537
+ entries = (0, import_fs50.readdirSync)(dir);
27256
27538
  } catch {
27257
27539
  return { knowledge: [] };
27258
27540
  }
27259
27541
  const knowledge = [];
27260
27542
  for (const filename of entries) {
27261
27543
  if (!FILENAME_RE2.test(filename)) continue;
27262
- const full = (0, import_path52.join)(dir, filename);
27544
+ const full = (0, import_path53.join)(dir, filename);
27263
27545
  try {
27264
- const st = (0, import_fs49.statSync)(full);
27546
+ const st = (0, import_fs50.statSync)(full);
27265
27547
  if (!st.isFile()) continue;
27266
- const raw = (0, import_fs49.readFileSync)(full, "utf-8");
27548
+ const raw = (0, import_fs50.readFileSync)(full, "utf-8");
27267
27549
  const { frontmatter, content } = parseFrontmatter2(raw);
27268
27550
  knowledge.push({ filename, frontmatter, content, agentId: "_gossip" });
27269
27551
  } catch {
@@ -27271,12 +27553,12 @@ async function gossipMemoryHandler(projectRoot) {
27271
27553
  }
27272
27554
  return { knowledge };
27273
27555
  }
27274
- var import_fs49, import_path52, FILENAME_RE2;
27556
+ var import_fs50, import_path53, FILENAME_RE2;
27275
27557
  var init_api_gossip_memory = __esm({
27276
27558
  "packages/relay/src/dashboard/api-gossip-memory.ts"() {
27277
27559
  "use strict";
27278
- import_fs49 = require("fs");
27279
- import_path52 = require("path");
27560
+ import_fs50 = require("fs");
27561
+ import_path53 = require("path");
27280
27562
  init_api_native_memory();
27281
27563
  FILENAME_RE2 = /^[A-Za-z0-9_.-]+\.md$/;
27282
27564
  }
@@ -27288,7 +27570,7 @@ async function consensusHandler(projectRoot, query) {
27288
27570
  const rawPageSize = parseInt(query?.get("pageSize") ?? "", 10);
27289
27571
  const page = isNaN(rawPage) || rawPage < 1 ? 1 : rawPage;
27290
27572
  const pageSize = isNaN(rawPageSize) || rawPageSize < 1 ? DEFAULT_PAGE_SIZE : Math.min(rawPageSize, MAX_PAGE_SIZE);
27291
- const perfPath = (0, import_path53.join)(projectRoot, ".gossip", "agent-performance.jsonl");
27573
+ const perfPath = (0, import_path54.join)(projectRoot, ".gossip", "agent-performance.jsonl");
27292
27574
  let retractedIds = /* @__PURE__ */ new Set();
27293
27575
  let roundRetractions = [];
27294
27576
  try {
@@ -27303,10 +27585,10 @@ async function consensusHandler(projectRoot, query) {
27303
27585
  retractionByConsensusId.set(r.consensus_id, { reason: r.reason, retracted_at: r.retracted_at });
27304
27586
  }
27305
27587
  const retractedConsensusIds = Array.from(retractedIds);
27306
- if (!(0, import_fs50.existsSync)(perfPath)) return { runs: [], totalRuns: 0, totalSignals: 0, page, pageSize, retractedConsensusIds, roundRetractions };
27588
+ if (!(0, import_fs51.existsSync)(perfPath)) return { runs: [], totalRuns: 0, totalSignals: 0, page, pageSize, retractedConsensusIds, roundRetractions };
27307
27589
  const signals = [];
27308
27590
  try {
27309
- const lines = (0, import_fs50.readFileSync)(perfPath, "utf-8").trim().split("\n").filter(Boolean);
27591
+ const lines = (0, import_fs51.readFileSync)(perfPath, "utf-8").trim().split("\n").filter(Boolean);
27310
27592
  for (const line of lines) {
27311
27593
  try {
27312
27594
  const parsed = JSON.parse(line);
@@ -27383,12 +27665,12 @@ async function consensusHandler(projectRoot, query) {
27383
27665
  const paginatedRuns = runs.slice(offset, offset + pageSize);
27384
27666
  return { runs: paginatedRuns, totalRuns, totalSignals: signals.length, page, pageSize, retractedConsensusIds, roundRetractions };
27385
27667
  }
27386
- var import_fs50, import_path53, RESOLUTION_SIGNALS, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE;
27668
+ var import_fs51, import_path54, RESOLUTION_SIGNALS, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE;
27387
27669
  var init_api_consensus = __esm({
27388
27670
  "packages/relay/src/dashboard/api-consensus.ts"() {
27389
27671
  "use strict";
27390
- import_fs50 = require("fs");
27391
- import_path53 = require("path");
27672
+ import_fs51 = require("fs");
27673
+ import_path54 = require("path");
27392
27674
  RESOLUTION_SIGNALS = /* @__PURE__ */ new Set(["agreement", "unique_confirmed", "consensus_verified"]);
27393
27675
  DEFAULT_PAGE_SIZE = 10;
27394
27676
  MAX_PAGE_SIZE = 500;
@@ -27420,13 +27702,13 @@ async function signalsHandler(projectRoot, query) {
27420
27702
  const cursor = query?.get("cursor") ?? null;
27421
27703
  const limit = Math.min(Math.max(parseInt(query?.get("limit") ?? "", 10) || DEFAULT_LIMIT, 1), MAX_LIMIT);
27422
27704
  const offset = Math.max(parseInt(query?.get("offset") ?? "", 10) || 0, 0);
27423
- const perfPath = (0, import_path54.join)(projectRoot, ".gossip", "agent-performance.jsonl");
27424
- if (!(0, import_fs51.existsSync)(perfPath)) return { items: [], total: 0, offset, limit };
27705
+ const perfPath = (0, import_path55.join)(projectRoot, ".gossip", "agent-performance.jsonl");
27706
+ if (!(0, import_fs52.existsSync)(perfPath)) return { items: [], total: 0, offset, limit };
27425
27707
  const all = [];
27426
27708
  const roundRetractions = [];
27427
27709
  const signalFilterSet = signalFilters.length ? new Set(signalFilters) : null;
27428
27710
  try {
27429
- const lines = (0, import_fs51.readFileSync)(perfPath, "utf-8").trim().split("\n").filter(Boolean);
27711
+ const lines = (0, import_fs52.readFileSync)(perfPath, "utf-8").trim().split("\n").filter(Boolean);
27430
27712
  for (const line of lines) {
27431
27713
  try {
27432
27714
  const entry = JSON.parse(line);
@@ -27481,12 +27763,12 @@ async function signalsHandler(projectRoot, query) {
27481
27763
  if (nextCursor) response.nextCursor = nextCursor;
27482
27764
  return response;
27483
27765
  }
27484
- var import_fs51, import_path54, MAX_LIMIT, DEFAULT_LIMIT, META_SIGNALS;
27766
+ var import_fs52, import_path55, MAX_LIMIT, DEFAULT_LIMIT, META_SIGNALS;
27485
27767
  var init_api_signals = __esm({
27486
27768
  "packages/relay/src/dashboard/api-signals.ts"() {
27487
27769
  "use strict";
27488
- import_fs51 = require("fs");
27489
- import_path54 = require("path");
27770
+ import_fs52 = require("fs");
27771
+ import_path55 = require("path");
27490
27772
  MAX_LIMIT = 500;
27491
27773
  DEFAULT_LIMIT = 50;
27492
27774
  META_SIGNALS = /* @__PURE__ */ new Set(["task_completed", "tool_turns", "format_compliance"]);
@@ -27498,7 +27780,7 @@ function extractCitations(findingText, projectRoot) {
27498
27780
  const out = [];
27499
27781
  let realRoot;
27500
27782
  try {
27501
- realRoot = (0, import_fs52.realpathSync)((0, import_path55.resolve)(projectRoot));
27783
+ realRoot = (0, import_fs53.realpathSync)((0, import_path56.resolve)(projectRoot));
27502
27784
  } catch {
27503
27785
  return out;
27504
27786
  }
@@ -27509,23 +27791,23 @@ function extractCitations(findingText, projectRoot) {
27509
27791
  const line = parseInt(m[2], 10);
27510
27792
  if (!Number.isFinite(line) || line < 1) continue;
27511
27793
  if (filePath.includes("\0")) continue;
27512
- const abs = (0, import_path55.resolve)(realRoot, filePath);
27513
- const preRel = (0, import_path55.relative)(realRoot, abs);
27794
+ const abs = (0, import_path56.resolve)(realRoot, filePath);
27795
+ const preRel = (0, import_path56.relative)(realRoot, abs);
27514
27796
  if (preRel === "" || preRel.startsWith("..")) continue;
27515
- if (!(0, import_fs52.existsSync)(abs)) {
27797
+ if (!(0, import_fs53.existsSync)(abs)) {
27516
27798
  out.push({ file: filePath, line, snippet: "// file not found" });
27517
27799
  continue;
27518
27800
  }
27519
27801
  let realAbs;
27520
27802
  try {
27521
- realAbs = (0, import_fs52.realpathSync)(abs);
27803
+ realAbs = (0, import_fs53.realpathSync)(abs);
27522
27804
  } catch {
27523
27805
  continue;
27524
27806
  }
27525
- const rel = (0, import_path55.relative)(realRoot, realAbs);
27807
+ const rel = (0, import_path56.relative)(realRoot, realAbs);
27526
27808
  if (rel === "" || rel.startsWith("..")) continue;
27527
27809
  try {
27528
- const lines = (0, import_fs52.readFileSync)(realAbs, "utf-8").split("\n");
27810
+ const lines = (0, import_fs53.readFileSync)(realAbs, "utf-8").split("\n");
27529
27811
  const start = Math.max(0, line - 1 - SNIPPET_CONTEXT);
27530
27812
  const end = Math.min(lines.length, line + SNIPPET_CONTEXT);
27531
27813
  out.push({ file: filePath, line, snippet: lines.slice(start, end).join("\n") });
@@ -27538,9 +27820,9 @@ function extractCitations(findingText, projectRoot) {
27538
27820
  async function findingHandler(projectRoot, consensusId, findingId) {
27539
27821
  if (!SAFE_ID.test(consensusId)) throw new Error(`consensus ${consensusId} not found`);
27540
27822
  if (!SAFE_ID.test(findingId)) throw new Error(`finding ${findingId} not found in ${consensusId}`);
27541
- const reportPath = (0, import_path55.join)(projectRoot, ".gossip", "consensus-reports", `${consensusId}.json`);
27542
- if (!(0, import_fs52.existsSync)(reportPath)) throw new Error(`consensus ${consensusId} not found`);
27543
- const report = JSON.parse((0, import_fs52.readFileSync)(reportPath, "utf-8"));
27823
+ const reportPath = (0, import_path56.join)(projectRoot, ".gossip", "consensus-reports", `${consensusId}.json`);
27824
+ if (!(0, import_fs53.existsSync)(reportPath)) throw new Error(`consensus ${consensusId} not found`);
27825
+ const report = JSON.parse((0, import_fs53.readFileSync)(reportPath, "utf-8"));
27544
27826
  const buckets = [
27545
27827
  ["confirmed", "confirmed"],
27546
27828
  ["disputed", "disputed"],
@@ -27561,9 +27843,9 @@ async function findingHandler(projectRoot, consensusId, findingId) {
27561
27843
  }
27562
27844
  if (!found) throw new Error(`finding ${findingId} not found in ${consensusId}`);
27563
27845
  const signals = [];
27564
- const perfPath = (0, import_path55.join)(projectRoot, ".gossip", "agent-performance.jsonl");
27565
- if ((0, import_fs52.existsSync)(perfPath)) {
27566
- const lines = (0, import_fs52.readFileSync)(perfPath, "utf-8").trim().split("\n").filter(Boolean);
27846
+ const perfPath = (0, import_path56.join)(projectRoot, ".gossip", "agent-performance.jsonl");
27847
+ if ((0, import_fs53.existsSync)(perfPath)) {
27848
+ const lines = (0, import_fs53.readFileSync)(perfPath, "utf-8").trim().split("\n").filter(Boolean);
27567
27849
  for (const line of lines) {
27568
27850
  try {
27569
27851
  const rec = JSON.parse(line);
@@ -27599,12 +27881,12 @@ async function findingHandler(projectRoot, consensusId, findingId) {
27599
27881
  citations
27600
27882
  };
27601
27883
  }
27602
- var import_fs52, import_path55, SAFE_ID, SNIPPET_CONTEXT, CITE_PATTERN;
27884
+ var import_fs53, import_path56, SAFE_ID, SNIPPET_CONTEXT, CITE_PATTERN;
27603
27885
  var init_api_finding = __esm({
27604
27886
  "packages/relay/src/dashboard/api-finding.ts"() {
27605
27887
  "use strict";
27606
- import_fs52 = require("fs");
27607
- import_path55 = require("path");
27888
+ import_fs53 = require("fs");
27889
+ import_path56 = require("path");
27608
27890
  SAFE_ID = /^[\w:.\-]+$/;
27609
27891
  SNIPPET_CONTEXT = 2;
27610
27892
  CITE_PATTERN = /<cite tag="file">([^<:]+):(\d+)<\/cite>/g;
@@ -27617,13 +27899,13 @@ async function openFindingsHandler(projectRoot) {
27617
27899
  let open = 0;
27618
27900
  let resolved = 0;
27619
27901
  let staleAnchor = 0;
27620
- const findingsPath = (0, import_path56.join)(projectRoot, ".gossip", "implementation-findings.jsonl");
27621
- if (!(0, import_fs53.existsSync)(findingsPath)) {
27902
+ const findingsPath = (0, import_path57.join)(projectRoot, ".gossip", "implementation-findings.jsonl");
27903
+ if (!(0, import_fs54.existsSync)(findingsPath)) {
27622
27904
  return { rows, totals: { open: 0, resolved: 0, staleAnchor: 0 } };
27623
27905
  }
27624
27906
  let raw;
27625
27907
  try {
27626
- raw = (0, import_fs53.readFileSync)(findingsPath, "utf-8");
27908
+ raw = (0, import_fs54.readFileSync)(findingsPath, "utf-8");
27627
27909
  } catch {
27628
27910
  return { rows, totals: { open: 0, resolved: 0, staleAnchor: 0 } };
27629
27911
  }
@@ -27637,6 +27919,7 @@ async function openFindingsHandler(projectRoot) {
27637
27919
  }
27638
27920
  const findingId = String(entry.taskId ?? entry.findingId ?? entry.id ?? "");
27639
27921
  if (!findingId) continue;
27922
+ if (entry.type === "insight") continue;
27640
27923
  let state;
27641
27924
  if (entry.status === "resolved") {
27642
27925
  state = entry.resolvedBy === "stale_anchor" ? "stale-anchor" : "resolved";
@@ -27660,40 +27943,40 @@ async function openFindingsHandler(projectRoot) {
27660
27943
  }
27661
27944
  return { rows, totals: { open, resolved, staleAnchor } };
27662
27945
  }
27663
- var import_fs53, import_path56;
27946
+ var import_fs54, import_path57;
27664
27947
  var init_api_open_findings = __esm({
27665
27948
  "packages/relay/src/dashboard/api-open-findings.ts"() {
27666
27949
  "use strict";
27667
- import_fs53 = require("fs");
27668
- import_path56 = require("path");
27950
+ import_fs54 = require("fs");
27951
+ import_path57 = require("path");
27669
27952
  }
27670
27953
  });
27671
27954
 
27672
27955
  // packages/relay/src/dashboard/api-learnings.ts
27673
27956
  async function learningsHandler(projectRoot) {
27674
- const agentsDir = (0, import_path57.join)(projectRoot, ".gossip", "agents");
27675
- if (!(0, import_fs54.existsSync)(agentsDir)) return { learnings: [] };
27957
+ const agentsDir = (0, import_path58.join)(projectRoot, ".gossip", "agents");
27958
+ if (!(0, import_fs55.existsSync)(agentsDir)) return { learnings: [] };
27676
27959
  const all = [];
27677
27960
  let agentIds;
27678
27961
  try {
27679
- agentIds = (0, import_fs54.readdirSync)(agentsDir).filter((f) => !f.startsWith("."));
27962
+ agentIds = (0, import_fs55.readdirSync)(agentsDir).filter((f) => !f.startsWith("."));
27680
27963
  } catch {
27681
27964
  return { learnings: [] };
27682
27965
  }
27683
27966
  for (const agentId of agentIds) {
27684
- const knowledgeDir = (0, import_path57.join)(agentsDir, agentId, "memory", "knowledge");
27685
- if (!(0, import_fs54.existsSync)(knowledgeDir)) continue;
27967
+ const knowledgeDir = (0, import_path58.join)(agentsDir, agentId, "memory", "knowledge");
27968
+ if (!(0, import_fs55.existsSync)(knowledgeDir)) continue;
27686
27969
  let files;
27687
27970
  try {
27688
- files = (0, import_fs54.readdirSync)(knowledgeDir).filter((f) => f.endsWith(".md"));
27971
+ files = (0, import_fs55.readdirSync)(knowledgeDir).filter((f) => f.endsWith(".md"));
27689
27972
  } catch {
27690
27973
  continue;
27691
27974
  }
27692
27975
  for (const filename of files) {
27693
- const filepath = (0, import_path57.join)(knowledgeDir, filename);
27976
+ const filepath = (0, import_path58.join)(knowledgeDir, filename);
27694
27977
  try {
27695
- const stat4 = (0, import_fs54.statSync)(filepath);
27696
- const raw = (0, import_fs54.readFileSync)(filepath, "utf-8");
27978
+ const stat4 = (0, import_fs55.statSync)(filepath);
27979
+ const raw = (0, import_fs55.readFileSync)(filepath, "utf-8");
27697
27980
  const fm = parseFrontmatter3(raw);
27698
27981
  all.push({
27699
27982
  agentId,
@@ -27720,12 +28003,12 @@ function parseFrontmatter3(raw) {
27720
28003
  }
27721
28004
  return fm;
27722
28005
  }
27723
- var import_fs54, import_path57, MAX_LEARNINGS;
28006
+ var import_fs55, import_path58, MAX_LEARNINGS;
27724
28007
  var init_api_learnings = __esm({
27725
28008
  "packages/relay/src/dashboard/api-learnings.ts"() {
27726
28009
  "use strict";
27727
- import_fs54 = require("fs");
27728
- import_path57 = require("path");
28010
+ import_fs55 = require("fs");
28011
+ import_path58 = require("path");
27729
28012
  MAX_LEARNINGS = 10;
27730
28013
  }
27731
28014
  });
@@ -27736,12 +28019,12 @@ async function tasksHandler(projectRoot, query) {
27736
28019
  const rawOffset = parseInt(query?.get("offset") ?? "0", 10);
27737
28020
  const limit = isNaN(rawLimit) || rawLimit < 1 ? 50 : Math.min(rawLimit, 2e3);
27738
28021
  const offset = isNaN(rawOffset) || rawOffset < 0 ? 0 : rawOffset;
27739
- const graphPath = (0, import_path58.join)(projectRoot, ".gossip", "task-graph.jsonl");
27740
- if (!(0, import_fs55.existsSync)(graphPath)) return { items: [], total: 0, offset, limit };
28022
+ const graphPath = (0, import_path59.join)(projectRoot, ".gossip", "task-graph.jsonl");
28023
+ if (!(0, import_fs56.existsSync)(graphPath)) return { items: [], total: 0, offset, limit };
27741
28024
  const created = /* @__PURE__ */ new Map();
27742
28025
  const completed = /* @__PURE__ */ new Map();
27743
28026
  try {
27744
- const lines = (0, import_fs55.readFileSync)(graphPath, "utf-8").trim().split("\n").filter(Boolean);
28027
+ const lines = (0, import_fs56.readFileSync)(graphPath, "utf-8").trim().split("\n").filter(Boolean);
27745
28028
  for (const line of lines) {
27746
28029
  try {
27747
28030
  const entry = JSON.parse(line);
@@ -27797,24 +28080,24 @@ async function tasksHandler(projectRoot, query) {
27797
28080
  tasks.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
27798
28081
  return { items: tasks.slice(offset, offset + limit), total: tasks.length, offset, limit };
27799
28082
  }
27800
- var import_fs55, import_path58;
28083
+ var import_fs56, import_path59;
27801
28084
  var init_api_tasks = __esm({
27802
28085
  "packages/relay/src/dashboard/api-tasks.ts"() {
27803
28086
  "use strict";
27804
- import_fs55 = require("fs");
27805
- import_path58 = require("path");
28087
+ import_fs56 = require("fs");
28088
+ import_path59 = require("path");
27806
28089
  init_utility_agents();
27807
28090
  }
27808
28091
  });
27809
28092
 
27810
28093
  // packages/relay/src/dashboard/api-active-tasks.ts
27811
28094
  async function activeTasksHandler(projectRoot) {
27812
- const taskGraphPath = (0, import_path59.join)(projectRoot, ".gossip", "task-graph.jsonl");
27813
- if (!(0, import_fs56.existsSync)(taskGraphPath)) return { tasks: [] };
28095
+ const taskGraphPath = (0, import_path60.join)(projectRoot, ".gossip", "task-graph.jsonl");
28096
+ if (!(0, import_fs57.existsSync)(taskGraphPath)) return { tasks: [] };
27814
28097
  const created = /* @__PURE__ */ new Map();
27815
28098
  const finished = /* @__PURE__ */ new Set();
27816
28099
  try {
27817
- const lines = (0, import_fs56.readFileSync)(taskGraphPath, "utf-8").trim().split("\n").filter(Boolean);
28100
+ const lines = (0, import_fs57.readFileSync)(taskGraphPath, "utf-8").trim().split("\n").filter(Boolean);
27818
28101
  for (const line of lines) {
27819
28102
  try {
27820
28103
  const ev = JSON.parse(line);
@@ -27842,12 +28125,12 @@ async function activeTasksHandler(projectRoot) {
27842
28125
  active.sort((a, b) => b.startedAt.localeCompare(a.startedAt));
27843
28126
  return { tasks: active.slice(0, 10) };
27844
28127
  }
27845
- var import_fs56, import_path59;
28128
+ var import_fs57, import_path60;
27846
28129
  var init_api_active_tasks = __esm({
27847
28130
  "packages/relay/src/dashboard/api-active-tasks.ts"() {
27848
28131
  "use strict";
27849
- import_fs56 = require("fs");
27850
- import_path59 = require("path");
28132
+ import_fs57 = require("fs");
28133
+ import_path60 = require("path");
27851
28134
  init_utility_agents();
27852
28135
  }
27853
28136
  });
@@ -27861,25 +28144,25 @@ function categorize(text) {
27861
28144
  return "other";
27862
28145
  }
27863
28146
  function logsHandler(projectRoot, query) {
27864
- const logPath = (0, import_path60.join)(projectRoot, ".gossip", "mcp.log");
27865
- if (!(0, import_fs57.existsSync)(logPath)) {
28147
+ const logPath = (0, import_path61.join)(projectRoot, ".gossip", "mcp.log");
28148
+ if (!(0, import_fs58.existsSync)(logPath)) {
27866
28149
  return { entries: [], totalLines: 0, fileSize: 0 };
27867
28150
  }
27868
28151
  const filter = query?.get("filter") || void 0;
27869
28152
  const tail = parseInt(query?.get("tail") || "200", 10);
27870
28153
  const clampedTail = Math.min(Math.max(tail, 10), 2e3);
27871
- const fileSize = (0, import_fs57.statSync)(logPath).size;
28154
+ const fileSize = (0, import_fs58.statSync)(logPath).size;
27872
28155
  const MAX_READ = 512 * 1024;
27873
28156
  const readFrom = Math.max(0, fileSize - MAX_READ);
27874
28157
  const readLen = fileSize - readFrom;
27875
- const fd = (0, import_fs57.openSync)(logPath, "r");
28158
+ const fd = (0, import_fs58.openSync)(logPath, "r");
27876
28159
  let buf = Buffer.alloc(0);
27877
28160
  try {
27878
28161
  buf = Buffer.allocUnsafe(readLen);
27879
- const bytesRead = (0, import_fs57.readSync)(fd, buf, 0, readLen, readFrom);
28162
+ const bytesRead = (0, import_fs58.readSync)(fd, buf, 0, readLen, readFrom);
27880
28163
  buf = buf.subarray(0, bytesRead);
27881
28164
  } finally {
27882
- (0, import_fs57.closeSync)(fd);
28165
+ (0, import_fs58.closeSync)(fd);
27883
28166
  }
27884
28167
  let raw = buf.toString("utf-8");
27885
28168
  let lineOffset = 0;
@@ -27887,13 +28170,13 @@ function logsHandler(projectRoot, query) {
27887
28170
  const nl = raw.indexOf("\n");
27888
28171
  raw = nl >= 0 ? raw.slice(nl + 1) : raw;
27889
28172
  const SCAN_CHUNK = 64 * 1024;
27890
- const scanFd = (0, import_fs57.openSync)(logPath, "r");
28173
+ const scanFd = (0, import_fs58.openSync)(logPath, "r");
27891
28174
  try {
27892
28175
  let pos = 0;
27893
28176
  const chunk = Buffer.allocUnsafe(SCAN_CHUNK);
27894
28177
  while (pos < readFrom) {
27895
28178
  const len = Math.min(SCAN_CHUNK, readFrom - pos);
27896
- const n = (0, import_fs57.readSync)(scanFd, chunk, 0, len, pos);
28179
+ const n = (0, import_fs58.readSync)(scanFd, chunk, 0, len, pos);
27897
28180
  if (n === 0) break;
27898
28181
  for (let j = 0; j < n; j++) {
27899
28182
  if (chunk[j] === 10) lineOffset++;
@@ -27901,7 +28184,7 @@ function logsHandler(projectRoot, query) {
27901
28184
  pos += n;
27902
28185
  }
27903
28186
  } finally {
27904
- (0, import_fs57.closeSync)(scanFd);
28187
+ (0, import_fs58.closeSync)(scanFd);
27905
28188
  }
27906
28189
  }
27907
28190
  const allLines = raw.split("\n").filter(Boolean);
@@ -27919,12 +28202,12 @@ function logsHandler(projectRoot, query) {
27919
28202
  entries = entries.slice(-clampedTail);
27920
28203
  return { entries, totalLines, fileSize, filter };
27921
28204
  }
27922
- var import_fs57, import_path60, CATEGORY_PATTERNS2;
28205
+ var import_fs58, import_path61, CATEGORY_PATTERNS2;
27923
28206
  var init_api_logs = __esm({
27924
28207
  "packages/relay/src/dashboard/api-logs.ts"() {
27925
28208
  "use strict";
27926
- import_fs57 = require("fs");
27927
- import_path60 = require("path");
28209
+ import_fs58 = require("fs");
28210
+ import_path61 = require("path");
27928
28211
  CATEGORY_PATTERNS2 = [
27929
28212
  [/^\[worker:/, "worker"],
27930
28213
  [/^\[Gemini\]/, "gemini"],
@@ -27951,18 +28234,57 @@ var init_api_logs = __esm({
27951
28234
  }
27952
28235
  });
27953
28236
 
28237
+ // packages/relay/src/dashboard/api-violations.ts
28238
+ function violationsHandler(projectRoot, query) {
28239
+ const page = Math.max(1, parseInt(query?.get("page") ?? "1", 10));
28240
+ const pageSize = Math.min(100, Math.max(1, parseInt(query?.get("pageSize") ?? "25", 10)));
28241
+ const agentFilter = query?.get("agentId") ?? null;
28242
+ const filePath = (0, import_path62.join)(projectRoot, FILE);
28243
+ if (!(0, import_fs59.existsSync)(filePath)) {
28244
+ return { items: [], total: 0, page, pageSize };
28245
+ }
28246
+ const raw = (0, import_fs59.readFileSync)(filePath, "utf-8");
28247
+ const lines = raw.trim().split("\n").filter(Boolean);
28248
+ const entries = [];
28249
+ for (const line of lines) {
28250
+ try {
28251
+ const entry = JSON.parse(line);
28252
+ if (!agentFilter || entry.agentId === agentFilter) {
28253
+ entries.push(entry);
28254
+ }
28255
+ } catch {
28256
+ continue;
28257
+ }
28258
+ }
28259
+ entries.sort(
28260
+ (a, b) => new Date(b.detectedAt).getTime() - new Date(a.detectedAt).getTime()
28261
+ );
28262
+ const total = entries.length;
28263
+ const start = (page - 1) * pageSize;
28264
+ return { items: entries.slice(start, start + pageSize), total, page, pageSize };
28265
+ }
28266
+ var import_fs59, import_path62, FILE;
28267
+ var init_api_violations = __esm({
28268
+ "packages/relay/src/dashboard/api-violations.ts"() {
28269
+ "use strict";
28270
+ import_fs59 = require("fs");
28271
+ import_path62 = require("path");
28272
+ FILE = ".gossip/process-violations.jsonl";
28273
+ }
28274
+ });
28275
+
27954
28276
  // packages/relay/src/dashboard/routes.ts
27955
28277
  function resolveDashboardRoot(projectRoot) {
27956
28278
  const candidates = [
27957
- (0, import_path61.resolve)(__dirname, "..", "dist-dashboard"),
28279
+ (0, import_path63.resolve)(__dirname, "..", "dist-dashboard"),
27958
28280
  // bundled: dist-mcp/mcp-server.js → ../dist-dashboard
27959
- (0, import_path61.resolve)(__dirname, "..", "..", "..", "..", "dist-dashboard"),
28281
+ (0, import_path63.resolve)(__dirname, "..", "..", "..", "..", "dist-dashboard"),
27960
28282
  // tsc dev: packages/relay/dist/dashboard → repo-root
27961
- (0, import_path61.join)(projectRoot, "dist-dashboard")
28283
+ (0, import_path63.join)(projectRoot, "dist-dashboard")
27962
28284
  // legacy dev fallback (git-clone running from repo root)
27963
28285
  ];
27964
28286
  for (const p of candidates) {
27965
- if ((0, import_fs58.existsSync)(p)) return p;
28287
+ if ((0, import_fs60.existsSync)(p)) return p;
27966
28288
  }
27967
28289
  return null;
27968
28290
  }
@@ -27989,7 +28311,7 @@ function readBody(req) {
27989
28311
  });
27990
28312
  });
27991
28313
  }
27992
- var import_fs58, import_path61, import_crypto21, AUTH_MAX_ATTEMPTS, AUTH_LOCKOUT_MS, DashboardRouter, MAX_BODY_SIZE;
28314
+ var import_fs60, import_path63, import_crypto21, AUTH_MAX_ATTEMPTS, AUTH_LOCKOUT_MS, DashboardRouter, MAX_BODY_SIZE;
27993
28315
  var init_routes = __esm({
27994
28316
  "packages/relay/src/dashboard/routes.ts"() {
27995
28317
  "use strict";
@@ -28008,8 +28330,9 @@ var init_routes = __esm({
28008
28330
  init_api_tasks();
28009
28331
  init_api_active_tasks();
28010
28332
  init_api_logs();
28011
- import_fs58 = require("fs");
28012
- import_path61 = require("path");
28333
+ init_api_violations();
28334
+ import_fs60 = require("fs");
28335
+ import_path63 = require("path");
28013
28336
  import_crypto21 = require("crypto");
28014
28337
  AUTH_MAX_ATTEMPTS = 10;
28015
28338
  AUTH_LOCKOUT_MS = 6e4;
@@ -28249,6 +28572,11 @@ var init_routes = __esm({
28249
28572
  this.json(res, 200, data);
28250
28573
  return true;
28251
28574
  }
28575
+ if (url2 === "/dashboard/api/violations" && req.method === "GET") {
28576
+ const data = violationsHandler(this.projectRoot, query ?? void 0);
28577
+ this.json(res, 200, data);
28578
+ return true;
28579
+ }
28252
28580
  if ((url2 === "/dashboard/api/native-memory" || url2 === "/dashboard/api/auto-memory") && req.method === "GET") {
28253
28581
  const data = await autoMemoryHandler(this.projectRoot);
28254
28582
  this.json(res, 200, data);
@@ -28281,13 +28609,13 @@ var init_routes = __esm({
28281
28609
  res.end("Dashboard assets not found. Reinstall gossipcat or rebuild from source.");
28282
28610
  return true;
28283
28611
  }
28284
- const htmlPath = (0, import_path61.join)(this.dashboardRoot, "index.html");
28285
- if (!(0, import_fs58.existsSync)(htmlPath)) {
28612
+ const htmlPath = (0, import_path63.join)(this.dashboardRoot, "index.html");
28613
+ if (!(0, import_fs60.existsSync)(htmlPath)) {
28286
28614
  res.writeHead(503, { "Content-Type": "text/plain" });
28287
28615
  res.end(`Dashboard index.html missing at ${this.dashboardRoot}. Reinstall gossipcat.`);
28288
28616
  return true;
28289
28617
  }
28290
- const html = (0, import_fs58.readFileSync)(htmlPath, "utf-8");
28618
+ const html = (0, import_fs60.readFileSync)(htmlPath, "utf-8");
28291
28619
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
28292
28620
  res.end(html);
28293
28621
  return true;
@@ -28313,16 +28641,16 @@ var init_routes = __esm({
28313
28641
  const ext = "." + (relativePath.split(".").pop() || "");
28314
28642
  const mime = MIME[ext];
28315
28643
  if (!mime) return false;
28316
- const filePath = (0, import_path61.join)(this.dashboardRoot, relativePath);
28644
+ const filePath = (0, import_path63.join)(this.dashboardRoot, relativePath);
28317
28645
  try {
28318
- const realFile = (0, import_fs58.realpathSync)(filePath);
28319
- const realBase = (0, import_fs58.realpathSync)(this.dashboardRoot);
28646
+ const realFile = (0, import_fs60.realpathSync)(filePath);
28647
+ const realBase = (0, import_fs60.realpathSync)(this.dashboardRoot);
28320
28648
  if (!realFile.startsWith(realBase + "/")) {
28321
28649
  res.writeHead(404);
28322
28650
  res.end();
28323
28651
  return true;
28324
28652
  }
28325
- const data = (0, import_fs58.readFileSync)(realFile);
28653
+ const data = (0, import_fs60.readFileSync)(realFile);
28326
28654
  res.writeHead(200, { "Content-Type": mime, "Cache-Control": "public, max-age=86400" });
28327
28655
  res.end(data);
28328
28656
  return true;
@@ -28337,8 +28665,8 @@ var init_routes = __esm({
28337
28665
  return match ? match[1] : null;
28338
28666
  }
28339
28667
  getConsensusReports(page = 1, pageSize = 5) {
28340
- const { readdirSync: readdirSync18, readFileSync: readFileSync59, existsSync: existsSync61 } = require("fs");
28341
- const reportsDir = (0, import_path61.join)(this.projectRoot, ".gossip", "consensus-reports");
28668
+ const { readdirSync: readdirSync18, readFileSync: readFileSync61, existsSync: existsSync64 } = require("fs");
28669
+ const reportsDir = (0, import_path63.join)(this.projectRoot, ".gossip", "consensus-reports");
28342
28670
  let retractedConsensusIds = [];
28343
28671
  let roundRetractions = [];
28344
28672
  try {
@@ -28348,13 +28676,13 @@ var init_routes = __esm({
28348
28676
  roundRetractions = reader.getRoundRetractions();
28349
28677
  } catch {
28350
28678
  }
28351
- if (!existsSync61(reportsDir)) return { reports: [], totalReports: 0, page, pageSize, retractedConsensusIds, roundRetractions };
28679
+ if (!existsSync64(reportsDir)) return { reports: [], totalReports: 0, page, pageSize, retractedConsensusIds, roundRetractions };
28352
28680
  try {
28353
- const { statSync: statSync23 } = require("fs");
28681
+ const { statSync: statSync24 } = require("fs");
28354
28682
  const allFiles = readdirSync18(reportsDir).filter((f) => f.endsWith(".json")).sort((a, b) => {
28355
28683
  try {
28356
- const aTime = statSync23((0, import_path61.join)(reportsDir, a)).mtimeMs;
28357
- const bTime = statSync23((0, import_path61.join)(reportsDir, b)).mtimeMs;
28684
+ const aTime = statSync24((0, import_path63.join)(reportsDir, a)).mtimeMs;
28685
+ const bTime = statSync24((0, import_path63.join)(reportsDir, b)).mtimeMs;
28358
28686
  return bTime - aTime;
28359
28687
  } catch {
28360
28688
  return 0;
@@ -28365,13 +28693,13 @@ var init_routes = __esm({
28365
28693
  const clampedPage = Math.max(page, 1);
28366
28694
  const start = (clampedPage - 1) * clampedPageSize;
28367
28695
  const files = allFiles.slice(start, start + clampedPageSize);
28368
- const realReportsDir = (0, import_fs58.realpathSync)(reportsDir);
28696
+ const realReportsDir = (0, import_fs60.realpathSync)(reportsDir);
28369
28697
  const reports = files.map((f) => {
28370
28698
  try {
28371
- const filePath = (0, import_path61.join)(reportsDir, f);
28372
- const realFile = (0, import_fs58.realpathSync)(filePath);
28699
+ const filePath = (0, import_path63.join)(reportsDir, f);
28700
+ const realFile = (0, import_fs60.realpathSync)(filePath);
28373
28701
  if (!realFile.startsWith(realReportsDir + "/")) return null;
28374
- return JSON.parse(readFileSync59(realFile, "utf-8"));
28702
+ return JSON.parse(readFileSync61(realFile, "utf-8"));
28375
28703
  } catch {
28376
28704
  return null;
28377
28705
  }
@@ -28382,29 +28710,29 @@ var init_routes = __esm({
28382
28710
  }
28383
28711
  }
28384
28712
  archiveFindings() {
28385
- const { readdirSync: readdirSync18, readFileSync: readFileSync59, renameSync: renameSync8, writeFileSync: writeFileSync25, mkdirSync: mkdirSync30, existsSync: existsSync61 } = require("fs");
28386
- const reportsDir = (0, import_path61.join)(this.projectRoot, ".gossip", "consensus-reports");
28387
- const archiveDir = (0, import_path61.join)(this.projectRoot, ".gossip", "consensus-reports-archive");
28713
+ const { readdirSync: readdirSync18, readFileSync: readFileSync61, renameSync: renameSync9, writeFileSync: writeFileSync26, mkdirSync: mkdirSync33, existsSync: existsSync64 } = require("fs");
28714
+ const reportsDir = (0, import_path63.join)(this.projectRoot, ".gossip", "consensus-reports");
28715
+ const archiveDir = (0, import_path63.join)(this.projectRoot, ".gossip", "consensus-reports-archive");
28388
28716
  let archived = 0;
28389
- if (existsSync61(reportsDir)) {
28717
+ if (existsSync64(reportsDir)) {
28390
28718
  const files = readdirSync18(reportsDir).filter((f) => f.endsWith(".json")).sort().reverse();
28391
28719
  if (files.length > 5) {
28392
- mkdirSync30(archiveDir, { recursive: true });
28720
+ mkdirSync33(archiveDir, { recursive: true });
28393
28721
  const toArchive = files.slice(5);
28394
28722
  for (const f of toArchive) {
28395
28723
  try {
28396
- renameSync8((0, import_path61.join)(reportsDir, f), (0, import_path61.join)(archiveDir, f));
28724
+ renameSync9((0, import_path63.join)(reportsDir, f), (0, import_path63.join)(archiveDir, f));
28397
28725
  archived++;
28398
28726
  } catch {
28399
28727
  }
28400
28728
  }
28401
28729
  }
28402
28730
  }
28403
- const findingsPath = (0, import_path61.join)(this.projectRoot, ".gossip", "implementation-findings.jsonl");
28731
+ const findingsPath = (0, import_path63.join)(this.projectRoot, ".gossip", "implementation-findings.jsonl");
28404
28732
  let findingsCleared = 0;
28405
- if (existsSync61(findingsPath)) {
28733
+ if (existsSync64(findingsPath)) {
28406
28734
  try {
28407
- const lines = readFileSync59(findingsPath, "utf-8").trim().split("\n").filter(Boolean);
28735
+ const lines = readFileSync61(findingsPath, "utf-8").trim().split("\n").filter(Boolean);
28408
28736
  const kept = lines.filter((line) => {
28409
28737
  try {
28410
28738
  const entry = JSON.parse(line);
@@ -28417,11 +28745,11 @@ var init_routes = __esm({
28417
28745
  return true;
28418
28746
  }
28419
28747
  });
28420
- writeFileSync25(findingsPath, kept.join("\n") + (kept.length > 0 ? "\n" : ""));
28748
+ writeFileSync26(findingsPath, kept.join("\n") + (kept.length > 0 ? "\n" : ""));
28421
28749
  } catch {
28422
28750
  }
28423
28751
  }
28424
- const remaining = existsSync61(reportsDir) ? readdirSync18(reportsDir).filter((f) => f.endsWith(".json")).length : 0;
28752
+ const remaining = existsSync64(reportsDir) ? readdirSync18(reportsDir).filter((f) => f.endsWith(".json")).length : 0;
28425
28753
  return { archived, remaining, findingsCleared };
28426
28754
  }
28427
28755
  json(res, status, data) {
@@ -28434,14 +28762,14 @@ var init_routes = __esm({
28434
28762
  });
28435
28763
 
28436
28764
  // packages/relay/src/dashboard/ws.ts
28437
- var import_ws3, import_fs59, import_fs60, import_path62, DashboardWs;
28765
+ var import_ws3, import_fs61, import_fs62, import_path64, DashboardWs;
28438
28766
  var init_ws = __esm({
28439
28767
  "packages/relay/src/dashboard/ws.ts"() {
28440
28768
  "use strict";
28441
28769
  import_ws3 = require("ws");
28442
- import_fs59 = require("fs");
28443
- import_fs60 = require("fs");
28444
- import_path62 = require("path");
28770
+ import_fs61 = require("fs");
28771
+ import_fs62 = require("fs");
28772
+ import_path64 = require("path");
28445
28773
  DashboardWs = class {
28446
28774
  clients = /* @__PURE__ */ new Set();
28447
28775
  logWatcher = null;
@@ -28466,15 +28794,15 @@ var init_ws = __esm({
28466
28794
  /** Start watching mcp.log for new lines and broadcasting them to connected clients. */
28467
28795
  startLogWatcher(projectRoot) {
28468
28796
  this.stopLogWatcher();
28469
- this.logPath = (0, import_path62.join)(projectRoot, ".gossip", "mcp.log");
28470
- if (!(0, import_fs59.existsSync)(this.logPath)) return;
28797
+ this.logPath = (0, import_path64.join)(projectRoot, ".gossip", "mcp.log");
28798
+ if (!(0, import_fs61.existsSync)(this.logPath)) return;
28471
28799
  try {
28472
- this.logOffset = (0, import_fs59.statSync)(this.logPath).size;
28800
+ this.logOffset = (0, import_fs61.statSync)(this.logPath).size;
28473
28801
  } catch {
28474
28802
  this.logOffset = 0;
28475
28803
  }
28476
28804
  try {
28477
- this.logWatcher = (0, import_fs60.watch)(this.logPath, () => {
28805
+ this.logWatcher = (0, import_fs62.watch)(this.logPath, () => {
28478
28806
  if (this.clients.size === 0) return;
28479
28807
  this.readNewLines();
28480
28808
  });
@@ -28491,7 +28819,7 @@ var init_ws = __esm({
28491
28819
  if (this.logReading) return;
28492
28820
  this.logReading = true;
28493
28821
  try {
28494
- const currentSize = (0, import_fs59.statSync)(this.logPath).size;
28822
+ const currentSize = (0, import_fs61.statSync)(this.logPath).size;
28495
28823
  if (currentSize < this.logOffset) {
28496
28824
  this.logOffset = 0;
28497
28825
  this.logCarry = "";
@@ -28504,7 +28832,7 @@ var init_ws = __esm({
28504
28832
  const readFrom = capped ? (this.logCarry = "", this.logCapped = true, currentSize - 65536) : (this.logCapped = false, this.logOffset);
28505
28833
  let stream;
28506
28834
  try {
28507
- stream = (0, import_fs59.createReadStream)(this.logPath, { start: readFrom, end: currentSize - 1 });
28835
+ stream = (0, import_fs61.createReadStream)(this.logPath, { start: readFrom, end: currentSize - 1 });
28508
28836
  } catch {
28509
28837
  this.logReading = false;
28510
28838
  return;
@@ -29013,14 +29341,14 @@ __export(sandbox_exports, {
29013
29341
  });
29014
29342
  function rotateIfNeeded2(filePath, maxBytes) {
29015
29343
  try {
29016
- const st = (0, import_fs61.statSync)(filePath);
29344
+ const st = (0, import_fs63.statSync)(filePath);
29017
29345
  if (st.size < maxBytes) return;
29018
29346
  const rotated = filePath + ".1";
29019
29347
  try {
29020
- if ((0, import_fs61.existsSync)(rotated)) (0, import_fs61.unlinkSync)(rotated);
29348
+ if ((0, import_fs63.existsSync)(rotated)) (0, import_fs63.unlinkSync)(rotated);
29021
29349
  } catch {
29022
29350
  }
29023
- (0, import_fs61.renameSync)(filePath, rotated);
29351
+ (0, import_fs63.renameSync)(filePath, rotated);
29024
29352
  } catch {
29025
29353
  }
29026
29354
  }
@@ -29093,9 +29421,9 @@ Tests passing is NOT sufficient verification \u2014 the 2026-04-22 incident had
29093
29421
  }
29094
29422
  function readSandboxMode(projectRoot) {
29095
29423
  try {
29096
- const p = (0, import_path63.join)(projectRoot, ".gossip", "config.json");
29097
- if (!(0, import_fs61.existsSync)(p)) return "warn";
29098
- const raw = JSON.parse((0, import_fs61.readFileSync)(p, "utf-8"));
29424
+ const p = (0, import_path65.join)(projectRoot, ".gossip", "config.json");
29425
+ if (!(0, import_fs63.existsSync)(p)) return "warn";
29426
+ const raw = JSON.parse((0, import_fs63.readFileSync)(p, "utf-8"));
29099
29427
  const mode = raw?.sandboxEnforcement;
29100
29428
  if (mode === "off" || mode === "warn" || mode === "block") return mode;
29101
29429
  return "warn";
@@ -29105,8 +29433,8 @@ function readSandboxMode(projectRoot) {
29105
29433
  }
29106
29434
  function recordDispatchMetadata(projectRoot, meta3) {
29107
29435
  try {
29108
- const dir = (0, import_path63.join)(projectRoot, ".gossip");
29109
- (0, import_fs61.mkdirSync)(dir, { recursive: true });
29436
+ const dir = (0, import_path65.join)(projectRoot, ".gossip");
29437
+ (0, import_fs63.mkdirSync)(dir, { recursive: true });
29110
29438
  const snapshotted = { ...meta3 };
29111
29439
  if (meta3.writeMode === "scoped" || meta3.writeMode === "worktree") {
29112
29440
  try {
@@ -29124,15 +29452,15 @@ function recordDispatchMetadata(projectRoot, meta3) {
29124
29452
  } catch {
29125
29453
  }
29126
29454
  }
29127
- (0, import_fs61.appendFileSync)((0, import_path63.join)(dir, METADATA_FILE), JSON.stringify(snapshotted) + "\n");
29455
+ (0, import_fs63.appendFileSync)((0, import_path65.join)(dir, METADATA_FILE), JSON.stringify(snapshotted) + "\n");
29128
29456
  } catch {
29129
29457
  }
29130
29458
  }
29131
29459
  function updateDispatchMetadata(projectRoot, taskId, patch) {
29132
29460
  try {
29133
- const p = (0, import_path63.join)(projectRoot, ".gossip", METADATA_FILE);
29134
- if (!(0, import_fs61.existsSync)(p)) return false;
29135
- const raw = (0, import_fs61.readFileSync)(p, "utf-8");
29461
+ const p = (0, import_path65.join)(projectRoot, ".gossip", METADATA_FILE);
29462
+ if (!(0, import_fs63.existsSync)(p)) return false;
29463
+ const raw = (0, import_fs63.readFileSync)(p, "utf-8");
29136
29464
  const lines = raw.split("\n");
29137
29465
  let patched = false;
29138
29466
  for (let i = lines.length - 1; i >= 0; i--) {
@@ -29150,7 +29478,7 @@ function updateDispatchMetadata(projectRoot, taskId, patch) {
29150
29478
  }
29151
29479
  }
29152
29480
  if (!patched) return false;
29153
- (0, import_fs61.writeFileSync)(p, lines.join("\n"));
29481
+ (0, import_fs63.writeFileSync)(p, lines.join("\n"));
29154
29482
  return true;
29155
29483
  } catch {
29156
29484
  return false;
@@ -29159,14 +29487,14 @@ function updateDispatchMetadata(projectRoot, taskId, patch) {
29159
29487
  function stampTaskSentinel(projectRoot, taskId) {
29160
29488
  if (!taskId) return null;
29161
29489
  try {
29162
- const dir = (0, import_path63.join)(projectRoot, ".gossip", SENTINEL_DIR);
29163
- (0, import_fs61.mkdirSync)(dir, { recursive: true });
29490
+ const dir = (0, import_path65.join)(projectRoot, ".gossip", SENTINEL_DIR);
29491
+ (0, import_fs63.mkdirSync)(dir, { recursive: true });
29164
29492
  const slug = taskId.replace(/[^a-zA-Z0-9_-]/g, "_");
29165
- const path6 = (0, import_path63.join)(dir, `${slug}.sentinel`);
29166
- const fd = (0, import_fs61.openSync)(path6, "w");
29167
- (0, import_fs61.closeSync)(fd);
29493
+ const path6 = (0, import_path65.join)(dir, `${slug}.sentinel`);
29494
+ const fd = (0, import_fs63.openSync)(path6, "w");
29495
+ (0, import_fs63.closeSync)(fd);
29168
29496
  const stampTime = new Date(Date.now() - 2e3);
29169
- (0, import_fs61.utimesSync)(path6, stampTime, stampTime);
29497
+ (0, import_fs63.utimesSync)(path6, stampTime, stampTime);
29170
29498
  return path6;
29171
29499
  } catch {
29172
29500
  return null;
@@ -29175,15 +29503,15 @@ function stampTaskSentinel(projectRoot, taskId) {
29175
29503
  function cleanupTaskSentinel(sentinelPath) {
29176
29504
  if (!sentinelPath) return;
29177
29505
  try {
29178
- (0, import_fs61.unlinkSync)(sentinelPath);
29506
+ (0, import_fs63.unlinkSync)(sentinelPath);
29179
29507
  } catch {
29180
29508
  }
29181
29509
  }
29182
29510
  function lookupDispatchMetadata(projectRoot, taskId) {
29183
29511
  try {
29184
- const p = (0, import_path63.join)(projectRoot, ".gossip", METADATA_FILE);
29185
- if (!(0, import_fs61.existsSync)(p)) return null;
29186
- const raw = (0, import_fs61.readFileSync)(p, "utf-8");
29512
+ const p = (0, import_path65.join)(projectRoot, ".gossip", METADATA_FILE);
29513
+ if (!(0, import_fs63.existsSync)(p)) return null;
29514
+ const raw = (0, import_fs63.readFileSync)(p, "utf-8");
29187
29515
  const lines = raw.split("\n").filter(Boolean);
29188
29516
  for (let i = lines.length - 1; i >= 0; i--) {
29189
29517
  try {
@@ -29215,21 +29543,21 @@ function parseGitStatus(porcelain) {
29215
29543
  function normalizeScope(scope, projectRoot) {
29216
29544
  let s = scope.trim();
29217
29545
  if (!s) return "";
29218
- if ((0, import_path63.isAbsolute)(s)) {
29219
- s = (0, import_path63.relative)(projectRoot, s);
29546
+ if ((0, import_path65.isAbsolute)(s)) {
29547
+ s = (0, import_path65.relative)(projectRoot, s);
29220
29548
  } else if (s.startsWith("./")) {
29221
29549
  s = s.slice(2);
29222
29550
  }
29223
- s = (0, import_path63.normalize)(s).replace(/\/+$/, "");
29551
+ s = (0, import_path65.normalize)(s).replace(/\/+$/, "");
29224
29552
  if (s === "." || s === "") return "";
29225
29553
  return s;
29226
29554
  }
29227
29555
  function isInsideScope(filePath, scope) {
29228
29556
  if (!scope) return true;
29229
- const f = (0, import_path63.normalize)(filePath).replace(/^\.\//, "");
29557
+ const f = (0, import_path65.normalize)(filePath).replace(/^\.\//, "");
29230
29558
  const s = scope.replace(/^\.\//, "").replace(/\/+$/, "");
29231
29559
  if (f === s) return true;
29232
- return f.startsWith(s + "/") || f.startsWith(s + import_path63.sep);
29560
+ return f.startsWith(s + "/") || f.startsWith(s + import_path65.sep);
29233
29561
  }
29234
29562
  function detectBoundaryEscapes(meta3, modifiedFiles, projectRoot) {
29235
29563
  const mode = meta3.writeMode;
@@ -29290,8 +29618,8 @@ function recordBoundaryEscape(projectRoot, meta3, violations, mode) {
29290
29618
  } catch {
29291
29619
  }
29292
29620
  try {
29293
- const dir = (0, import_path63.join)(projectRoot, ".gossip");
29294
- (0, import_fs61.mkdirSync)(dir, { recursive: true });
29621
+ const dir = (0, import_path65.join)(projectRoot, ".gossip");
29622
+ (0, import_fs63.mkdirSync)(dir, { recursive: true });
29295
29623
  const line = {
29296
29624
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
29297
29625
  taskId: meta3.taskId,
@@ -29302,15 +29630,15 @@ function recordBoundaryEscape(projectRoot, meta3, violations, mode) {
29302
29630
  action: mode
29303
29631
  // "warn" or "block"
29304
29632
  };
29305
- const escapeFile = (0, import_path63.join)(dir, BOUNDARY_ESCAPE_FILE);
29633
+ const escapeFile = (0, import_path65.join)(dir, BOUNDARY_ESCAPE_FILE);
29306
29634
  rotateIfNeeded2(escapeFile, MAX_BOUNDARY_ESCAPE_BYTES);
29307
- (0, import_fs61.appendFileSync)(escapeFile, JSON.stringify(line) + "\n");
29635
+ (0, import_fs63.appendFileSync)(escapeFile, JSON.stringify(line) + "\n");
29308
29636
  } catch {
29309
29637
  }
29310
29638
  }
29311
29639
  function canonicalize(p) {
29312
29640
  try {
29313
- return (0, import_path63.resolve)(p).replace(/\/+$/, "") || "/";
29641
+ return (0, import_path65.resolve)(p).replace(/\/+$/, "") || "/";
29314
29642
  } catch {
29315
29643
  return p.replace(/\/+$/, "") || "/";
29316
29644
  }
@@ -29521,7 +29849,7 @@ function buildAuditExclusions(projectRoot, ownWorktree, scope) {
29521
29849
  for (const v of expandTmpVariants(wt)) excl.add(v);
29522
29850
  }
29523
29851
  if (scope) {
29524
- const s = canonicalize((0, import_path63.join)(projectRoot, scope));
29852
+ const s = canonicalize((0, import_path65.join)(projectRoot, scope));
29525
29853
  for (const v of expandTmpVariants(s)) excl.add(v);
29526
29854
  }
29527
29855
  return Array.from(excl);
@@ -29575,12 +29903,12 @@ function auditFilesystemSinceSentinel(projectRoot, meta3, options = {}) {
29575
29903
  return { violations: [], skipped: "win32" };
29576
29904
  }
29577
29905
  const sentinel = meta3.sentinelPath;
29578
- if (!sentinel || !(0, import_fs61.existsSync)(sentinel)) {
29906
+ if (!sentinel || !(0, import_fs63.existsSync)(sentinel)) {
29579
29907
  return { violations: [], skipped: "sentinel missing" };
29580
29908
  }
29581
29909
  let sentinelMtimeMs = 0;
29582
29910
  try {
29583
- sentinelMtimeMs = (0, import_fs61.statSync)(sentinel).mtimeMs;
29911
+ sentinelMtimeMs = (0, import_fs63.statSync)(sentinel).mtimeMs;
29584
29912
  } catch {
29585
29913
  }
29586
29914
  if (sentinelMtimeMs === 0) {
@@ -29592,11 +29920,11 @@ function auditFilesystemSinceSentinel(projectRoot, meta3, options = {}) {
29592
29920
  );
29593
29921
  const exclusions = buildAuditExclusions(projectRoot, meta3.worktreePath, options.scope);
29594
29922
  const findBin = options.findBinary ?? "find";
29595
- const sentinelDir = canonicalize((0, import_path63.join)(projectRoot, ".gossip", SENTINEL_DIR));
29923
+ const sentinelDir = canonicalize((0, import_path65.join)(projectRoot, ".gossip", SENTINEL_DIR));
29596
29924
  const mainSet = /* @__PURE__ */ new Set();
29597
29925
  const sensitiveSet = /* @__PURE__ */ new Set();
29598
29926
  for (const root of scanRoots) {
29599
- if (!(0, import_fs61.existsSync)(root)) continue;
29927
+ if (!(0, import_fs63.existsSync)(root)) continue;
29600
29928
  const canonRoot = canonicalize(root);
29601
29929
  const allExcl = [...exclusions, ...expandTmpVariants(sentinelDir)];
29602
29930
  const args = buildFindPruneArgs(canonRoot, allExcl, sentinel);
@@ -29632,7 +29960,7 @@ function auditFilesystemSinceSentinel(projectRoot, meta3, options = {}) {
29632
29960
  }
29633
29961
  const sensitiveTargets = buildSensitiveTargets(platform2);
29634
29962
  for (const target of sensitiveTargets) {
29635
- if (!(0, import_fs61.existsSync)(target.path)) continue;
29963
+ if (!(0, import_fs63.existsSync)(target.path)) continue;
29636
29964
  const canonTarget = canonicalize(target.path);
29637
29965
  const args = buildSensitiveFindArgs(
29638
29966
  canonTarget,
@@ -29688,8 +30016,8 @@ function auditFilesystemSinceSentinel(projectRoot, meta3, options = {}) {
29688
30016
  }
29689
30017
  function recordLayer3Violations(projectRoot, meta3, violations, source) {
29690
30018
  try {
29691
- const dir = (0, import_path63.join)(projectRoot, ".gossip");
29692
- (0, import_fs61.mkdirSync)(dir, { recursive: true });
30019
+ const dir = (0, import_path65.join)(projectRoot, ".gossip");
30020
+ (0, import_fs63.mkdirSync)(dir, { recursive: true });
29693
30021
  const ts2 = (/* @__PURE__ */ new Date()).toISOString();
29694
30022
  const lines = violations.map(
29695
30023
  (path6) => JSON.stringify({
@@ -29700,9 +30028,9 @@ function recordLayer3Violations(projectRoot, meta3, violations, source) {
29700
30028
  source
29701
30029
  })
29702
30030
  );
29703
- const escapeFile = (0, import_path63.join)(dir, BOUNDARY_ESCAPE_FILE);
30031
+ const escapeFile = (0, import_path65.join)(dir, BOUNDARY_ESCAPE_FILE);
29704
30032
  rotateIfNeeded2(escapeFile, MAX_BOUNDARY_ESCAPE_BYTES);
29705
- (0, import_fs61.appendFileSync)(escapeFile, lines.join("\n") + "\n");
30033
+ (0, import_fs63.appendFileSync)(escapeFile, lines.join("\n") + "\n");
29706
30034
  } catch {
29707
30035
  }
29708
30036
  if (source === "layer3-sensitive" && violations.length > 0) {
@@ -29759,14 +30087,14 @@ function runLayer3Audit(projectRoot, taskId) {
29759
30087
  }
29760
30088
  return { blockError, warnPrefix };
29761
30089
  }
29762
- var import_child_process8, import_fs61, import_os3, import_path63, 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, __test__;
30090
+ var import_child_process8, import_fs63, import_os3, import_path65, 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, __test__;
29763
30091
  var init_sandbox2 = __esm({
29764
30092
  "apps/cli/src/sandbox.ts"() {
29765
30093
  "use strict";
29766
30094
  import_child_process8 = require("child_process");
29767
- import_fs61 = require("fs");
30095
+ import_fs63 = require("fs");
29768
30096
  import_os3 = require("os");
29769
- import_path63 = require("path");
30097
+ import_path65 = require("path");
29770
30098
  METADATA_FILE = "dispatch-metadata.jsonl";
29771
30099
  BOUNDARY_ESCAPE_FILE = "boundary-escapes.jsonl";
29772
30100
  SENTINEL_DIR = "sentinels";
@@ -29825,6 +30153,114 @@ var init_sandbox2 = __esm({
29825
30153
  }
29826
30154
  });
29827
30155
 
30156
+ // apps/cli/src/handlers/ref-allowlist-detection.ts
30157
+ var ref_allowlist_detection_exports = {};
30158
+ __export(ref_allowlist_detection_exports, {
30159
+ capturePreDispatchSha: () => capturePreDispatchSha,
30160
+ checkRefAllowlistViolation: () => checkRefAllowlistViolation
30161
+ });
30162
+ function capturePreDispatchSha() {
30163
+ try {
30164
+ const sha = (0, import_child_process9.execFileSync)("git", ["rev-parse", "origin/master"], {
30165
+ cwd: process.cwd(),
30166
+ stdio: ["ignore", "pipe", "ignore"]
30167
+ }).toString().trim();
30168
+ return sha || null;
30169
+ } catch {
30170
+ process.stderr.write("[gossipcat] ref-allowlist: could not read origin/master SHA (offline or no remote) \u2014 skipping pre-dispatch snapshot\n");
30171
+ return null;
30172
+ }
30173
+ }
30174
+ function getPrMergeCommits(preSha, postSha) {
30175
+ try {
30176
+ const out = (0, import_child_process9.execFileSync)(
30177
+ "git",
30178
+ ["log", `${preSha}..${postSha}`, `--grep=(#[0-9]`, "--format=%H %s"],
30179
+ { cwd: process.cwd(), stdio: ["ignore", "pipe", "ignore"] }
30180
+ ).toString().trim();
30181
+ return out ? out.split("\n").filter(Boolean) : [];
30182
+ } catch {
30183
+ return [];
30184
+ }
30185
+ }
30186
+ function getCommitRange(preSha, postSha) {
30187
+ try {
30188
+ const out = (0, import_child_process9.execFileSync)(
30189
+ "git",
30190
+ ["log", `${preSha}..${postSha}`, "--format=%H %s"],
30191
+ { cwd: process.cwd(), stdio: ["ignore", "pipe", "ignore"] }
30192
+ ).toString().trim();
30193
+ return out ? out.split("\n").filter(Boolean) : [];
30194
+ } catch {
30195
+ return [];
30196
+ }
30197
+ }
30198
+ function appendViolationRecord(record2) {
30199
+ try {
30200
+ const projectRoot = process.cwd();
30201
+ (0, import_fs64.mkdirSync)((0, import_path66.join)(projectRoot, ".gossip"), { recursive: true });
30202
+ const logPath = (0, import_path66.join)(projectRoot, PROCESS_VIOLATIONS_FILE);
30203
+ (0, import_fs64.appendFileSync)(logPath, JSON.stringify(record2) + "\n", "utf8");
30204
+ } catch (err) {
30205
+ process.stderr.write(`[gossipcat] ref-allowlist: failed to append violation record: ${err.message}
30206
+ `);
30207
+ }
30208
+ }
30209
+ function checkRefAllowlistViolation(taskId, agentId, preSha) {
30210
+ let postSha;
30211
+ try {
30212
+ postSha = (0, import_child_process9.execFileSync)("git", ["rev-parse", "origin/master"], {
30213
+ cwd: process.cwd(),
30214
+ stdio: ["ignore", "pipe", "ignore"]
30215
+ }).toString().trim();
30216
+ } catch {
30217
+ return;
30218
+ }
30219
+ if (!postSha || postSha === preSha) return;
30220
+ const mergeCommits = getPrMergeCommits(preSha, postSha);
30221
+ if (mergeCommits.length > 0) return;
30222
+ const allCommits = getCommitRange(preSha, postSha);
30223
+ const detectedAt = (/* @__PURE__ */ new Date()).toISOString();
30224
+ appendViolationRecord({ taskId, agentId, preSha, postSha, detectedAt, commits: allCommits });
30225
+ try {
30226
+ const { emitConsensusSignals: emitConsensusSignals2 } = (init_src4(), __toCommonJS(src_exports3));
30227
+ emitConsensusSignals2(process.cwd(), [
30228
+ {
30229
+ type: "consensus",
30230
+ signal: "boundary_escape",
30231
+ agentId,
30232
+ taskId,
30233
+ findingId: `proc:${taskId}:master_push`,
30234
+ category: "process_discipline",
30235
+ severity: "high",
30236
+ evidence: `origin/master moved from ${preSha} to ${postSha} during task ${taskId} without a PR-merge entry \u2014 direct push detected. Commits: ${allCommits.slice(0, 5).join("; ")}`,
30237
+ timestamp: detectedAt
30238
+ }
30239
+ ]);
30240
+ } catch (err) {
30241
+ process.stderr.write(`[gossipcat] ref-allowlist: failed to emit boundary_escape signal: ${err.message}
30242
+ `);
30243
+ }
30244
+ process.stderr.write(
30245
+ `
30246
+ REF-ALLOWLIST VIOLATION: ${taskId} agent=${agentId} origin/master moved ${preSha.slice(0, 8)}\u2192${postSha.slice(0, 8)} with no PR merge.
30247
+ Operator confirmation required for revert or retroactive PR.
30248
+ Full audit trail at .gossip/process-violations.jsonl
30249
+
30250
+ `
30251
+ );
30252
+ }
30253
+ var import_fs64, import_path66, import_child_process9, PROCESS_VIOLATIONS_FILE;
30254
+ var init_ref_allowlist_detection = __esm({
30255
+ "apps/cli/src/handlers/ref-allowlist-detection.ts"() {
30256
+ "use strict";
30257
+ import_fs64 = require("fs");
30258
+ import_path66 = require("path");
30259
+ import_child_process9 = require("child_process");
30260
+ PROCESS_VIOLATIONS_FILE = ".gossip/process-violations.jsonl";
30261
+ }
30262
+ });
30263
+
29828
30264
  // apps/cli/src/handlers/native-tasks.ts
29829
30265
  function pruneExpiredRecentConsensusTaskIds(now = Date.now()) {
29830
30266
  try {
@@ -29904,11 +30340,11 @@ function taskWasInConsensusRound(taskId, agentId, rounds, recentTaskIds, recentA
29904
30340
  }
29905
30341
  function appendRelayWarning(projectRoot, entry) {
29906
30342
  try {
29907
- const { appendFileSync: appendFileSync16, mkdirSync: mkdirSync30 } = require("fs");
29908
- const { join: join70 } = require("path");
29909
- const dir = join70(projectRoot, ".gossip");
29910
- mkdirSync30(dir, { recursive: true });
29911
- appendFileSync16(join70(dir, RELAY_WARNINGS_FILE), JSON.stringify(entry) + "\n", "utf8");
30343
+ const { appendFileSync: appendFileSync18, mkdirSync: mkdirSync33 } = require("fs");
30344
+ const { join: join73 } = require("path");
30345
+ const dir = join73(projectRoot, ".gossip");
30346
+ mkdirSync33(dir, { recursive: true });
30347
+ appendFileSync18(join73(dir, RELAY_WARNINGS_FILE), JSON.stringify(entry) + "\n", "utf8");
29912
30348
  } catch (err) {
29913
30349
  process.stderr.write(`[gossipcat] append relay-warning failed: ${err.message}
29914
30350
  `);
@@ -30224,6 +30660,13 @@ async function handleNativeRelay(task_id, result, error48, agentStartedAt, relay
30224
30660
  ctx.mainAgent.scopeTracker.release(task_id);
30225
30661
  } catch {
30226
30662
  }
30663
+ if (taskInfo.writeMode && taskInfo.preDispatchSha) {
30664
+ try {
30665
+ const { checkRefAllowlistViolation: checkRefAllowlistViolation2 } = (init_ref_allowlist_detection(), __toCommonJS(ref_allowlist_detection_exports));
30666
+ checkRefAllowlistViolation2(task_id, taskInfo.agentId, taskInfo.preDispatchSha);
30667
+ } catch {
30668
+ }
30669
+ }
30227
30670
  if (error48 && taskInfo.writeMode === "worktree") {
30228
30671
  try {
30229
30672
  ctx.mainAgent.getWorktreeManager()?.pruneOrphans().catch(() => {
@@ -30575,12 +31018,12 @@ function startConsensusTimeout(consensusId) {
30575
31018
  const allEntries = [...snapshot.relayCrossReviewEntries, ...snapshot.nativeCrossReviewEntries];
30576
31019
  const report = await engine.synthesizeWithCrossReview(snapshot.allResults, allEntries, consensusId, snapshot.relayCrossReviewSkipped);
30577
31020
  try {
30578
- const { writeFileSync: writeFileSync25, mkdirSync: mkdirSync30 } = require("fs");
30579
- const { join: join70 } = require("path");
30580
- const reportsDir = join70(process.cwd(), ".gossip", "consensus-reports");
30581
- mkdirSync30(reportsDir, { recursive: true });
31021
+ const { writeFileSync: writeFileSync26, mkdirSync: mkdirSync33 } = require("fs");
31022
+ const { join: join73 } = require("path");
31023
+ const reportsDir = join73(process.cwd(), ".gossip", "consensus-reports");
31024
+ mkdirSync33(reportsDir, { recursive: true });
30582
31025
  const topic = snapshot.allResults?.find((r) => r.task)?.task?.slice(0, 500) || "";
30583
- writeFileSync25(join70(reportsDir, `${consensusId}.json`), JSON.stringify({
31026
+ writeFileSync26(join73(reportsDir, `${consensusId}.json`), JSON.stringify({
30584
31027
  id: consensusId,
30585
31028
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
30586
31029
  topic,
@@ -30747,13 +31190,13 @@ async function handleRelayCrossReview(consensus_id, agent_id, result) {
30747
31190
  synthSnapshot.relayCrossReviewSkipped
30748
31191
  );
30749
31192
  try {
30750
- const { writeFileSync: writeFileSync25, mkdirSync: mkdirSync30 } = require("fs");
30751
- const { join: join70 } = require("path");
30752
- const reportsDir = join70(process.cwd(), ".gossip", "consensus-reports");
30753
- mkdirSync30(reportsDir, { recursive: true });
30754
- const reportPath = join70(reportsDir, `${consensus_id}.json`);
31193
+ const { writeFileSync: writeFileSync26, mkdirSync: mkdirSync33 } = require("fs");
31194
+ const { join: join73 } = require("path");
31195
+ const reportsDir = join73(process.cwd(), ".gossip", "consensus-reports");
31196
+ mkdirSync33(reportsDir, { recursive: true });
31197
+ const reportPath = join73(reportsDir, `${consensus_id}.json`);
30755
31198
  const topic = synthSnapshot.allResults?.find((r) => r.task)?.task?.slice(0, 500) || "";
30756
- writeFileSync25(reportPath, JSON.stringify({
31199
+ writeFileSync26(reportPath, JSON.stringify({
30757
31200
  id: consensus_id,
30758
31201
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
30759
31202
  topic,
@@ -30796,10 +31239,10 @@ function persistPendingConsensus() {
30796
31239
  try {
30797
31240
  const projectRoot = ctx.mainAgent?.projectRoot;
30798
31241
  if (!projectRoot) return;
30799
- const { writeFileSync: writeFileSync25, mkdirSync: mkdirSync30 } = require("fs");
30800
- const { join: join70 } = require("path");
30801
- const dir = join70(projectRoot, ".gossip");
30802
- mkdirSync30(dir, { recursive: true });
31242
+ const { writeFileSync: writeFileSync26, mkdirSync: mkdirSync33 } = require("fs");
31243
+ const { join: join73 } = require("path");
31244
+ const dir = join73(projectRoot, ".gossip");
31245
+ mkdirSync33(dir, { recursive: true });
30803
31246
  const rounds = {};
30804
31247
  for (const [id, round] of ctx.pendingConsensusRounds) {
30805
31248
  rounds[id] = {
@@ -30824,7 +31267,7 @@ function persistPendingConsensus() {
30824
31267
  resolutionRoots: round.resolutionRoots ? [...round.resolutionRoots] : void 0
30825
31268
  };
30826
31269
  }
30827
- writeFileSync25(join70(dir, CONSENSUS_FILE), JSON.stringify(rounds));
31270
+ writeFileSync26(join73(dir, CONSENSUS_FILE), JSON.stringify(rounds));
30828
31271
  } catch (err) {
30829
31272
  process.stderr.write(`[gossipcat] persistPendingConsensus failed: ${err.message}
30830
31273
  `);
@@ -30832,11 +31275,11 @@ function persistPendingConsensus() {
30832
31275
  }
30833
31276
  function restorePendingConsensus(projectRoot) {
30834
31277
  try {
30835
- const { existsSync: existsSync61, readFileSync: readFileSync59, unlinkSync: unlinkSync7 } = require("fs");
30836
- const { join: join70 } = require("path");
30837
- const filePath = join70(projectRoot, ".gossip", CONSENSUS_FILE);
30838
- if (!existsSync61(filePath)) return;
30839
- const raw = JSON.parse(readFileSync59(filePath, "utf-8"));
31278
+ const { existsSync: existsSync64, readFileSync: readFileSync61, unlinkSync: unlinkSync8 } = require("fs");
31279
+ const { join: join73 } = require("path");
31280
+ const filePath = join73(projectRoot, ".gossip", CONSENSUS_FILE);
31281
+ if (!existsSync64(filePath)) return;
31282
+ const raw = JSON.parse(readFileSync61(filePath, "utf-8"));
30840
31283
  const now = Date.now();
30841
31284
  for (const [id, data] of Object.entries(raw)) {
30842
31285
  if (now > data.deadline + 3e5) {
@@ -30867,7 +31310,7 @@ function restorePendingConsensus(projectRoot) {
30867
31310
  process.stderr.write(`[gossipcat] Restored consensus round ${id} \u2014 ${data.pendingNativeAgents?.length ?? 0} agents pending
30868
31311
  `);
30869
31312
  }
30870
- unlinkSync7(filePath);
31313
+ unlinkSync8(filePath);
30871
31314
  } catch {
30872
31315
  }
30873
31316
  }
@@ -30881,6 +31324,178 @@ var init_relay_cross_review = __esm({
30881
31324
  }
30882
31325
  });
30883
31326
 
31327
+ // apps/cli/src/lifecycle-tasks.ts
31328
+ var lifecycle_tasks_exports = {};
31329
+ __export(lifecycle_tasks_exports, {
31330
+ __resetLifecycleTasksForTests: () => __resetLifecycleTasksForTests,
31331
+ drainLifecycleTasks: () => drainLifecycleTasks,
31332
+ installLifecycleDrainHandlers: () => installLifecycleDrainHandlers,
31333
+ trackLifecycleTask: () => trackLifecycleTask
31334
+ });
31335
+ function trackLifecycleTask(p) {
31336
+ inFlight.add(p);
31337
+ p.finally(() => {
31338
+ inFlight.delete(p);
31339
+ }).catch(() => {
31340
+ });
31341
+ }
31342
+ function drainLifecycleTasks(maxMs = 8e3) {
31343
+ if (inFlight.size === 0) return Promise.resolve();
31344
+ const snapshot = [...inFlight];
31345
+ return Promise.race([
31346
+ Promise.allSettled(snapshot).then(() => void 0),
31347
+ new Promise((resolve28) => setTimeout(resolve28, maxMs).unref?.())
31348
+ ]).then(() => void 0);
31349
+ }
31350
+ function installLifecycleDrainHandlers() {
31351
+ if (__installed) return;
31352
+ __installed = true;
31353
+ const drainAndContinue = async (signal) => {
31354
+ try {
31355
+ const pending = inFlight.size;
31356
+ if (pending > 0) {
31357
+ process.stderr.write(`[gossipcat] ${signal}: draining ${pending} lifecycle task(s)
31358
+ `);
31359
+ }
31360
+ await drainLifecycleTasks();
31361
+ } catch {
31362
+ }
31363
+ };
31364
+ process.on("beforeExit", () => {
31365
+ void drainAndContinue("beforeExit");
31366
+ });
31367
+ }
31368
+ function __resetLifecycleTasksForTests() {
31369
+ if (process.env.NODE_ENV !== "test") {
31370
+ throw new Error("__resetLifecycleTasksForTests is test-only \u2014 refusing to run outside NODE_ENV=test");
31371
+ }
31372
+ inFlight.clear();
31373
+ __installed = false;
31374
+ }
31375
+ var inFlight, __installed;
31376
+ var init_lifecycle_tasks = __esm({
31377
+ "apps/cli/src/lifecycle-tasks.ts"() {
31378
+ "use strict";
31379
+ inFlight = /* @__PURE__ */ new Set();
31380
+ __installed = false;
31381
+ }
31382
+ });
31383
+
31384
+ // apps/cli/src/handlers/check-effectiveness-runner.ts
31385
+ var check_effectiveness_runner_exports = {};
31386
+ __export(check_effectiveness_runner_exports, {
31387
+ runCheckEffectivenessForAllSkills: () => runCheckEffectivenessForAllSkills
31388
+ });
31389
+ function writeHealthAtomic(projectRoot, record2) {
31390
+ const dir = (0, import_path68.join)(projectRoot, ".gossip");
31391
+ const finalPath = (0, import_path68.join)(dir, "skill-runner-health.json");
31392
+ const tmpPath = finalPath + ".tmp";
31393
+ try {
31394
+ (0, import_fs66.mkdirSync)(dir, { recursive: true });
31395
+ (0, import_fs66.writeFileSync)(tmpPath, JSON.stringify(record2, null, 2));
31396
+ (0, import_fs66.renameSync)(tmpPath, finalPath);
31397
+ } catch (e) {
31398
+ process.stderr.write(`[gossipcat] checkEffectiveness: health write failed: ${e.message}
31399
+ `);
31400
+ try {
31401
+ if ((0, import_fs66.existsSync)(tmpPath)) (0, import_fs66.unlinkSync)(tmpPath);
31402
+ } catch {
31403
+ }
31404
+ }
31405
+ }
31406
+ async function runCheckEffectivenessForAllSkills(opts) {
31407
+ const startedAt = Date.now();
31408
+ const baseDir = (0, import_path68.join)(opts.projectRoot, ".gossip", "agents");
31409
+ let agentDirs = [];
31410
+ let canonicalBaseDir;
31411
+ if ((0, import_fs66.existsSync)(baseDir)) {
31412
+ try {
31413
+ canonicalBaseDir = (0, import_fs66.realpathSync)(baseDir);
31414
+ agentDirs = (0, import_fs66.readdirSync)(canonicalBaseDir);
31415
+ } catch {
31416
+ }
31417
+ }
31418
+ process.stderr.write(`[gossipcat] checkEffectiveness: scanning across ${agentDirs.length} agents
31419
+ `);
31420
+ let skillsChecked = 0;
31421
+ const transitions = {
31422
+ passed: 0,
31423
+ failed: 0,
31424
+ flagged_for_manual_review: 0,
31425
+ inconclusive: 0,
31426
+ pending: 0
31427
+ };
31428
+ let lastError = null;
31429
+ if (canonicalBaseDir && agentDirs.length > 0) {
31430
+ for (const agentId of agentDirs) {
31431
+ if (agentId.startsWith("_")) continue;
31432
+ if (!SAFE_NAME2.test(agentId)) continue;
31433
+ const skillsDir = (0, import_path68.join)(canonicalBaseDir, agentId, "skills");
31434
+ if (!(0, import_fs66.existsSync)(skillsDir)) continue;
31435
+ let canonicalSkillsDir;
31436
+ try {
31437
+ canonicalSkillsDir = (0, import_fs66.realpathSync)(skillsDir);
31438
+ } catch {
31439
+ continue;
31440
+ }
31441
+ if (!canonicalSkillsDir.startsWith(canonicalBaseDir + "/")) {
31442
+ process.stderr.write(`[gossipcat] checkEffectiveness: skipping ${agentId} \u2014 skillsDir resolved outside agents tree (canonical=${canonicalSkillsDir})
31443
+ `);
31444
+ continue;
31445
+ }
31446
+ const role = opts.registryGet(agentId)?.role;
31447
+ if (role === "implementer") continue;
31448
+ const files = (0, import_fs66.readdirSync)(canonicalSkillsDir).filter((f) => f.endsWith(".md"));
31449
+ for (const file2 of files) {
31450
+ const category = file2.replace(/\.md$/, "");
31451
+ if (!SAFE_NAME2.test(category)) continue;
31452
+ skillsChecked++;
31453
+ try {
31454
+ const verdict = await opts.skillEngine.checkEffectiveness(agentId, category, { role });
31455
+ if (verdict.shouldUpdate) {
31456
+ const loggedStates = /* @__PURE__ */ new Set(["passed", "failed", "flagged_for_manual_review"]);
31457
+ if (loggedStates.has(verdict.status)) {
31458
+ process.stderr.write(
31459
+ `[gossipcat] checkEffectiveness ${agentId}/${category}: ${verdict.status}` + (verdict.effectiveness !== void 0 ? ` (\u0394=${verdict.effectiveness.toFixed(3)})` : "") + `
31460
+ `
31461
+ );
31462
+ }
31463
+ if (transitions[verdict.status] != null) {
31464
+ transitions[verdict.status]++;
31465
+ }
31466
+ }
31467
+ } catch (e) {
31468
+ lastError = e.message;
31469
+ process.stderr.write(`[gossipcat] checkEffectiveness ${agentId}/${category} threw: ${lastError}
31470
+ `);
31471
+ }
31472
+ }
31473
+ }
31474
+ }
31475
+ const totalTransitions = transitions.passed + transitions.failed + transitions.flagged_for_manual_review + transitions.inconclusive + transitions.pending;
31476
+ const durationMs = Date.now() - startedAt;
31477
+ process.stderr.write(
31478
+ `[gossipcat] checkEffectiveness: done in ${durationMs}ms (skills: ${skillsChecked}, transitions: ${totalTransitions})
31479
+ `
31480
+ );
31481
+ writeHealthAtomic(opts.projectRoot, {
31482
+ last_run_at: new Date(startedAt).toISOString(),
31483
+ last_run_duration_ms: durationMs,
31484
+ skills_evaluated: skillsChecked,
31485
+ transitions,
31486
+ last_error: lastError
31487
+ });
31488
+ }
31489
+ var import_fs66, import_path68, SAFE_NAME2;
31490
+ var init_check_effectiveness_runner = __esm({
31491
+ "apps/cli/src/handlers/check-effectiveness-runner.ts"() {
31492
+ "use strict";
31493
+ import_fs66 = require("fs");
31494
+ import_path68 = require("path");
31495
+ SAFE_NAME2 = /^(?!.*\.\.)[a-zA-Z0-9._-]+$/;
31496
+ }
31497
+ });
31498
+
30884
31499
  // apps/cli/src/config.ts
30885
31500
  var config_exports = {};
30886
31501
  __export(config_exports, {
@@ -30897,18 +31512,18 @@ __export(config_exports, {
30897
31512
  function findConfigPath(projectRoot) {
30898
31513
  const root = projectRoot || process.cwd();
30899
31514
  const candidates = [
30900
- (0, import_path65.resolve)(root, ".gossip", "config.json"),
30901
- (0, import_path65.resolve)(root, "gossip.agents.json"),
30902
- (0, import_path65.resolve)(root, "gossip.agents.yaml"),
30903
- (0, import_path65.resolve)(root, "gossip.agents.yml")
31515
+ (0, import_path69.resolve)(root, ".gossip", "config.json"),
31516
+ (0, import_path69.resolve)(root, "gossip.agents.json"),
31517
+ (0, import_path69.resolve)(root, "gossip.agents.yaml"),
31518
+ (0, import_path69.resolve)(root, "gossip.agents.yml")
30904
31519
  ];
30905
31520
  for (const p of candidates) {
30906
- if ((0, import_fs63.existsSync)(p)) return p;
31521
+ if ((0, import_fs67.existsSync)(p)) return p;
30907
31522
  }
30908
31523
  return null;
30909
31524
  }
30910
31525
  function loadConfig(configPath) {
30911
- const raw = (0, import_fs63.readFileSync)(configPath, "utf-8");
31526
+ const raw = (0, import_fs67.readFileSync)(configPath, "utf-8");
30912
31527
  let parsed;
30913
31528
  try {
30914
31529
  parsed = JSON.parse(raw);
@@ -30998,19 +31613,19 @@ function configToAgentConfigs(config2) {
30998
31613
  }
30999
31614
  function loadClaudeSubagents(projectRoot, existingIds) {
31000
31615
  const root = projectRoot || process.cwd();
31001
- const agentsDir = (0, import_path65.join)(root, ".claude", "agents");
31002
- if (!(0, import_fs63.existsSync)(agentsDir)) return [];
31616
+ const agentsDir = (0, import_path69.join)(root, ".claude", "agents");
31617
+ if (!(0, import_fs67.existsSync)(agentsDir)) return [];
31003
31618
  let files;
31004
31619
  try {
31005
- files = (0, import_fs63.readdirSync)(agentsDir).filter((f) => f.endsWith(".md"));
31620
+ files = (0, import_fs67.readdirSync)(agentsDir).filter((f) => f.endsWith(".md"));
31006
31621
  } catch {
31007
31622
  return [];
31008
31623
  }
31009
31624
  const agents = [];
31010
31625
  for (const file2 of files) {
31011
- const filePath = (0, import_path65.join)(agentsDir, file2);
31626
+ const filePath = (0, import_path69.join)(agentsDir, file2);
31012
31627
  try {
31013
- const content = (0, import_fs63.readFileSync)(filePath, "utf-8");
31628
+ const content = (0, import_fs67.readFileSync)(filePath, "utf-8");
31014
31629
  const frontmatter = content.match(/^---\n([\s\S]*?)\n---/);
31015
31630
  if (!frontmatter) continue;
31016
31631
  const fm = frontmatter[1];
@@ -31065,12 +31680,12 @@ function inferSkills(description, name) {
31065
31680
  if (skills.length === 0) skills.push("general");
31066
31681
  return skills;
31067
31682
  }
31068
- var import_fs63, import_path65, VALID_PROVIDERS, VALID_MAIN_PROVIDERS, CLAUDE_MODEL_MAP;
31683
+ var import_fs67, import_path69, VALID_PROVIDERS, VALID_MAIN_PROVIDERS, CLAUDE_MODEL_MAP;
31069
31684
  var init_config = __esm({
31070
31685
  "apps/cli/src/config.ts"() {
31071
31686
  "use strict";
31072
- import_fs63 = require("fs");
31073
- import_path65 = require("path");
31687
+ import_fs67 = require("fs");
31688
+ import_path69 = require("path");
31074
31689
  VALID_PROVIDERS = ["anthropic", "openai", "openclaw", "google", "local", "native", "none"];
31075
31690
  VALID_MAIN_PROVIDERS = VALID_PROVIDERS.filter((p) => p !== "native");
31076
31691
  CLAUDE_MODEL_MAP = {
@@ -31088,86 +31703,38 @@ __export(skill_develop_audit_exports, {
31088
31703
  });
31089
31704
  function appendSkillDevelopAudit(entry) {
31090
31705
  try {
31091
- const gossipDir2 = (0, import_path66.join)(process.cwd(), ".gossip");
31092
- (0, import_fs64.mkdirSync)(gossipDir2, { recursive: true });
31706
+ const gossipDir2 = (0, import_path70.join)(process.cwd(), ".gossip");
31707
+ (0, import_fs68.mkdirSync)(gossipDir2, { recursive: true });
31093
31708
  const line = JSON.stringify(entry) + "\n";
31094
- (0, import_fs64.appendFileSync)((0, import_path66.join)(gossipDir2, AUDIT_FILE), line);
31095
- (0, import_fs64.appendFileSync)((0, import_path66.join)(gossipDir2, LEGACY_FILE), line);
31709
+ (0, import_fs68.appendFileSync)((0, import_path70.join)(gossipDir2, AUDIT_FILE), line);
31710
+ (0, import_fs68.appendFileSync)((0, import_path70.join)(gossipDir2, LEGACY_FILE), line);
31096
31711
  } catch {
31097
31712
  }
31098
31713
  }
31099
- var import_fs64, import_path66, AUDIT_FILE, LEGACY_FILE;
31714
+ var import_fs68, import_path70, AUDIT_FILE, LEGACY_FILE;
31100
31715
  var init_skill_develop_audit = __esm({
31101
31716
  "apps/cli/src/handlers/skill-develop-audit.ts"() {
31102
31717
  "use strict";
31103
- import_fs64 = require("fs");
31104
- import_path66 = require("path");
31718
+ import_fs68 = require("fs");
31719
+ import_path70 = require("path");
31105
31720
  AUDIT_FILE = "skill-develop-audit.jsonl";
31106
31721
  LEGACY_FILE = "forced-skill-develops.jsonl";
31107
31722
  }
31108
31723
  });
31109
31724
 
31110
- // apps/cli/src/handlers/check-effectiveness-runner.ts
31111
- var check_effectiveness_runner_exports = {};
31112
- __export(check_effectiveness_runner_exports, {
31113
- runCheckEffectivenessForAllSkills: () => runCheckEffectivenessForAllSkills
31114
- });
31115
- async function runCheckEffectivenessForAllSkills(opts) {
31116
- const baseDir = (0, import_path67.join)(opts.projectRoot, ".gossip", "agents");
31117
- if (!(0, import_fs65.existsSync)(baseDir)) return;
31118
- const agentDirs = (0, import_fs65.readdirSync)(baseDir);
31119
- for (const agentId of agentDirs) {
31120
- if (agentId.startsWith("_")) continue;
31121
- if (!SAFE_NAME2.test(agentId)) continue;
31122
- const skillsDir = (0, import_path67.join)(baseDir, agentId, "skills");
31123
- if (!(0, import_fs65.existsSync)(skillsDir)) continue;
31124
- const role = opts.registryGet(agentId)?.role;
31125
- if (role === "implementer") continue;
31126
- const files = (0, import_fs65.readdirSync)(skillsDir).filter((f) => f.endsWith(".md"));
31127
- for (const file2 of files) {
31128
- const category = file2.replace(/\.md$/, "");
31129
- if (!SAFE_NAME2.test(category)) continue;
31130
- try {
31131
- const verdict = await opts.skillEngine.checkEffectiveness(agentId, category, { role });
31132
- if (verdict.shouldUpdate) {
31133
- const loggedStates = /* @__PURE__ */ new Set(["passed", "failed", "flagged_for_manual_review"]);
31134
- if (loggedStates.has(verdict.status)) {
31135
- process.stderr.write(
31136
- `[gossipcat] checkEffectiveness ${agentId}/${category}: ${verdict.status}` + (verdict.effectiveness !== void 0 ? ` (\u0394=${verdict.effectiveness.toFixed(3)})` : "") + `
31137
- `
31138
- );
31139
- }
31140
- }
31141
- } catch (e) {
31142
- process.stderr.write(`[gossipcat] checkEffectiveness ${agentId}/${category} threw: ${e.message}
31143
- `);
31144
- }
31145
- }
31146
- }
31147
- }
31148
- var import_fs65, import_path67, SAFE_NAME2;
31149
- var init_check_effectiveness_runner = __esm({
31150
- "apps/cli/src/handlers/check-effectiveness-runner.ts"() {
31151
- "use strict";
31152
- import_fs65 = require("fs");
31153
- import_path67 = require("path");
31154
- SAFE_NAME2 = /^(?!.*\.\.)[a-zA-Z0-9._-]+$/;
31155
- }
31156
- });
31157
-
31158
31725
  // apps/cli/src/keychain.ts
31159
31726
  var keychain_exports = {};
31160
31727
  __export(keychain_exports, {
31161
31728
  Keychain: () => Keychain
31162
31729
  });
31163
- var import_child_process9, import_os4, import_fs69, import_path71, import_crypto25, DEFAULT_SERVICE_NAME, VALID_PROVIDERS2, ENCRYPTED_FILE, ALGO, Keychain;
31730
+ var import_child_process10, import_os4, import_fs72, import_path74, import_crypto25, DEFAULT_SERVICE_NAME, VALID_PROVIDERS2, ENCRYPTED_FILE, ALGO, Keychain;
31164
31731
  var init_keychain = __esm({
31165
31732
  "apps/cli/src/keychain.ts"() {
31166
31733
  "use strict";
31167
- import_child_process9 = require("child_process");
31734
+ import_child_process10 = require("child_process");
31168
31735
  import_os4 = require("os");
31169
- import_fs69 = require("fs");
31170
- import_path71 = require("path");
31736
+ import_fs72 = require("fs");
31737
+ import_path74 = require("path");
31171
31738
  import_crypto25 = require("crypto");
31172
31739
  DEFAULT_SERVICE_NAME = "gossip-mesh";
31173
31740
  VALID_PROVIDERS2 = /^[a-zA-Z0-9_-]{1,32}$/;
@@ -31211,10 +31778,10 @@ var init_keychain = __esm({
31211
31778
  return (0, import_crypto25.pbkdf2Sync)(seed, salt, 6e5, 32, "sha256");
31212
31779
  }
31213
31780
  loadEncryptedFile() {
31214
- const filePath = (0, import_path71.join)(process.cwd(), ENCRYPTED_FILE);
31215
- if (!(0, import_fs69.existsSync)(filePath)) return;
31781
+ const filePath = (0, import_path74.join)(process.cwd(), ENCRYPTED_FILE);
31782
+ if (!(0, import_fs72.existsSync)(filePath)) return;
31216
31783
  try {
31217
- const raw = (0, import_fs69.readFileSync)(filePath);
31784
+ const raw = (0, import_fs72.readFileSync)(filePath);
31218
31785
  if (raw.length < 61) return;
31219
31786
  const salt = raw.subarray(0, 32);
31220
31787
  const iv = raw.subarray(32, 44);
@@ -31232,9 +31799,9 @@ var init_keychain = __esm({
31232
31799
  }
31233
31800
  }
31234
31801
  saveEncryptedFile() {
31235
- const filePath = (0, import_path71.join)(process.cwd(), ENCRYPTED_FILE);
31236
- const dir = (0, import_path71.join)(process.cwd(), ".gossip");
31237
- if (!(0, import_fs69.existsSync)(dir)) (0, import_fs69.mkdirSync)(dir, { recursive: true });
31802
+ const filePath = (0, import_path74.join)(process.cwd(), ENCRYPTED_FILE);
31803
+ const dir = (0, import_path74.join)(process.cwd(), ".gossip");
31804
+ if (!(0, import_fs72.existsSync)(dir)) (0, import_fs72.mkdirSync)(dir, { recursive: true });
31238
31805
  const data = JSON.stringify(Object.fromEntries(this.inMemoryStore));
31239
31806
  const salt = (0, import_crypto25.randomBytes)(32);
31240
31807
  const iv = (0, import_crypto25.randomBytes)(12);
@@ -31242,12 +31809,12 @@ var init_keychain = __esm({
31242
31809
  const cipher = (0, import_crypto25.createCipheriv)(ALGO, key, iv);
31243
31810
  const encrypted = Buffer.concat([cipher.update(data, "utf8"), cipher.final()]);
31244
31811
  const tag = cipher.getAuthTag();
31245
- (0, import_fs69.writeFileSync)(filePath, Buffer.concat([salt, iv, tag, encrypted]), { mode: 384 });
31812
+ (0, import_fs72.writeFileSync)(filePath, Buffer.concat([salt, iv, tag, encrypted]), { mode: 384 });
31246
31813
  }
31247
31814
  isKeychainAvailable() {
31248
31815
  if ((0, import_os4.platform)() === "darwin") {
31249
31816
  try {
31250
- (0, import_child_process9.execFileSync)("security", ["help"], { stdio: "pipe" });
31817
+ (0, import_child_process10.execFileSync)("security", ["help"], { stdio: "pipe" });
31251
31818
  return true;
31252
31819
  } catch {
31253
31820
  return false;
@@ -31255,7 +31822,7 @@ var init_keychain = __esm({
31255
31822
  }
31256
31823
  if ((0, import_os4.platform)() === "linux") {
31257
31824
  try {
31258
- (0, import_child_process9.execFileSync)("which", ["secret-tool"], { stdio: "pipe" });
31825
+ (0, import_child_process10.execFileSync)("which", ["secret-tool"], { stdio: "pipe" });
31259
31826
  return true;
31260
31827
  } catch {
31261
31828
  return false;
@@ -31271,7 +31838,7 @@ var init_keychain = __esm({
31271
31838
  readFromKeychain(provider) {
31272
31839
  this.validateProvider(provider);
31273
31840
  if ((0, import_os4.platform)() === "darwin") {
31274
- return (0, import_child_process9.execFileSync)("security", [
31841
+ return (0, import_child_process10.execFileSync)("security", [
31275
31842
  "find-generic-password",
31276
31843
  "-s",
31277
31844
  this.serviceName,
@@ -31281,7 +31848,7 @@ var init_keychain = __esm({
31281
31848
  ], { stdio: "pipe" }).toString().trim();
31282
31849
  }
31283
31850
  if ((0, import_os4.platform)() === "linux") {
31284
- return (0, import_child_process9.execFileSync)("secret-tool", [
31851
+ return (0, import_child_process10.execFileSync)("secret-tool", [
31285
31852
  "lookup",
31286
31853
  "service",
31287
31854
  this.serviceName,
@@ -31295,7 +31862,7 @@ var init_keychain = __esm({
31295
31862
  this.validateProvider(provider);
31296
31863
  if ((0, import_os4.platform)() === "darwin") {
31297
31864
  try {
31298
- (0, import_child_process9.execFileSync)("security", [
31865
+ (0, import_child_process10.execFileSync)("security", [
31299
31866
  "delete-generic-password",
31300
31867
  "-s",
31301
31868
  this.serviceName,
@@ -31304,7 +31871,7 @@ var init_keychain = __esm({
31304
31871
  ], { stdio: "pipe" });
31305
31872
  } catch {
31306
31873
  }
31307
- (0, import_child_process9.execFileSync)("security", [
31874
+ (0, import_child_process10.execFileSync)("security", [
31308
31875
  "add-generic-password",
31309
31876
  "-s",
31310
31877
  this.serviceName,
@@ -31316,7 +31883,7 @@ var init_keychain = __esm({
31316
31883
  return;
31317
31884
  }
31318
31885
  if ((0, import_os4.platform)() === "linux") {
31319
- (0, import_child_process9.execFileSync)("secret-tool", [
31886
+ (0, import_child_process10.execFileSync)("secret-tool", [
31320
31887
  "store",
31321
31888
  "--label",
31322
31889
  `Gossip Mesh ${provider}`,
@@ -31342,23 +31909,23 @@ __export(identity_exports, {
31342
31909
  normalizeGitUrl: () => normalizeGitUrl
31343
31910
  });
31344
31911
  function getOrCreateSalt(projectRoot) {
31345
- const saltPath = (0, import_path72.join)(projectRoot, ".gossip", "local-salt");
31912
+ const saltPath = (0, import_path75.join)(projectRoot, ".gossip", "local-salt");
31346
31913
  try {
31347
- return (0, import_fs70.readFileSync)(saltPath, "utf-8").trim();
31914
+ return (0, import_fs73.readFileSync)(saltPath, "utf-8").trim();
31348
31915
  } catch {
31349
31916
  const salt = (0, import_crypto26.randomBytes)(16).toString("hex");
31350
- (0, import_fs70.mkdirSync)((0, import_path72.join)(projectRoot, ".gossip"), { recursive: true });
31917
+ (0, import_fs73.mkdirSync)((0, import_path75.join)(projectRoot, ".gossip"), { recursive: true });
31351
31918
  try {
31352
- (0, import_fs70.writeFileSync)(saltPath, salt, { flag: "wx" });
31919
+ (0, import_fs73.writeFileSync)(saltPath, salt, { flag: "wx" });
31353
31920
  return salt;
31354
31921
  } catch {
31355
- return (0, import_fs70.readFileSync)(saltPath, "utf-8").trim();
31922
+ return (0, import_fs73.readFileSync)(saltPath, "utf-8").trim();
31356
31923
  }
31357
31924
  }
31358
31925
  }
31359
31926
  function getUserId(projectRoot) {
31360
31927
  try {
31361
- const email3 = (0, import_child_process10.execFileSync)("git", ["config", "user.email"], { stdio: "pipe" }).toString().trim();
31928
+ const email3 = (0, import_child_process11.execFileSync)("git", ["config", "user.email"], { stdio: "pipe" }).toString().trim();
31362
31929
  const salt = getOrCreateSalt(projectRoot);
31363
31930
  return (0, import_crypto26.createHash)("sha256").update(email3 + projectRoot + salt).digest("hex").slice(0, 16);
31364
31931
  } catch {
@@ -31381,7 +31948,7 @@ function getTeamUserId(email3, teamSalt) {
31381
31948
  }
31382
31949
  function getGitEmail() {
31383
31950
  try {
31384
- const email3 = (0, import_child_process10.execFileSync)("git", ["config", "user.email"], { stdio: "pipe" }).toString().trim();
31951
+ const email3 = (0, import_child_process11.execFileSync)("git", ["config", "user.email"], { stdio: "pipe" }).toString().trim();
31385
31952
  return email3 || null;
31386
31953
  } catch {
31387
31954
  return null;
@@ -31389,7 +31956,7 @@ function getGitEmail() {
31389
31956
  }
31390
31957
  function getProjectId(projectRoot) {
31391
31958
  try {
31392
- const remoteUrl = (0, import_child_process10.execFileSync)(
31959
+ const remoteUrl = (0, import_child_process11.execFileSync)(
31393
31960
  "git",
31394
31961
  ["config", "--get", "remote.origin.url"],
31395
31962
  { cwd: projectRoot, stdio: "pipe" }
@@ -31402,14 +31969,14 @@ function getProjectId(projectRoot) {
31402
31969
  }
31403
31970
  return (0, import_crypto26.createHash)("sha256").update(projectRoot).digest("hex").slice(0, 16);
31404
31971
  }
31405
- var import_fs70, import_path72, import_crypto26, import_child_process10;
31972
+ var import_fs73, import_path75, import_crypto26, import_child_process11;
31406
31973
  var init_identity = __esm({
31407
31974
  "apps/cli/src/identity.ts"() {
31408
31975
  "use strict";
31409
- import_fs70 = require("fs");
31410
- import_path72 = require("path");
31976
+ import_fs73 = require("fs");
31977
+ import_path75 = require("path");
31411
31978
  import_crypto26 = require("crypto");
31412
- import_child_process10 = require("child_process");
31979
+ import_child_process11 = require("child_process");
31413
31980
  }
31414
31981
  });
31415
31982
 
@@ -31429,9 +31996,9 @@ async function getLatestVersion() {
31429
31996
  return data.version;
31430
31997
  }
31431
31998
  function detectInstallMethod() {
31432
- const packageRoot = (0, import_path73.resolve)(__dirname, "..", "..", "..", "..");
31999
+ const packageRoot = (0, import_path76.resolve)(__dirname, "..", "..", "..", "..");
31433
32000
  if (process.env.npm_config_global === "true") return "global";
31434
- if ((0, import_fs71.existsSync)((0, import_path73.join)(packageRoot, ".git"))) return "git-clone";
32001
+ if ((0, import_fs74.existsSync)((0, import_path76.join)(packageRoot, ".git"))) return "git-clone";
31435
32002
  return "local";
31436
32003
  }
31437
32004
  function updateCommand(method, version2) {
@@ -31479,10 +32046,15 @@ Check your internet connection or visit https://www.npmjs.com/package/gossipcat
31479
32046
  }]
31480
32047
  };
31481
32048
  }
32049
+ const scrubbedEnv = { ...process.env };
32050
+ for (const key of Object.keys(scrubbedEnv)) {
32051
+ if (/^GOSSIPCAT_/i.test(key)) delete scrubbedEnv[key];
32052
+ }
31482
32053
  try {
31483
- (0, import_child_process11.execSync)(command, {
32054
+ (0, import_child_process12.execSync)(command, {
31484
32055
  stdio: "inherit",
31485
- cwd: method === "git-clone" ? (0, import_path73.resolve)(__dirname, "..", "..", "..", "..") : process.cwd()
32056
+ cwd: method === "git-clone" ? (0, import_path76.resolve)(__dirname, "..", "..", "..", "..") : process.cwd(),
32057
+ env: scrubbedEnv
31486
32058
  });
31487
32059
  } catch (err) {
31488
32060
  return {
@@ -31504,13 +32076,13 @@ Run /mcp reconnect in Claude Code to load the new version.`
31504
32076
  }]
31505
32077
  };
31506
32078
  }
31507
- var import_child_process11, import_fs71, import_path73;
32079
+ var import_child_process12, import_fs74, import_path76;
31508
32080
  var init_gossip_update = __esm({
31509
32081
  "apps/cli/src/handlers/gossip-update.ts"() {
31510
32082
  "use strict";
31511
- import_child_process11 = require("child_process");
31512
- import_fs71 = require("fs");
31513
- import_path73 = require("path");
32083
+ import_child_process12 = require("child_process");
32084
+ import_fs74 = require("fs");
32085
+ import_path76 = require("path");
31514
32086
  init_version();
31515
32087
  }
31516
32088
  });
@@ -31716,8 +32288,8 @@ var init_verify_memory = __esm({
31716
32288
  });
31717
32289
 
31718
32290
  // apps/cli/src/mcp-server-sdk.ts
31719
- var import_fs72 = require("fs");
31720
- var import_path74 = require("path");
32291
+ var import_fs75 = require("fs");
32292
+ var import_path77 = require("path");
31721
32293
  var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
31722
32294
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
31723
32295
  var import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
@@ -45544,8 +46116,8 @@ init_native_tasks();
45544
46116
 
45545
46117
  // apps/cli/src/handlers/dispatch.ts
45546
46118
  var import_crypto24 = require("crypto");
45547
- var import_fs62 = require("fs");
45548
- var import_path64 = require("path");
46119
+ var import_fs65 = require("fs");
46120
+ var import_path67 = require("path");
45549
46121
  init_src4();
45550
46122
  init_src3();
45551
46123
  init_mcp_context();
@@ -45572,7 +46144,11 @@ function persistRelayTasks() {
45572
46144
  agentId: task.agentId,
45573
46145
  task: task.task.slice(0, 5e3),
45574
46146
  startedAt: task.startedAt,
45575
- timeoutMs: task.timeoutMs
46147
+ timeoutMs: task.timeoutMs,
46148
+ // Persist the dispatched resolutionRoots so post-reconnect audit /
46149
+ // dashboard rendering can show the worktree binding even after the
46150
+ // websocket-bound runtime task is marked timed_out.
46151
+ ...task.resolutionRoots && task.resolutionRoots.length > 0 ? { resolutionRoots: [...task.resolutionRoots] } : {}
45576
46152
  });
45577
46153
  }
45578
46154
  wf(j(dir, RELAY_TASK_FILE), JSON.stringify({ tasks: records }));
@@ -45683,9 +46259,9 @@ function formatFalsifiedNote(block, verdicts) {
45683
46259
  }
45684
46260
  function writePremiseVerificationLog(projectRoot, taskId, result, opts) {
45685
46261
  try {
45686
- const logPath = (0, import_path64.join)(projectRoot, PREMISE_VERIFICATION_LOG);
46262
+ const logPath = (0, import_path67.join)(projectRoot, PREMISE_VERIFICATION_LOG);
45687
46263
  rotateIfNeeded2(logPath, MAX_PREMISE_VERIFICATION_BYTES);
45688
- (0, import_fs62.mkdirSync)((0, import_path64.join)(projectRoot, ".gossip"), { recursive: true });
46264
+ (0, import_fs65.mkdirSync)((0, import_path67.join)(projectRoot, ".gossip"), { recursive: true });
45689
46265
  const row = {
45690
46266
  ts: (/* @__PURE__ */ new Date()).toISOString(),
45691
46267
  consensus_id: null,
@@ -45699,7 +46275,7 @@ function writePremiseVerificationLog(projectRoot, taskId, result, opts) {
45699
46275
  skill_bound: opts.skill_bound
45700
46276
  };
45701
46277
  if (opts.schema_lint) row.schema_lint = opts.schema_lint;
45702
- (0, import_fs62.appendFileSync)(logPath, JSON.stringify(row) + "\n");
46278
+ (0, import_fs65.appendFileSync)(logPath, JSON.stringify(row) + "\n");
45703
46279
  } catch {
45704
46280
  }
45705
46281
  }
@@ -45811,7 +46387,7 @@ var AGENT_PROVIDER_MAP = {
45811
46387
  };
45812
46388
  function readQuotaState() {
45813
46389
  try {
45814
- const raw = (0, import_fs62.readFileSync)((0, import_path64.join)(process.cwd(), ".gossip", "quota-state.json"), "utf8");
46390
+ const raw = (0, import_fs65.readFileSync)((0, import_path67.join)(process.cwd(), ".gossip", "quota-state.json"), "utf8");
45815
46391
  return JSON.parse(raw);
45816
46392
  } catch {
45817
46393
  return {};
@@ -45835,7 +46411,7 @@ function reroutableAgent(agentId) {
45835
46411
  }
45836
46412
  return agentId;
45837
46413
  }
45838
- async function handleDispatchSingle(agent_id, task, write_mode, scope, timeout_ms, plan_id, step) {
46414
+ async function handleDispatchSingle(agent_id, task, write_mode, scope, timeout_ms, plan_id, step, resolutionRoots) {
45839
46415
  await ctx.boot();
45840
46416
  await ctx.syncWorkersViaKeychain();
45841
46417
  if (!/^[a-zA-Z0-9_-]+$/.test(agent_id)) {
@@ -45857,6 +46433,9 @@ async function handleDispatchSingle(agent_id, task, write_mode, scope, timeout_m
45857
46433
  options.planId = plan_id;
45858
46434
  options.step = step;
45859
46435
  }
46436
+ if (resolutionRoots && resolutionRoots.length > 0) {
46437
+ options.resolutionRoots = resolutionRoots;
46438
+ }
45860
46439
  const dispatchOptions = Object.keys(options).length > 0 ? options : void 0;
45861
46440
  const nativeConfig = ctx.nativeAgentConfigs.get(agent_id);
45862
46441
  if (nativeConfig) {
@@ -45878,7 +46457,12 @@ async function handleDispatchSingle(agent_id, task, write_mode, scope, timeout_m
45878
46457
  if (premiseResult.signals.length > 0) {
45879
46458
  emitConsensusSignals(process.cwd(), premiseResult.signals);
45880
46459
  }
45881
- ctx.nativeTaskMap.set(taskId, { agentId: agent_id, task, startedAt: Date.now(), timeoutMs, planId: plan_id, step, writeMode: write_mode, relayToken });
46460
+ let preDispatchSha = null;
46461
+ if (write_mode) {
46462
+ const { capturePreDispatchSha: capturePreDispatchSha2 } = (init_ref_allowlist_detection(), __toCommonJS(ref_allowlist_detection_exports));
46463
+ preDispatchSha = capturePreDispatchSha2();
46464
+ }
46465
+ ctx.nativeTaskMap.set(taskId, { agentId: agent_id, task, startedAt: Date.now(), timeoutMs, planId: plan_id, step, writeMode: write_mode, relayToken, preDispatchSha });
45882
46466
  spawnTimeoutWatcher(taskId, ctx.nativeTaskMap.get(taskId));
45883
46467
  persistNativeTaskMap();
45884
46468
  process.stderr.write(`[gossipcat] \u2192 dispatch \u2192 ${agent_id} (${nativeConfig.model}) [${taskId}]
@@ -45975,7 +46559,7 @@ ${agentPrompt}` }
45975
46559
  return { content: [{ type: "text", text: err.message }] };
45976
46560
  }
45977
46561
  }
45978
- async function handleDispatchParallel(taskDefs, consensus) {
46562
+ async function handleDispatchParallel(taskDefs, consensus, resolutionRoots) {
45979
46563
  await ctx.boot();
45980
46564
  await ctx.syncWorkersViaKeychain();
45981
46565
  for (const def of taskDefs) {
@@ -46000,11 +46584,21 @@ async function handleDispatchParallel(taskDefs, consensus) {
46000
46584
  const lines = [];
46001
46585
  if (relayTasks.length > 0) {
46002
46586
  const { taskIds, errors } = await ctx.mainAgent.dispatchParallel(
46003
- relayTasks.map((d) => ({
46004
- agentId: d.agent_id,
46005
- task: d.task,
46006
- options: d.write_mode ? { writeMode: d.write_mode, scope: d.scope } : void 0
46007
- })),
46587
+ relayTasks.map((d) => {
46588
+ const opts = {};
46589
+ if (d.write_mode) {
46590
+ opts.writeMode = d.write_mode;
46591
+ if (d.scope) opts.scope = d.scope;
46592
+ }
46593
+ if (resolutionRoots && resolutionRoots.length > 0) {
46594
+ opts.resolutionRoots = resolutionRoots;
46595
+ }
46596
+ return {
46597
+ agentId: d.agent_id,
46598
+ task: d.task,
46599
+ options: Object.keys(opts).length > 0 ? opts : void 0
46600
+ };
46601
+ }),
46008
46602
  consensus ? { consensus: true } : void 0
46009
46603
  );
46010
46604
  persistRelayTasks();
@@ -46197,12 +46791,13 @@ async function handleDispatchConsensus(taskDefs, _utility_task_id, dispatchResol
46197
46791
  const lines = [];
46198
46792
  const allTaskIds = [];
46199
46793
  if (relayTasks.length > 0) {
46794
+ const relayOptions = dispatchResolutionRoots && dispatchResolutionRoots.length > 0 ? { resolutionRoots: dispatchResolutionRoots } : void 0;
46200
46795
  const { taskIds, errors } = precomputedLenses ? await ctx.mainAgent.dispatchParallelWithLenses(
46201
- relayTasks.map((d) => ({ agentId: d.agent_id, task: d.task })),
46796
+ relayTasks.map((d) => ({ agentId: d.agent_id, task: d.task, options: relayOptions })),
46202
46797
  { consensus: true },
46203
46798
  precomputedLenses
46204
46799
  ) : await ctx.mainAgent.dispatchParallel(
46205
- relayTasks.map((d) => ({ agentId: d.agent_id, task: d.task })),
46800
+ relayTasks.map((d) => ({ agentId: d.agent_id, task: d.task, options: relayOptions })),
46206
46801
  { consensus: true }
46207
46802
  );
46208
46803
  persistRelayTasks();
@@ -46319,8 +46914,29 @@ ${p.prompt}` });
46319
46914
  init_mcp_context();
46320
46915
  init_relay_cross_review();
46321
46916
  init_native_tasks();
46917
+ init_lifecycle_tasks();
46322
46918
  init_src3();
46323
46919
  init_src4();
46920
+ function scheduleSkillRunner(c, mainAgent) {
46921
+ if (!c.skillEngine) return;
46922
+ const skillEngine = c.skillEngine;
46923
+ const registryGet = (id) => mainAgent.getAgentConfig(id);
46924
+ const projectRoot = process.cwd();
46925
+ const runRunner = async () => {
46926
+ try {
46927
+ const { runCheckEffectivenessForAllSkills: runCheckEffectivenessForAllSkills2 } = await Promise.resolve().then(() => (init_check_effectiveness_runner(), check_effectiveness_runner_exports));
46928
+ await runCheckEffectivenessForAllSkills2({
46929
+ skillEngine,
46930
+ registryGet,
46931
+ projectRoot
46932
+ });
46933
+ } catch (e) {
46934
+ process.stderr.write(`[gossipcat] checkEffectiveness post-collect run failed: ${e.message}
46935
+ `);
46936
+ }
46937
+ };
46938
+ trackLifecycleTask(Promise.resolve().then(runRunner));
46939
+ }
46324
46940
  var _autoResolverErrors = /* @__PURE__ */ new Set();
46325
46941
  var _autoResolverLockWarned = false;
46326
46942
  var CONSENSUS_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{8}$/;
@@ -46509,6 +47125,7 @@ ${t.skillWarnings.map((w) => ` - ${w}`).join("\n")}`;
46509
47125
  });
46510
47126
  let consensusReport = void 0;
46511
47127
  let provisionalSignalCount = 0;
47128
+ let outerEffectiveRoots = resolutionRoots ?? [];
46512
47129
  const CONSENSUS_TIMEOUT_MS = 18e5;
46513
47130
  if (consensus && allResults.filter((r) => r.status === "completed").length >= 2) {
46514
47131
  const nativeAgentIds = /* @__PURE__ */ new Set();
@@ -46559,6 +47176,7 @@ ${t.skillWarnings.map((w) => ` - ${w}`).join("\n")}`;
46559
47176
  process.stderr.write(`[consensus] auto-discovery failed: ${err.message}
46560
47177
  `);
46561
47178
  }
47179
+ outerEffectiveRoots = effectiveRoots;
46562
47180
  const verifierFs = new FileTools(new Sandbox(process.cwd()));
46563
47181
  const verifierGit = new GitTools(process.cwd());
46564
47182
  const verifierMemory = new MemorySearcher(process.cwd());
@@ -46831,6 +47449,7 @@ ${np.user}
46831
47449
  ctx.nativeTaskMap.delete(id);
46832
47450
  }
46833
47451
  }
47452
+ scheduleSkillRunner(ctx, ctx.mainAgent);
46834
47453
  return { content: [{ type: "text", text: partialOutput }] };
46835
47454
  }
46836
47455
  }
@@ -46846,9 +47465,10 @@ ${np.user}
46846
47465
  try {
46847
47466
  const { writeFileSync: wfr, mkdirSync: mdr } = require("fs");
46848
47467
  const { join: jr } = require("path");
47468
+ const { randomBytes: rb } = require("crypto");
46849
47469
  const reportsDir = jr(process.cwd(), ".gossip", "consensus-reports");
46850
47470
  mdr(reportsDir, { recursive: true });
46851
- const reportId = consensusReport.signals?.[0]?.consensusId || Date.now().toString();
47471
+ const reportId = consensusReport.signals?.[0]?.consensusId || `${rb(4).toString("hex")}-${rb(4).toString("hex")}`;
46852
47472
  const reportPath = jr(reportsDir, `${reportId}.json`);
46853
47473
  const topic = allResults?.find((r) => r.task)?.task?.slice(0, 500) || "";
46854
47474
  wfr(reportPath, JSON.stringify({
@@ -46857,6 +47477,13 @@ ${np.user}
46857
47477
  topic,
46858
47478
  agentCount: consensusReport.agentCount,
46859
47479
  rounds: consensusReport.rounds,
47480
+ // Persist resolutionRoots so the transport-failure detector
47481
+ // (Path 2, spec docs/specs/2026-04-29-relay-worker-resolution-roots.md)
47482
+ // can look up "did this round dispatch with resolutionRoots?" from a
47483
+ // bare `consensus_id` derived from a `finding_id`. Empty array when
47484
+ // none was passed — preserving the explicit "no roots" signal in the
47485
+ // record.
47486
+ resolutionRoots: outerEffectiveRoots.length > 0 ? [...outerEffectiveRoots] : [],
46860
47487
  confirmed: consensusReport.confirmed || [],
46861
47488
  disputed: consensusReport.disputed || [],
46862
47489
  unverified: consensusReport.unverified || [],
@@ -46870,7 +47497,12 @@ ${np.user}
46870
47497
  // round-trip through the JSON payload or the feature is invisible.
46871
47498
  ...consensusReport.authorDiagnostics ? { authorDiagnostics: consensusReport.authorDiagnostics } : {}
46872
47499
  }, null, 2));
46873
- } catch {
47500
+ } catch (e) {
47501
+ const reportIdForLog = consensusReport.signals?.[0]?.consensusId || "<unknown>";
47502
+ process.stderr.write(
47503
+ `[gossipcat] consensus-report write failed for ${reportIdForLog}: ${e.message ?? e}
47504
+ `
47505
+ );
46874
47506
  }
46875
47507
  }
46876
47508
  if (consensusReport) {
@@ -47132,25 +47764,7 @@ ${gaps.map((g) => ` - ${g.agentId} needs "${g.category}" (score: ${g.score.toFi
47132
47764
  }
47133
47765
  } catch {
47134
47766
  }
47135
- if (ctx.skillEngine) {
47136
- const skillEngine = ctx.skillEngine;
47137
- const mainAgent = ctx.mainAgent;
47138
- const registryGet = (id) => mainAgent.getAgentConfig(id);
47139
- const projectRoot = process.cwd();
47140
- setImmediate(async () => {
47141
- try {
47142
- const { runCheckEffectivenessForAllSkills: runCheckEffectivenessForAllSkills2 } = await Promise.resolve().then(() => (init_check_effectiveness_runner(), check_effectiveness_runner_exports));
47143
- await runCheckEffectivenessForAllSkills2({
47144
- skillEngine,
47145
- registryGet,
47146
- projectRoot
47147
- });
47148
- } catch (e) {
47149
- process.stderr.write(`[gossipcat] checkEffectiveness post-collect run failed: ${e.message}
47150
- `);
47151
- }
47152
- });
47153
- }
47767
+ scheduleSkillRunner(ctx, ctx.mainAgent);
47154
47768
  try {
47155
47769
  const taskCount = ctx.mainAgent.getSessionGossip().length;
47156
47770
  const consensusCount = ctx.mainAgent.getSessionConsensusHistory().length;
@@ -47168,19 +47782,19 @@ REQUIRED_BEFORE_END: gossip_session_save() \u2014 ${taskCount} tasks, ${consensu
47168
47782
  init_relay_cross_review();
47169
47783
 
47170
47784
  // apps/cli/src/stickyPort.ts
47171
- var import_fs66 = require("fs");
47172
- var import_path68 = require("path");
47785
+ var import_fs69 = require("fs");
47786
+ var import_path71 = require("path");
47173
47787
  var import_net = require("net");
47174
- var RELAY_STICKY_FILE = (0, import_path68.join)(".gossip", "relay.port");
47175
- var HTTP_MCP_STICKY_FILE = (0, import_path68.join)(".gossip", "http-mcp.port");
47788
+ var RELAY_STICKY_FILE = (0, import_path71.join)(".gossip", "relay.port");
47789
+ var HTTP_MCP_STICKY_FILE = (0, import_path71.join)(".gossip", "http-mcp.port");
47176
47790
  function stickyPath(filename) {
47177
- return (0, import_path68.join)(process.cwd(), filename);
47791
+ return (0, import_path71.join)(process.cwd(), filename);
47178
47792
  }
47179
47793
  function readStickyPort(filename) {
47180
47794
  const p = stickyPath(filename);
47181
- if (!(0, import_fs66.existsSync)(p)) return null;
47795
+ if (!(0, import_fs69.existsSync)(p)) return null;
47182
47796
  try {
47183
- const raw = (0, import_fs66.readFileSync)(p, "utf-8").trim();
47797
+ const raw = (0, import_fs69.readFileSync)(p, "utf-8").trim();
47184
47798
  const n = parseInt(raw, 10);
47185
47799
  if (!Number.isFinite(n) || n < 1 || n > 65535) return null;
47186
47800
  return n;
@@ -47192,8 +47806,8 @@ function writeStickyPort(filename, port) {
47192
47806
  if (!Number.isFinite(port) || port < 1 || port > 65535) return;
47193
47807
  const p = stickyPath(filename);
47194
47808
  try {
47195
- (0, import_fs66.mkdirSync)((0, import_path68.dirname)(p), { recursive: true });
47196
- (0, import_fs66.writeFileSync)(p, String(port), "utf-8");
47809
+ (0, import_fs69.mkdirSync)((0, import_path71.dirname)(p), { recursive: true });
47810
+ (0, import_fs69.writeFileSync)(p, String(port), "utf-8");
47197
47811
  } catch {
47198
47812
  }
47199
47813
  }
@@ -47257,12 +47871,12 @@ function buildDashboardAdvisory(input) {
47257
47871
  }
47258
47872
 
47259
47873
  // apps/cli/src/native-agent-cache.ts
47260
- var import_fs67 = require("fs");
47261
- var import_path69 = require("path");
47874
+ var import_fs70 = require("fs");
47875
+ var import_path72 = require("path");
47262
47876
  var realFs = {
47263
- existsSync: import_fs67.existsSync,
47264
- readFileSync: import_fs67.readFileSync,
47265
- statSync: (p) => ({ mtimeMs: (0, import_fs67.statSync)(p).mtimeMs })
47877
+ existsSync: import_fs70.existsSync,
47878
+ readFileSync: import_fs70.readFileSync,
47879
+ statSync: (p) => ({ mtimeMs: (0, import_fs70.statSync)(p).mtimeMs })
47266
47880
  };
47267
47881
  function modelTierFromId(modelId) {
47268
47882
  if (modelId.includes("opus")) return "opus";
@@ -47270,8 +47884,8 @@ function modelTierFromId(modelId) {
47270
47884
  return "sonnet";
47271
47885
  }
47272
47886
  function refreshNativeAgentFromDisk(ac, cache2, projectRoot, fs5 = realFs) {
47273
- const claudeAgentPath = (0, import_path69.join)(projectRoot, ".claude", "agents", `${ac.id}.md`);
47274
- const instrPath = (0, import_path69.join)(projectRoot, ".gossip", "agents", ac.id, "instructions.md");
47887
+ const claudeAgentPath = (0, import_path72.join)(projectRoot, ".claude", "agents", `${ac.id}.md`);
47888
+ const instrPath = (0, import_path72.join)(projectRoot, ".gossip", "agents", ac.id, "instructions.md");
47275
47889
  const prev = cache2.get(ac.id);
47276
47890
  let sourcePath = null;
47277
47891
  let stripFrontmatter = false;
@@ -47330,16 +47944,16 @@ function refreshNativeAgentFromDisk(ac, cache2, projectRoot, fs5 = realFs) {
47330
47944
  }
47331
47945
 
47332
47946
  // apps/cli/src/rules-content.ts
47333
- var import_fs68 = require("fs");
47334
- var import_path70 = require("path");
47947
+ var import_fs71 = require("fs");
47948
+ var import_path73 = require("path");
47335
47949
  var AGENT_LIST_PLACEHOLDER = "{{AGENT_LIST}}";
47336
47950
  function resolveRulesPath() {
47337
47951
  const candidates = [
47338
- (0, import_path70.join)(process.cwd(), "docs", "RULES.md"),
47339
- (0, import_path70.join)(__dirname, "..", "docs", "RULES.md"),
47340
- (0, import_path70.join)(__dirname, "docs", "RULES.md")
47952
+ (0, import_path73.join)(process.cwd(), "docs", "RULES.md"),
47953
+ (0, import_path73.join)(__dirname, "..", "docs", "RULES.md"),
47954
+ (0, import_path73.join)(__dirname, "docs", "RULES.md")
47341
47955
  ];
47342
- return candidates.find((p) => (0, import_fs68.existsSync)(p)) ?? null;
47956
+ return candidates.find((p) => (0, import_fs71.existsSync)(p)) ?? null;
47343
47957
  }
47344
47958
  function generateRulesContent(agentList) {
47345
47959
  const rulesPath = resolveRulesPath();
@@ -47348,7 +47962,7 @@ function generateRulesContent(agentList) {
47348
47962
  `[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/.`
47349
47963
  );
47350
47964
  }
47351
- const template = (0, import_fs68.readFileSync)(rulesPath, "utf-8");
47965
+ const template = (0, import_fs71.readFileSync)(rulesPath, "utf-8");
47352
47966
  return template.split(AGENT_LIST_PLACEHOLDER).join(agentList);
47353
47967
  }
47354
47968
 
@@ -47364,6 +47978,23 @@ function formatDropReceipt(drops) {
47364
47978
  ${lines.join("\n")}`;
47365
47979
  }
47366
47980
 
47981
+ // apps/cli/src/shutdown.ts
47982
+ async function shutdownOnSignal(signal, deps) {
47983
+ try {
47984
+ await deps.drainLifecycleTasks();
47985
+ } catch {
47986
+ }
47987
+ deps.eviction.stop();
47988
+ process.stderr.write(`[relay] shutdown reason=${signal} pid=${deps.pid}
47989
+ `);
47990
+ try {
47991
+ await deps.relayStop();
47992
+ } catch {
47993
+ }
47994
+ deps.cleanupPid();
47995
+ deps.exit(0);
47996
+ }
47997
+
47367
47998
  // apps/cli/src/mcp-server-sdk.ts
47368
47999
  var import_os5 = require("os");
47369
48000
 
@@ -47419,17 +48050,17 @@ function filterWatchEvents(rawJsonl, opts) {
47419
48050
  }
47420
48051
 
47421
48052
  // apps/cli/src/mcp-server-sdk.ts
47422
- var gossipDir = (0, import_path74.join)(process.cwd(), ".gossip");
48053
+ var gossipDir = (0, import_path77.join)(process.cwd(), ".gossip");
47423
48054
  try {
47424
- (0, import_fs72.mkdirSync)(gossipDir, { recursive: true });
48055
+ (0, import_fs75.mkdirSync)(gossipDir, { recursive: true });
47425
48056
  } catch {
47426
48057
  }
47427
- var logStream = (0, import_fs72.createWriteStream)((0, import_path74.join)(gossipDir, "mcp.log"), { flags: "a" });
48058
+ var logStream = (0, import_fs75.createWriteStream)((0, import_path77.join)(gossipDir, "mcp.log"), { flags: "a" });
47428
48059
  process.stderr.write = ((chunk, ...args) => {
47429
48060
  return logStream.write(chunk, ...args);
47430
48061
  });
47431
48062
  function memoryDirForProject(cwd) {
47432
- return (0, import_path74.join)((0, import_os5.homedir)(), ".claude", "projects", cwd.replaceAll("/", "-"), "memory");
48063
+ return (0, import_path77.join)((0, import_os5.homedir)(), ".claude", "projects", cwd.replaceAll("/", "-"), "memory");
47433
48064
  }
47434
48065
  function detectEnvironment() {
47435
48066
  if (process.env.CLAUDECODE === "1" || process.env.CLAUDE_CODE_ENTRYPOINT) {
@@ -47453,9 +48084,9 @@ var planExecutionDepth = 0;
47453
48084
  var _pendingSessionData = /* @__PURE__ */ new Map();
47454
48085
  async function writeArtifactsInOrder(a) {
47455
48086
  const { writeFileSync: wf, mkdirSync: md } = await import("fs");
47456
- const { dirname: dirname16 } = await import("path");
48087
+ const { dirname: dirname17 } = await import("path");
47457
48088
  try {
47458
- md(dirname16(a.nextSessionPath), { recursive: true });
48089
+ md(dirname17(a.nextSessionPath), { recursive: true });
47459
48090
  wf(a.nextSessionPath, a.nextSessionContent);
47460
48091
  } catch (err) {
47461
48092
  process.stderr.write(`[gossipcat] next-session.md write FAILED: ${err.message}
@@ -47463,7 +48094,7 @@ async function writeArtifactsInOrder(a) {
47463
48094
  throw err;
47464
48095
  }
47465
48096
  try {
47466
- md(dirname16(a.knowledgePath), { recursive: true });
48097
+ md(dirname17(a.knowledgePath), { recursive: true });
47467
48098
  wf(a.knowledgePath, a.knowledgeContent);
47468
48099
  } catch (err) {
47469
48100
  process.stderr.write(`[gossipcat] cognitive knowledge write failed: ${err.message}
@@ -47484,14 +48115,14 @@ var _pendingPlanData = /* @__PURE__ */ new Map();
47484
48115
  var _utilityGuardSnapshots = /* @__PURE__ */ new Map();
47485
48116
  var _modules = null;
47486
48117
  function lookupFindingSeverity(findingId, projectRoot) {
47487
- const { existsSync: existsSync61, readdirSync: readdirSync18, readFileSync: readFileSync59 } = require("fs");
47488
- const { join: join70 } = require("path");
47489
- const reportsDir = join70(projectRoot, ".gossip", "consensus-reports");
47490
- if (!existsSync61(reportsDir)) return null;
48118
+ const { existsSync: existsSync64, readdirSync: readdirSync18, readFileSync: readFileSync61 } = require("fs");
48119
+ const { join: join73 } = require("path");
48120
+ const reportsDir = join73(projectRoot, ".gossip", "consensus-reports");
48121
+ if (!existsSync64(reportsDir)) return null;
47491
48122
  try {
47492
48123
  const files = readdirSync18(reportsDir).filter((f) => f.endsWith(".json"));
47493
48124
  for (const file2 of files) {
47494
- const report = JSON.parse(readFileSync59(join70(reportsDir, file2), "utf-8"));
48125
+ const report = JSON.parse(readFileSync61(join73(reportsDir, file2), "utf-8"));
47495
48126
  for (const bucket of ["confirmed", "disputed", "unverified", "unique"]) {
47496
48127
  for (const finding of report[bucket] || []) {
47497
48128
  if (finding.id === findingId && finding.severity) {
@@ -47569,7 +48200,7 @@ async function doBoot() {
47569
48200
  const agentConfigs = m.configToAgentConfigs(config2);
47570
48201
  ctx.keychain = new m.Keychain();
47571
48202
  const { existsSync: pidExists, readFileSync: readPid, writeFileSync: writePid, unlinkSync: delPid } = require("fs");
47572
- const pidFile = (0, import_path74.join)(process.cwd(), ".gossip", "relay.pid");
48203
+ const pidFile = (0, import_path77.join)(process.cwd(), ".gossip", "relay.pid");
47573
48204
  if (pidExists(pidFile)) {
47574
48205
  const oldPid = parseInt(readPid(pidFile, "utf-8").trim(), 10);
47575
48206
  if (!isNaN(oldPid) && oldPid !== process.pid) {
@@ -47613,31 +48244,25 @@ async function doBoot() {
47613
48244
  process.once("exit", cleanupPid);
47614
48245
  let shuttingDown = false;
47615
48246
  const eviction = scheduleNativeTaskEviction();
48247
+ const { installLifecycleDrainHandlers: installLifecycleDrainHandlers2, drainLifecycleTasks: drainLifecycleTasks2 } = await Promise.resolve().then(() => (init_lifecycle_tasks(), lifecycle_tasks_exports));
48248
+ installLifecycleDrainHandlers2();
48249
+ const shutdownDeps = {
48250
+ eviction,
48251
+ relayStop: () => ctx.relay.stop(),
48252
+ cleanupPid,
48253
+ drainLifecycleTasks: () => drainLifecycleTasks2(),
48254
+ exit: (code) => process.exit(code),
48255
+ pid: process.pid
48256
+ };
47616
48257
  process.once("SIGTERM", async () => {
47617
48258
  if (shuttingDown) return;
47618
48259
  shuttingDown = true;
47619
- eviction.stop();
47620
- process.stderr.write(`[relay] shutdown reason=SIGTERM pid=${process.pid}
47621
- `);
47622
- try {
47623
- await ctx.relay.stop();
47624
- } catch {
47625
- }
47626
- cleanupPid();
47627
- process.exit(0);
48260
+ await shutdownOnSignal("SIGTERM", shutdownDeps);
47628
48261
  });
47629
48262
  process.once("SIGINT", async () => {
47630
48263
  if (shuttingDown) return;
47631
48264
  shuttingDown = true;
47632
- eviction.stop();
47633
- process.stderr.write(`[relay] shutdown reason=SIGINT pid=${process.pid}
47634
- `);
47635
- try {
47636
- await ctx.relay.stop();
47637
- } catch {
47638
- }
47639
- cleanupPid();
47640
- process.exit(0);
48265
+ await shutdownOnSignal("SIGINT", shutdownDeps);
47641
48266
  });
47642
48267
  if (ctx.relay.dashboardUrl) {
47643
48268
  process.stderr.write(`[gossipcat] \u{1F310} Dashboard: ${ctx.relay.dashboardUrl} (key: ${ctx.relay.dashboardKey})
@@ -47685,10 +48310,10 @@ async function doBoot() {
47685
48310
  }
47686
48311
  const key = await ctx.keychain.getKey(ac.provider);
47687
48312
  const llm = m.createProvider(ac.provider, ac.model, key ?? void 0, void 0, ac.base_url);
47688
- const { existsSync: existsSync61, readFileSync: readFileSync59 } = require("fs");
47689
- const { join: join70 } = require("path");
47690
- const instructionsPath = join70(process.cwd(), ".gossip", "agents", ac.id, "instructions.md");
47691
- const baseInstructions = existsSync61(instructionsPath) ? readFileSync59(instructionsPath, "utf-8") : "";
48313
+ const { existsSync: existsSync64, readFileSync: readFileSync61 } = require("fs");
48314
+ const { join: join73 } = require("path");
48315
+ const instructionsPath = join73(process.cwd(), ".gossip", "agents", ac.id, "instructions.md");
48316
+ const baseInstructions = existsSync64(instructionsPath) ? readFileSync61(instructionsPath, "utf-8") : "";
47692
48317
  const identity = ctx.identityRegistry.get(ac.id);
47693
48318
  const identityBlock = identity ? m.formatIdentityBlock(identity) + "\n" : "";
47694
48319
  const instructions = (identityBlock + baseInstructions).trim() || void 0;
@@ -48360,13 +48985,26 @@ server.tool(
48360
48985
  if (!agent_id || !task) {
48361
48986
  return { content: [{ type: "text", text: 'Error: mode:"single" requires agent_id and task.' }] };
48362
48987
  }
48363
- return handleDispatchSingle(agent_id, task, write_mode, scope, timeout_ms, plan_id, step);
48988
+ return handleDispatchSingle(
48989
+ agent_id,
48990
+ task,
48991
+ write_mode,
48992
+ scope,
48993
+ timeout_ms,
48994
+ plan_id,
48995
+ step,
48996
+ validatedDispatchRoots.length > 0 ? validatedDispatchRoots : void 0
48997
+ );
48364
48998
  }
48365
48999
  if (mode === "parallel") {
48366
49000
  if (!tasks || tasks.length === 0) {
48367
49001
  return { content: [{ type: "text", text: 'Error: mode:"parallel" requires a non-empty tasks array.' }] };
48368
49002
  }
48369
- return handleDispatchParallel(tasks, false);
49003
+ return handleDispatchParallel(
49004
+ tasks,
49005
+ false,
49006
+ validatedDispatchRoots.length > 0 ? validatedDispatchRoots : void 0
49007
+ );
48370
49008
  }
48371
49009
  if (mode === "consensus") {
48372
49010
  if (!tasks || tasks.length === 0) {
@@ -48484,9 +49122,26 @@ server.tool(
48484
49122
  lines.push(` HTTP MCP: :${ctx.httpMcpPort}/mcp${ctx.httpMcpPortSource === "sticky" ? " (sticky)" : ""}`);
48485
49123
  }
48486
49124
  try {
48487
- const { readFileSync: readFileSync59 } = await import("fs");
48488
- const quotaPath = (0, import_path74.join)(process.cwd(), ".gossip", "quota-state.json");
48489
- const quotaRaw = readFileSync59(quotaPath, "utf8");
49125
+ const { readFileSync: rfHealth } = await import("fs");
49126
+ const healthPath = (0, import_path77.join)(process.cwd(), ".gossip", "skill-runner-health.json");
49127
+ const raw = rfHealth(healthPath, "utf8");
49128
+ const h = JSON.parse(raw);
49129
+ const lastMs = h.last_run_at ? new Date(h.last_run_at).getTime() : NaN;
49130
+ if (Number.isFinite(lastMs)) {
49131
+ const ago = Date.now() - lastMs;
49132
+ const sec = Math.round(ago / 1e3);
49133
+ const rel = sec < 60 ? `${sec}s` : sec < 3600 ? `${Math.round(sec / 60)}m` : `${Math.round(sec / 3600)}h`;
49134
+ const t = h.transitions || {};
49135
+ const totalT = (t.passed ?? 0) + (t.failed ?? 0) + (t.flagged_for_manual_review ?? 0) + (t.inconclusive ?? 0) + (t.pending ?? 0);
49136
+ lines.push(` Skill graduation: last run ${rel} ago (${h.skills_evaluated ?? 0} skills, ${totalT} transitions)`);
49137
+ }
49138
+ } catch {
49139
+ lines.push(" Skill graduation: never run since session start (expected after first gossip_collect)");
49140
+ }
49141
+ try {
49142
+ const { readFileSync: readFileSync61 } = await import("fs");
49143
+ const quotaPath = (0, import_path77.join)(process.cwd(), ".gossip", "quota-state.json");
49144
+ const quotaRaw = readFileSync61(quotaPath, "utf8");
48490
49145
  const quotaState = JSON.parse(quotaRaw);
48491
49146
  for (const [provider, state] of Object.entries(quotaState)) {
48492
49147
  const now = Date.now();
@@ -48500,17 +49155,17 @@ server.tool(
48500
49155
  } catch {
48501
49156
  }
48502
49157
  try {
48503
- const { readFileSync: readFileSync59, readdirSync: readdirSync18, statSync: statSync23 } = await import("fs");
48504
- const reportsDir = (0, import_path74.join)(process.cwd(), ".gossip", "consensus-reports");
48505
- const perfPath = (0, import_path74.join)(process.cwd(), ".gossip", "agent-performance.jsonl");
49158
+ const { readFileSync: readFileSync61, readdirSync: readdirSync18, statSync: statSync24 } = await import("fs");
49159
+ const reportsDir = (0, import_path77.join)(process.cwd(), ".gossip", "consensus-reports");
49160
+ const perfPath = (0, import_path77.join)(process.cwd(), ".gossip", "agent-performance.jsonl");
48506
49161
  const WINDOW_MS2 = 24 * 60 * 60 * 1e3;
48507
49162
  const now = Date.now();
48508
49163
  const recentReports = [];
48509
49164
  try {
48510
49165
  for (const fname of readdirSync18(reportsDir)) {
48511
49166
  if (!fname.endsWith(".json")) continue;
48512
- const fpath = (0, import_path74.join)(reportsDir, fname);
48513
- const st = statSync23(fpath);
49167
+ const fpath = (0, import_path77.join)(reportsDir, fname);
49168
+ const st = statSync24(fpath);
48514
49169
  if (now - st.mtimeMs > WINDOW_MS2) continue;
48515
49170
  recentReports.push({ id: fname.replace(/\.json$/, ""), mtimeMs: st.mtimeMs });
48516
49171
  }
@@ -48519,7 +49174,7 @@ server.tool(
48519
49174
  if (recentReports.length > 0) {
48520
49175
  const covered = /* @__PURE__ */ new Set();
48521
49176
  try {
48522
- const perfRaw = readFileSync59(perfPath, "utf8");
49177
+ const perfRaw = readFileSync61(perfPath, "utf8");
48523
49178
  for (const line of perfRaw.split("\n")) {
48524
49179
  if (!line) continue;
48525
49180
  try {
@@ -48869,8 +49524,8 @@ server.tool(
48869
49524
  }
48870
49525
  return { content: [{ type: "text", text: results.join("\n") }] };
48871
49526
  }
48872
- const { writeFileSync: writeFileSync25, mkdirSync: mkdirSync30, existsSync: existsSync61 } = require("fs");
48873
- const { join: join70 } = require("path");
49527
+ const { writeFileSync: writeFileSync26, mkdirSync: mkdirSync33, existsSync: existsSync64 } = require("fs");
49528
+ const { join: join73 } = require("path");
48874
49529
  const root = process.cwd();
48875
49530
  const CLAUDE_MODEL_MAP2 = {
48876
49531
  opus: { provider: "anthropic", model: "claude-opus-4-6" },
@@ -48884,8 +49539,8 @@ server.tool(
48884
49539
  let existingAgents = {};
48885
49540
  if (mode === "merge") {
48886
49541
  try {
48887
- const { readFileSync: readFileSync59 } = require("fs");
48888
- const existing = JSON.parse(readFileSync59(join70(root, ".gossip", "config.json"), "utf-8"));
49542
+ const { readFileSync: readFileSync61 } = require("fs");
49543
+ const existing = JSON.parse(readFileSync61(join73(root, ".gossip", "config.json"), "utf-8"));
48889
49544
  existingAgents = existing.agents || {};
48890
49545
  } catch {
48891
49546
  }
@@ -48916,9 +49571,9 @@ server.tool(
48916
49571
  "",
48917
49572
  body
48918
49573
  ].join("\n");
48919
- const agentsDir = join70(root, ".claude", "agents");
48920
- mkdirSync30(agentsDir, { recursive: true });
48921
- writeFileSync25(join70(agentsDir, `${agent.id}.md`), md, "utf-8");
49574
+ const agentsDir = join73(root, ".claude", "agents");
49575
+ mkdirSync33(agentsDir, { recursive: true });
49576
+ writeFileSync26(join73(agentsDir, `${agent.id}.md`), md, "utf-8");
48922
49577
  nativeCreated.push(agent.id);
48923
49578
  configAgents[agent.id] = {
48924
49579
  provider: mapped.provider,
@@ -48936,8 +49591,8 @@ server.tool(
48936
49591
  errors.push(`${agent.id}: custom agent requires "custom_model" field`);
48937
49592
  continue;
48938
49593
  }
48939
- const nativeFile = join70(root, ".claude", "agents", `${agent.id}.md`);
48940
- const wasNative = existingAgents[agent.id]?.native || existsSync61(nativeFile);
49594
+ const nativeFile = join73(root, ".claude", "agents", `${agent.id}.md`);
49595
+ const wasNative = existingAgents[agent.id]?.native || existsSync64(nativeFile);
48941
49596
  if (wasNative) {
48942
49597
  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.`);
48943
49598
  continue;
@@ -48951,9 +49606,9 @@ server.tool(
48951
49606
  };
48952
49607
  customCreated.push(agent.id);
48953
49608
  if (agent.instructions) {
48954
- const instrDir = join70(root, ".gossip", "agents", agent.id);
48955
- mkdirSync30(instrDir, { recursive: true });
48956
- writeFileSync25(join70(instrDir, "instructions.md"), agent.instructions, "utf-8");
49609
+ const instrDir = join73(root, ".gossip", "agents", agent.id);
49610
+ mkdirSync33(instrDir, { recursive: true });
49611
+ writeFileSync26(join73(instrDir, "instructions.md"), agent.instructions, "utf-8");
48957
49612
  }
48958
49613
  }
48959
49614
  }
@@ -48967,8 +49622,8 @@ server.tool(
48967
49622
  } catch (err) {
48968
49623
  return { content: [{ type: "text", text: `Invalid config: ${err.message}` }] };
48969
49624
  }
48970
- mkdirSync30(join70(root, ".gossip"), { recursive: true });
48971
- writeFileSync25(join70(root, ".gossip", "config.json"), JSON.stringify(config2, null, 2));
49625
+ mkdirSync33(join73(root, ".gossip"), { recursive: true });
49626
+ writeFileSync26(join73(root, ".gossip", "config.json"), JSON.stringify(config2, null, 2));
48972
49627
  let hookSummary = "";
48973
49628
  try {
48974
49629
  const { installWorktreeSandboxHook: installWorktreeSandboxHook2, writeOrchestratorRoleMarker: writeOrchestratorRoleMarker2 } = (init_src4(), __toCommonJS(src_exports3));
@@ -49007,10 +49662,10 @@ server.tool(
49007
49662
  }
49008
49663
  }
49009
49664
  const agentList = Object.entries(config2.agents).map(([id, a]) => `- ${id}: ${a.provider}/${a.model} (${a.preset || "custom"})${a.native ? " \u2014 native" : ""}`).join("\n");
49010
- const rulesDir = join70(root, env.rulesDir);
49011
- const rulesFile = join70(root, env.rulesFile);
49012
- mkdirSync30(rulesDir, { recursive: true });
49013
- writeFileSync25(rulesFile, generateRulesContent(agentList));
49665
+ const rulesDir = join73(root, env.rulesDir);
49666
+ const rulesFile = join73(root, env.rulesFile);
49667
+ mkdirSync33(rulesDir, { recursive: true });
49668
+ writeFileSync26(rulesFile, generateRulesContent(agentList));
49014
49669
  const lines = [`Host: ${env.host}`, ""];
49015
49670
  if (nativeCreated.length > 0) {
49016
49671
  lines.push(`Native agents created (${nativeCreated.length}):`);
@@ -49297,11 +49952,11 @@ The original signal remains in the audit log but will be excluded from scoring.`
49297
49952
  const fid = finding_id.trim();
49298
49953
  const cwd = process.cwd();
49299
49954
  const findingsPath = require("path").join(cwd, ".gossip", "implementation-findings.jsonl");
49300
- const { readFileSync: readFileSync59, existsSync: existsSync61, writeFileSync: writeFileSync25, renameSync: renameSync8 } = require("fs");
49301
- if (!existsSync61(findingsPath)) {
49955
+ const { readFileSync: readFileSync61, existsSync: existsSync64, writeFileSync: writeFileSync26, renameSync: renameSync9 } = require("fs");
49956
+ if (!existsSync64(findingsPath)) {
49302
49957
  return { content: [{ type: "text", text: `No implementation-findings.jsonl found at ${findingsPath}` }] };
49303
49958
  }
49304
- const lines = readFileSync59(findingsPath, "utf-8").split("\n");
49959
+ const lines = readFileSync61(findingsPath, "utf-8").split("\n");
49305
49960
  let matched = false;
49306
49961
  let alreadyTarget = false;
49307
49962
  let matchedEntry = null;
@@ -49357,8 +50012,8 @@ The original signal remains in the audit log but will be excluded from scoring.`
49357
50012
  }
49358
50013
  try {
49359
50014
  const tmp = findingsPath + ".tmp." + Date.now();
49360
- writeFileSync25(tmp, newLines.join("\n"));
49361
- renameSync8(tmp, findingsPath);
50015
+ writeFileSync26(tmp, newLines.join("\n"));
50016
+ renameSync9(tmp, findingsPath);
49362
50017
  } catch (err) {
49363
50018
  return { content: [{ type: "text", text: `Failed to write findings: ${err.message}` }] };
49364
50019
  }
@@ -49389,19 +50044,19 @@ The original signal remains in the audit log but will be excluded from scoring.`
49389
50044
  return { content: [{ type: "text", text: "Error: consensus_id is required for bulk_from_consensus." }] };
49390
50045
  }
49391
50046
  try {
49392
- const { readFileSync: readFileSync59 } = await import("fs");
49393
- const { join: join70 } = await import("path");
49394
- const reportPath = join70(process.cwd(), ".gossip", "consensus-reports", `${consensus_id}.json`);
50047
+ const { readFileSync: readFileSync61 } = await import("fs");
50048
+ const { join: join73 } = await import("path");
50049
+ const reportPath = join73(process.cwd(), ".gossip", "consensus-reports", `${consensus_id}.json`);
49395
50050
  let report;
49396
50051
  try {
49397
- report = JSON.parse(readFileSync59(reportPath, "utf-8"));
50052
+ report = JSON.parse(readFileSync61(reportPath, "utf-8"));
49398
50053
  } catch {
49399
50054
  return { content: [{ type: "text", text: `Error: consensus report not found: ${consensus_id}` }] };
49400
50055
  }
49401
50056
  const existingFindingIds = /* @__PURE__ */ new Set();
49402
50057
  try {
49403
- const perfPath = join70(process.cwd(), ".gossip", "agent-performance.jsonl");
49404
- const lines = readFileSync59(perfPath, "utf-8").split("\n").filter(Boolean);
50058
+ const perfPath = join73(process.cwd(), ".gossip", "agent-performance.jsonl");
50059
+ const lines = readFileSync61(perfPath, "utf-8").split("\n").filter(Boolean);
49405
50060
  for (const line of lines) {
49406
50061
  try {
49407
50062
  const rec = JSON.parse(line);
@@ -49569,6 +50224,41 @@ Uncategorized finding_ids: ${uncategorized.join(", ")}`;
49569
50224
  timestamp: ts2
49570
50225
  };
49571
50226
  });
50227
+ try {
50228
+ const { maybeRewriteHallucinationToTransportFailure: maybeRewriteHallucinationToTransportFailure2 } = await Promise.resolve().then(() => (init_src4(), src_exports3));
50229
+ const { existsSync: nativeExists } = require("fs");
50230
+ const { join: nativeJoin } = require("path");
50231
+ const nativeFileCache = /* @__PURE__ */ new Map();
50232
+ const isNativeAgent = (agentId) => {
50233
+ if (ctx.nativeAgentConfigs.has(agentId)) return true;
50234
+ const cached3 = nativeFileCache.get(agentId);
50235
+ if (cached3 !== void 0) return cached3;
50236
+ let onDisk = false;
50237
+ try {
50238
+ onDisk = nativeExists(nativeJoin(process.cwd(), ".claude", "agents", `${agentId}.md`));
50239
+ } catch {
50240
+ onDisk = false;
50241
+ }
50242
+ nativeFileCache.set(agentId, onDisk);
50243
+ return onDisk;
50244
+ };
50245
+ for (let i = 0; i < formatted.length; i++) {
50246
+ const s = formatted[i];
50247
+ if (s.type !== "consensus") continue;
50248
+ if (s.signal !== "hallucination_caught") continue;
50249
+ const rewritten = maybeRewriteHallucinationToTransportFailure2(
50250
+ process.cwd(),
50251
+ s,
50252
+ isNativeAgent
50253
+ );
50254
+ if (rewritten !== s) {
50255
+ formatted[i] = rewritten;
50256
+ }
50257
+ }
50258
+ } catch (err) {
50259
+ process.stderr.write(`[gossip_signals] transport-failure detector failed: ${err.message}
50260
+ `);
50261
+ }
49572
50262
  const { extractCategories: extractCategories2, logUncategorizedFinding: logUncategorizedFinding2 } = await Promise.resolve().then(() => (init_src4(), src_exports3));
49573
50263
  const droppedNoCategory = [];
49574
50264
  const categoryEnforced = formatted.filter((s, i) => {
@@ -49602,9 +50292,9 @@ Uncategorized finding_ids: ${uncategorized.join(", ")}`;
49602
50292
  const existingFindingIds = /* @__PURE__ */ new Set();
49603
50293
  const existingKeyToFindingId = /* @__PURE__ */ new Map();
49604
50294
  try {
49605
- const { readFileSync: readFileSync59 } = await import("fs");
50295
+ const { readFileSync: readFileSync61 } = await import("fs");
49606
50296
  const perfPath = require("path").join(process.cwd(), ".gossip", "agent-performance.jsonl");
49607
- const lines = readFileSync59(perfPath, "utf-8").split("\n").filter(Boolean);
50297
+ const lines = readFileSync61(perfPath, "utf-8").split("\n").filter(Boolean);
49608
50298
  for (const line of lines) {
49609
50299
  try {
49610
50300
  const rec = JSON.parse(line);
@@ -50361,16 +51051,16 @@ ${preview}` }]
50361
51051
  const { SkillGapTracker: SkillGapTracker2, parseSkillFrontmatter: parseSkillFrontmatter2, normalizeSkillName: normalizeSkillName2 } = await Promise.resolve().then(() => (init_src4(), src_exports3));
50362
51052
  const tracker = new SkillGapTracker2(process.cwd());
50363
51053
  if (skills && skills.length > 0) {
50364
- const { writeFileSync: writeFileSync25, mkdirSync: mkdirSync30, existsSync: existsSync61, readFileSync: readFileSync59 } = require("fs");
50365
- const { join: join70 } = require("path");
50366
- const dir = join70(process.cwd(), ".gossip", "skills");
50367
- mkdirSync30(dir, { recursive: true });
51054
+ const { writeFileSync: writeFileSync26, mkdirSync: mkdirSync33, existsSync: existsSync64, readFileSync: readFileSync61 } = require("fs");
51055
+ const { join: join73 } = require("path");
51056
+ const dir = join73(process.cwd(), ".gossip", "skills");
51057
+ mkdirSync33(dir, { recursive: true });
50368
51058
  const results = [];
50369
51059
  for (const sk of skills) {
50370
51060
  const name = normalizeSkillName2(sk.name);
50371
- const filePath = join70(dir, `${name}.md`);
50372
- if (existsSync61(filePath)) {
50373
- const existing = readFileSync59(filePath, "utf-8");
51061
+ const filePath = join73(dir, `${name}.md`);
51062
+ if (existsSync64(filePath)) {
51063
+ const existing = readFileSync61(filePath, "utf-8");
50374
51064
  const fm = parseSkillFrontmatter2(existing);
50375
51065
  if (fm) {
50376
51066
  if (fm.generated_by === "manual") {
@@ -50387,7 +51077,7 @@ ${preview}` }]
50387
51077
  }
50388
51078
  }
50389
51079
  }
50390
- writeFileSync25(filePath, sk.content);
51080
+ writeFileSync26(filePath, sk.content);
50391
51081
  tracker.recordResolution(name);
50392
51082
  results.push(`Created .gossip/skills/${name}.md`);
50393
51083
  }
@@ -51100,10 +51790,10 @@ server.tool(
51100
51790
  max_events: external_exports.number().int().positive().max(WATCH_MAX_EVENTS).optional().describe(`Cap events returned (default ${WATCH_MAX_EVENTS}).`)
51101
51791
  },
51102
51792
  async ({ cursor, max_events }) => {
51103
- const { readFileSync: readFileSync59, existsSync: existsSync61 } = await import("node:fs");
51104
- const { join: join70 } = await import("node:path");
51105
- const perfPath = join70(process.cwd(), ".gossip", "agent-performance.jsonl");
51106
- const raw = existsSync61(perfPath) ? readFileSync59(perfPath, "utf-8") : "";
51793
+ const { readFileSync: readFileSync61, existsSync: existsSync64 } = await import("node:fs");
51794
+ const { join: join73 } = await import("node:path");
51795
+ const perfPath = join73(process.cwd(), ".gossip", "agent-performance.jsonl");
51796
+ const raw = existsSync64(perfPath) ? readFileSync61(perfPath, "utf-8") : "";
51107
51797
  const result = filterWatchEvents(raw, { cursor, maxEvents: max_events });
51108
51798
  return { content: [{ type: "text", text: JSON.stringify(result) }] };
51109
51799
  }